Merge "Upgrade to AGP 8.9.0-beta01" into androidx-main
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 10afede..9a7562d 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -2,6 +2,7 @@
checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
ktfmt_hook = ${REPO_ROOT}/frameworks/support/development/ktfmt.sh --skip-if-empty --file=${PREUPLOAD_FILES_PREFIXED}
warn_check_api = ${REPO_ROOT}/frameworks/support/development/apilint.py -f ${PREUPLOAD_FILES}
+relnote_required_runtime = ${REPO_ROOT}/frameworks/support/development/requirerelnote.py --module /androidx/compose/runtime/ --commit ${PREUPLOAD_COMMIT_MESSAGE} --file ${PREUPLOAD_FILES}
[Builtin Hooks]
commit_msg_changeid_field = true
diff --git a/appfunctions/appfunctions-common/src/main/java/androidx/appfunctions/AppFunctionSchemaDefinition.kt b/appfunctions/appfunctions-common/src/main/java/androidx/appfunctions/AppFunctionSchemaDefinition.kt
new file mode 100644
index 0000000..3469992
--- /dev/null
+++ b/appfunctions/appfunctions-common/src/main/java/androidx/appfunctions/AppFunctionSchemaDefinition.kt
@@ -0,0 +1,47 @@
+/*
+ * 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
+
+import androidx.annotation.RestrictTo
+import androidx.annotation.RestrictTo.Scope
+
+/**
+ * Annotates an interface defining the schema for an app function, outlining its input, output, and
+ * behavior
+ *
+ * Example Usage:
+ * ```kotlin
+ * @AppFunctionSchemaDefinition(name = "findNotes", version = 1, category = "Notes")
+ * interface FindNotes {
+ * suspend fun findNotes(
+ * appFunctionContext: AppFunctionContext,
+ * findNotesParams: FindNotesParams,
+ * ): List<Note>
+ * }
+ * ```
+ */
+@RestrictTo(Scope.LIBRARY_GROUP)
+@Retention(
+ // Binary because it's used to determine the annotation values from the compiled schema library.
+ AnnotationRetention.BINARY
+)
+@Target(AnnotationTarget.CLASS)
+public annotation class AppFunctionSchemaDefinition(
+ val name: String,
+ val version: Int,
+ val category: String
+)
diff --git a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavDeepLinkSaveStateControl.kt b/appfunctions/appfunctions-common/src/main/java/androidx/appfunctions/internal/Constants.kt
similarity index 60%
copy from navigation/navigation-runtime/src/main/java/androidx/navigation/NavDeepLinkSaveStateControl.kt
copy to appfunctions/appfunctions-common/src/main/java/androidx/appfunctions/internal/Constants.kt
index 469b038..d8cd558 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavDeepLinkSaveStateControl.kt
+++ b/appfunctions/appfunctions-common/src/main/java/androidx/appfunctions/internal/Constants.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 The Android Open Source Project
+ * 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.
@@ -14,10 +14,13 @@
* limitations under the License.
*/
-package androidx.navigation
+package androidx.appfunctions.internal
-/** @see NavController.enableDeepLinkSaveState */
-@Retention(AnnotationRetention.BINARY)
-@Target(AnnotationTarget.FUNCTION)
-@RequiresOptIn(level = RequiresOptIn.Level.WARNING)
-public annotation class NavDeepLinkSaveStateControl
+import androidx.annotation.RestrictTo
+
+/** Reusable constants values for AppFunction. */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public object Constants {
+ /** Android Log tag for AppFunction logs. */
+ public const val APP_FUNCTIONS_TAG: String = "AppFunctions"
+}
diff --git a/appfunctions/appfunctions-compiler/src/main/java/androidx/appfunctions/compiler/AppFunctionCompiler.kt b/appfunctions/appfunctions-compiler/src/main/java/androidx/appfunctions/compiler/AppFunctionCompiler.kt
index 6151d45..8acc8bc 100644
--- a/appfunctions/appfunctions-compiler/src/main/java/androidx/appfunctions/compiler/AppFunctionCompiler.kt
+++ b/appfunctions/appfunctions-compiler/src/main/java/androidx/appfunctions/compiler/AppFunctionCompiler.kt
@@ -20,6 +20,7 @@
import androidx.appfunctions.compiler.core.logException
import androidx.appfunctions.compiler.processors.AppFunctionIdProcessor
import androidx.appfunctions.compiler.processors.AppFunctionInventoryProcessor
+import androidx.appfunctions.compiler.processors.AppFunctionLegacyIndexXmlProcessor
import com.google.devtools.ksp.processing.KSPLogger
import com.google.devtools.ksp.processing.Resolver
import com.google.devtools.ksp.processing.SymbolProcessor
@@ -53,8 +54,11 @@
override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor {
val idProcessor = AppFunctionIdProcessor(environment.codeGenerator)
val inventoryProcessor = AppFunctionInventoryProcessor(environment.codeGenerator)
+ // TODO: Add compiler option to disable legacy xml generator.
+ val legacyIndexXmlProcessor =
+ AppFunctionLegacyIndexXmlProcessor(environment.codeGenerator)
return AppFunctionCompiler(
- listOf(idProcessor, inventoryProcessor),
+ listOf(idProcessor, inventoryProcessor, legacyIndexXmlProcessor),
environment.logger,
)
}
diff --git a/appfunctions/appfunctions-compiler/src/main/java/androidx/appfunctions/compiler/core/AppFunctionSymbolResolver.kt b/appfunctions/appfunctions-compiler/src/main/java/androidx/appfunctions/compiler/core/AppFunctionSymbolResolver.kt
index 2bac5ac..e8ae1c5 100644
--- a/appfunctions/appfunctions-compiler/src/main/java/androidx/appfunctions/compiler/core/AppFunctionSymbolResolver.kt
+++ b/appfunctions/appfunctions-compiler/src/main/java/androidx/appfunctions/compiler/core/AppFunctionSymbolResolver.kt
@@ -20,7 +20,11 @@
import androidx.appfunctions.compiler.core.IntrospectionHelper.AppFunctionAnnotation
import com.google.devtools.ksp.processing.Resolver
import com.google.devtools.ksp.symbol.KSClassDeclaration
+import com.google.devtools.ksp.symbol.KSFile
import com.google.devtools.ksp.symbol.KSFunctionDeclaration
+import com.google.devtools.ksp.symbol.KSTypeReference
+import com.google.devtools.ksp.symbol.KSValueParameter
+import com.squareup.kotlinpoet.LIST
/** The helper class to resolve AppFunction related symbols. */
class AppFunctionSymbolResolver(private val resolver: Resolver) {
@@ -99,7 +103,30 @@
}
private fun validateParameterTypes() {
- // TODO: Validate that the parameter type used by the app functions are supported
+ for (appFunctionDeclaration in appFunctionDeclarations) {
+ for ((paramIndex, ksValueParameter) in
+ appFunctionDeclaration.parameters.withIndex()) {
+ if (paramIndex == 0) {
+ // Skip the first parameter which is always the `AppFunctionContext`.
+ continue
+ }
+
+ if (!ksValueParameter.validateAppFunctionParameterType()) {
+ throw ProcessingException(
+ "App function parameters must be one of the following " +
+ "primitive types or a list of these types:\n${
+ SUPPORTED_RAW_PRIMITIVE_TYPES.joinToString(
+ ",\n"
+ )
+ }, but found ${
+ ksValueParameter.resolveTypeReference().ensureQualifiedTypeName()
+ .asString()
+ }",
+ ksValueParameter
+ )
+ }
+ }
+ }
}
/**
@@ -113,5 +140,57 @@
val methodName = functionDeclaration.simpleName.asString()
return "${packageName}.${className}#${methodName}"
}
+
+ /** Returns the file containing the class declaration and app functions. */
+ fun getSourceFile(): KSFile? = classDeclaration.containingFile
+
+ private fun KSValueParameter.validateAppFunctionParameterType(): Boolean {
+ // Todo(b/391342300): Allow AppFunctionSerializable type too.
+ if (type.isOfType(LIST)) {
+ val typeReferenceArgument = type.resolveListParameterizedType()
+ // List types only support raw primitive types
+ return SUPPORTED_RAW_PRIMITIVE_TYPES.contains(
+ typeReferenceArgument.ensureQualifiedTypeName().asString()
+ )
+ }
+ return SUPPORTED_RAW_PRIMITIVE_TYPES.contains(
+ type.ensureQualifiedTypeName().asString()
+ ) || SUPPORTED_ARRAY_PRIMITIVE_TYPES.contains(type.ensureQualifiedTypeName().asString())
+ }
+
+ /**
+ * Resolves the type reference of a parameter.
+ *
+ * If the parameter type is a list, it will resolve the type reference of the list element.
+ */
+ private fun KSValueParameter.resolveTypeReference(): KSTypeReference {
+ return if (type.isOfType(LIST)) {
+ type.resolveListParameterizedType()
+ } else {
+ type
+ }
+ }
+
+ private companion object {
+ val SUPPORTED_RAW_PRIMITIVE_TYPES: Set<String> =
+ setOf(
+ Int::class.qualifiedName!!,
+ Long::class.qualifiedName!!,
+ Float::class.qualifiedName!!,
+ Double::class.qualifiedName!!,
+ Boolean::class.qualifiedName!!,
+ String::class.qualifiedName!!,
+ )
+
+ val SUPPORTED_ARRAY_PRIMITIVE_TYPES: Set<String> =
+ setOf(
+ IntArray::class.qualifiedName!!,
+ LongArray::class.qualifiedName!!,
+ FloatArray::class.qualifiedName!!,
+ DoubleArray::class.qualifiedName!!,
+ BooleanArray::class.qualifiedName!!,
+ ByteArray::class.qualifiedName!!,
+ )
+ }
}
}
diff --git a/appfunctions/appfunctions-compiler/src/main/java/androidx/appfunctions/compiler/core/IntrospectionHelper.kt b/appfunctions/appfunctions-compiler/src/main/java/androidx/appfunctions/compiler/core/IntrospectionHelper.kt
index fe0d3fa..694a061 100644
--- a/appfunctions/appfunctions-compiler/src/main/java/androidx/appfunctions/compiler/core/IntrospectionHelper.kt
+++ b/appfunctions/appfunctions-compiler/src/main/java/androidx/appfunctions/compiler/core/IntrospectionHelper.kt
@@ -28,6 +28,18 @@
// Annotation classes
object AppFunctionAnnotation {
val CLASS_NAME = ClassName(APP_FUNCTIONS_PACKAGE_NAME, "AppFunction")
+ const val PROPERTY_IS_ENABLED = "isEnabled"
+ }
+
+ object AppFunctionSchemaDefinitionAnnotation {
+ val CLASS_NAME = ClassName(APP_FUNCTIONS_PACKAGE_NAME, "AppFunctionSchemaDefinition")
+ const val PROPERTY_CATEGORY = "category"
+ const val PROPERTY_NAME = "name"
+ const val PROPERTY_VERSION = "version"
+ }
+
+ object AppFunctionSerializableAnnotation {
+ val CLASS_NAME = ClassName(APP_FUNCTIONS_PACKAGE_NAME, "AppFunctionSerializable")
}
// Classes
diff --git a/appfunctions/appfunctions-compiler/src/main/java/androidx/appfunctions/compiler/core/KspUtils.kt b/appfunctions/appfunctions-compiler/src/main/java/androidx/appfunctions/compiler/core/KspUtils.kt
index 1fa4707..65af037 100644
--- a/appfunctions/appfunctions-compiler/src/main/java/androidx/appfunctions/compiler/core/KspUtils.kt
+++ b/appfunctions/appfunctions-compiler/src/main/java/androidx/appfunctions/compiler/core/KspUtils.kt
@@ -16,8 +16,31 @@
package androidx.appfunctions.compiler.core
+import com.google.devtools.ksp.symbol.KSAnnotation
+import com.google.devtools.ksp.symbol.KSName
import com.google.devtools.ksp.symbol.KSTypeReference
import com.squareup.kotlinpoet.ClassName
+import com.squareup.kotlinpoet.LIST
+import kotlin.reflect.KClass
+import kotlin.reflect.cast
+
+/**
+ * Resolves the type reference to the parameterized type if it is a list. Otherwise, it returns the
+ * type reference as is.
+ *
+ * @return the resolved type reference
+ * @throws ProcessingException If unable to resolve the type.
+ */
+fun KSTypeReference.resolveListParameterizedType(): KSTypeReference {
+ if (!isOfType(LIST)) {
+ throw ProcessingException(
+ "Unable to resolve list parameterized type for non list type",
+ this
+ )
+ }
+ return resolve().arguments.firstOrNull()?.type
+ ?: throw ProcessingException("Unable to resolve the parameterized type for the list", this)
+}
/**
* Checks if the type reference is of the given type.
@@ -27,12 +50,45 @@
* @throws ProcessingException If unable to resolve the type.
*/
fun KSTypeReference.isOfType(type: ClassName): Boolean {
- val ksType = this.resolve()
- val typeName =
- ksType.declaration.qualifiedName
- ?: throw ProcessingException(
- "Unable to resolve the type to check if it is of type [${type}]",
- this
- )
+ val typeName = ensureQualifiedTypeName()
return typeName.asString() == type.canonicalName
}
+
+/**
+ * Finds and returns an annotation of [annotationClass] type.
+ *
+ * @param annotationClass the annotation class to find
+ */
+fun Sequence<KSAnnotation>.findAnnotation(annotationClass: ClassName): KSAnnotation? =
+ this.singleOrNull {
+ val shortName = it.shortName.getShortName()
+ if (shortName != annotationClass.simpleName) {
+ false
+ } else {
+ val typeName = it.annotationType.ensureQualifiedTypeName()
+ typeName.asString() == annotationClass.canonicalName
+ }
+ }
+
+/**
+ * Resolves the type reference to its qualified name.
+ *
+ * @return the qualified name of the type reference
+ */
+fun KSTypeReference.ensureQualifiedTypeName(): KSName =
+ resolve().declaration.qualifiedName
+ ?: throw ProcessingException(
+ "Unable to resolve the qualified type name for this reference",
+ this
+ )
+
+/** Returns the value of the annotation property if found. */
+fun <T : Any> KSAnnotation.requirePropertyValueOfType(
+ propertyName: String,
+ expectedType: KClass<T>,
+): T {
+ val propertyValue =
+ this.arguments.singleOrNull { it.name?.asString() == propertyName }?.value
+ ?: throw ProcessingException("Unable to find property with name: $propertyName", this)
+ return expectedType.cast(propertyValue)
+}
diff --git a/appfunctions/appfunctions-compiler/src/main/java/androidx/appfunctions/compiler/processors/AppFunctionLegacyIndexXmlProcessor.kt b/appfunctions/appfunctions-compiler/src/main/java/androidx/appfunctions/compiler/processors/AppFunctionLegacyIndexXmlProcessor.kt
new file mode 100644
index 0000000..d41a4d3e
--- /dev/null
+++ b/appfunctions/appfunctions-compiler/src/main/java/androidx/appfunctions/compiler/processors/AppFunctionLegacyIndexXmlProcessor.kt
@@ -0,0 +1,250 @@
+/*
+ * 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.compiler.processors
+
+import androidx.appfunctions.compiler.core.AppFunctionSymbolResolver
+import androidx.appfunctions.compiler.core.AppFunctionSymbolResolver.AnnotatedAppFunctions
+import androidx.appfunctions.compiler.core.IntrospectionHelper.AppFunctionAnnotation
+import androidx.appfunctions.compiler.core.IntrospectionHelper.AppFunctionSchemaDefinitionAnnotation
+import androidx.appfunctions.compiler.core.ProcessingException
+import androidx.appfunctions.compiler.core.findAnnotation
+import androidx.appfunctions.compiler.core.requirePropertyValueOfType
+import com.google.devtools.ksp.processing.CodeGenerator
+import com.google.devtools.ksp.processing.Dependencies
+import com.google.devtools.ksp.processing.Resolver
+import com.google.devtools.ksp.processing.SymbolProcessor
+import com.google.devtools.ksp.symbol.KSAnnotated
+import com.google.devtools.ksp.symbol.KSClassDeclaration
+import com.google.devtools.ksp.symbol.KSFunctionDeclaration
+import com.squareup.kotlinpoet.ClassName
+import javax.xml.parsers.DocumentBuilderFactory
+import javax.xml.transform.OutputKeys
+import javax.xml.transform.TransformerFactory
+import javax.xml.transform.dom.DOMSource
+import javax.xml.transform.stream.StreamResult
+import org.w3c.dom.Document
+import org.w3c.dom.Element
+
+/**
+ * Generates AppFunction's index xml file for the legacy AppSearch indexer to index.
+ *
+ * The generator would write an XML file as `/assets/app_functions.xml`. The file would be packaged
+ * into the APK's asset when assembled. So that the AppSearch indexer can look up the asset and
+ * inject metadata into platform AppSearch database accordingly.
+ *
+ * The new indexer will index additional properties based on the schema defined in SDK instead of
+ * the pre-defined one in AppSearch.
+ */
+class AppFunctionLegacyIndexXmlProcessor(
+ private val codeGenerator: CodeGenerator,
+) : SymbolProcessor {
+
+ override fun process(resolver: Resolver): List<KSAnnotated> {
+ generateLegacyIndexXml(AppFunctionSymbolResolver(resolver).resolveAnnotatedAppFunctions())
+ return emptyList()
+ }
+
+ /**
+ * Generates AppFunction's legacy index xml files for v1 indexer in App Search.
+ *
+ * @param appFunctionsByClass a collection of functions annotated with @AppFunction grouped by
+ * their enclosing classes.
+ */
+ private fun generateLegacyIndexXml(
+ appFunctionsByClass: List<AnnotatedAppFunctions>,
+ ) {
+ if (appFunctionsByClass.isEmpty()) {
+ return
+ }
+ val xmlDetails = appFunctionsByClass.flatMap(::getAppFunctionXmlDetail)
+ writeXmlFile(xmlDetails, appFunctionsByClass)
+ }
+
+ private fun getAppFunctionXmlDetail(
+ appFunctionsByClass: AnnotatedAppFunctions
+ ): List<AppFunctionXmlDetails> {
+
+ return appFunctionsByClass.appFunctionDeclarations.map {
+ val appFunctionAnnotation =
+ it.annotations.findAnnotation(AppFunctionAnnotation.CLASS_NAME)
+ ?: throw ProcessingException("Function not annotated with @AppFunction.", it)
+ val enabled =
+ appFunctionAnnotation.requirePropertyValueOfType(
+ AppFunctionAnnotation.PROPERTY_IS_ENABLED,
+ Boolean::class,
+ )
+
+ val schemaDetail = getAppFunctionSchemaDetail(it)
+
+ AppFunctionXmlDetails(
+ appFunctionsByClass.getAppFunctionIdentifier(it),
+ enabled,
+ schemaDetail,
+ )
+ }
+ }
+
+ private fun getAppFunctionSchemaDetail(
+ function: KSFunctionDeclaration
+ ): AppFunctionSchemaDetail? {
+ val rootInterfaceWithAppFunctionSchemaDefinition =
+ findRootInterfaceWithAnnotation(
+ function,
+ AppFunctionSchemaDefinitionAnnotation.CLASS_NAME
+ ) ?: return null
+
+ val schemaFunctionAnnotation =
+ rootInterfaceWithAppFunctionSchemaDefinition.annotations.findAnnotation(
+ AppFunctionSchemaDefinitionAnnotation.CLASS_NAME
+ ) ?: return null
+ val schemaCategory =
+ schemaFunctionAnnotation.requirePropertyValueOfType(
+ AppFunctionSchemaDefinitionAnnotation.PROPERTY_CATEGORY,
+ String::class,
+ )
+ val schemaName =
+ schemaFunctionAnnotation.requirePropertyValueOfType(
+ AppFunctionSchemaDefinitionAnnotation.PROPERTY_NAME,
+ String::class,
+ )
+ val schemaVersion =
+ schemaFunctionAnnotation.requirePropertyValueOfType(
+ AppFunctionSchemaDefinitionAnnotation.PROPERTY_VERSION,
+ Int::class,
+ )
+ return AppFunctionSchemaDetail(schemaCategory, schemaName, schemaVersion)
+ }
+
+ private fun findRootInterfaceWithAnnotation(
+ function: KSFunctionDeclaration,
+ annotationName: ClassName
+ ): KSClassDeclaration? {
+ val parentDeclaration = function.parentDeclaration as? KSClassDeclaration ?: return null
+
+ // Check if the enclosing class has the @AppFunctionSchemaDefinition
+ val annotation = parentDeclaration.annotations.findAnnotation(annotationName)
+ if (annotation != null) {
+ return parentDeclaration
+ }
+
+ val superClassFunction = (function.findOverridee() as? KSFunctionDeclaration) ?: return null
+ return findRootInterfaceWithAnnotation(superClassFunction, annotationName)
+ }
+
+ private fun writeXmlFile(
+ xmlDetailsList: List<AppFunctionXmlDetails>,
+ appFunctionsByClass: List<AnnotatedAppFunctions>,
+ ) {
+ val xmlDocumentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
+ val xmlDocument = xmlDocumentBuilder.newDocument().apply { xmlStandalone = true }
+
+ val appFunctionsElement = xmlDocument.createElement(XmlElement.APP_FUNCTIONS_ELEMENTS_TAG)
+ xmlDocument.appendChild(appFunctionsElement)
+
+ for (xmlDetails in xmlDetailsList) {
+ appFunctionsElement.appendChild(xmlDocument.createAppFunctionElement(xmlDetails))
+ }
+
+ val transformer =
+ TransformerFactory.newInstance().newTransformer().apply {
+ setOutputProperty(OutputKeys.INDENT, "yes")
+ setOutputProperty(OutputKeys.ENCODING, "UTF-8")
+ setOutputProperty(OutputKeys.VERSION, "1.0")
+ setOutputProperty(OutputKeys.STANDALONE, "yes")
+ }
+
+ codeGenerator
+ .createNewFile(
+ Dependencies(
+ aggregating = true,
+ *appFunctionsByClass.mapNotNull { it.getSourceFile() }.toTypedArray()
+ ),
+ XML_PACKAGE_NAME,
+ XML_FILE_NAME,
+ XML_EXTENSION
+ )
+ .use { stream -> transformer.transform(DOMSource(xmlDocument), StreamResult(stream)) }
+ }
+
+ private fun Document.createAppFunctionElement(xmlDetails: AppFunctionXmlDetails): Element =
+ createElement(XmlElement.APP_FUNCTION_ITEM_TAG).apply {
+ appendChild(
+ createElementWithTextNode(XmlElement.APP_FUNCTION_ID_TAG, xmlDetails.functionId)
+ )
+
+ val schemaDetail = xmlDetails.schemaDetail
+ if (schemaDetail != null) {
+ appendChild(
+ createElementWithTextNode(
+ XmlElement.APP_FUNCTION_SCHEMA_CATEGORY_TAG,
+ schemaDetail.schemaCategory,
+ )
+ )
+ appendChild(
+ createElementWithTextNode(
+ XmlElement.APP_FUNCTION_SCHEMA_NAME_TAG,
+ schemaDetail.schemaName,
+ )
+ )
+ appendChild(
+ createElementWithTextNode(
+ XmlElement.APP_FUNCTION_SCHEMA_VERSION_TAG,
+ schemaDetail.schemaVersion.toString(),
+ )
+ )
+ }
+ appendChild(
+ createElementWithTextNode(
+ XmlElement.APP_FUNCTION_ENABLE_BY_DEFAULT_TAG,
+ xmlDetails.enabled.toString(),
+ )
+ )
+ }
+
+ private fun Document.createElementWithTextNode(elementName: String, text: String): Element =
+ createElement(elementName).apply { appendChild(createTextNode(text)) }
+
+ /** Details of an app function that are needed to generate its XML file. */
+ private data class AppFunctionXmlDetails(
+ val functionId: String,
+ val enabled: Boolean,
+ val schemaDetail: AppFunctionSchemaDetail?,
+ )
+
+ /** Details of an schema function that are needed to generate its XML file. */
+ private data class AppFunctionSchemaDetail(
+ val schemaCategory: String,
+ val schemaName: String,
+ val schemaVersion: Int,
+ )
+
+ private companion object {
+ private const val XML_PACKAGE_NAME = "assets"
+ private const val XML_FILE_NAME = "app_functions"
+ private const val XML_EXTENSION = "xml"
+
+ private object XmlElement {
+ const val APP_FUNCTIONS_ELEMENTS_TAG = "appfunctions"
+ const val APP_FUNCTION_ITEM_TAG = "appfunction"
+ const val APP_FUNCTION_ID_TAG = "function_id"
+ const val APP_FUNCTION_SCHEMA_CATEGORY_TAG = "schema_category"
+ const val APP_FUNCTION_SCHEMA_NAME_TAG = "schema_name"
+ const val APP_FUNCTION_SCHEMA_VERSION_TAG = "schema_version"
+ const val APP_FUNCTION_ENABLE_BY_DEFAULT_TAG = "enabled_by_default"
+ }
+ }
+}
diff --git a/appfunctions/appfunctions-compiler/src/test/java/androidx/appfunctions/compiler/AppFunctionCompilerTest.kt b/appfunctions/appfunctions-compiler/src/test/java/androidx/appfunctions/compiler/AppFunctionCompilerTest.kt
index 8860052..4204290 100644
--- a/appfunctions/appfunctions-compiler/src/test/java/androidx/appfunctions/compiler/AppFunctionCompilerTest.kt
+++ b/appfunctions/appfunctions-compiler/src/test/java/androidx/appfunctions/compiler/AppFunctionCompilerTest.kt
@@ -46,9 +46,9 @@
fun testSimpleFunction_genAppFunctionIds_success() {
val report = compilationTestHelper.compileAll(sourceFileNames = listOf("SimpleFunction.KT"))
- compilationTestHelper.assertSuccessWithContent(
+ compilationTestHelper.assertSuccessWithSourceContent(
report = report,
- expectGeneratedFileName = "SimpleFunctionIds.kt",
+ expectGeneratedSourceFileName = "SimpleFunctionIds.kt",
goldenFileName = "SimpleFunctionIds.KT"
)
}
@@ -85,10 +85,40 @@
fun testSimpleFunction_genAppFunctionInventoryImpl_success() {
val report = compilationTestHelper.compileAll(sourceFileNames = listOf("SimpleFunction.KT"))
- compilationTestHelper.assertSuccessWithContent(
+ compilationTestHelper.assertSuccessWithSourceContent(
report = report,
- expectGeneratedFileName = "SimpleFunction_AppFunctionInventory_Impl.kt",
+ expectGeneratedSourceFileName = "SimpleFunction_AppFunctionInventory_Impl.kt",
goldenFileName = "$%s".format("SimpleFunction_AppFunctionInventory_Impl.KT")
)
}
+
+ @Test
+ fun testAllPrimitiveInputFunctions_genAppFunctionInventoryImpl_success() {
+ val report =
+ compilationTestHelper.compileAll(
+ sourceFileNames = listOf("AllPrimitiveInputFunctions.KT")
+ )
+
+ compilationTestHelper.assertSuccessWithSourceContent(
+ report = report,
+ expectGeneratedSourceFileName =
+ "AllPrimitiveInputFunctions_AppFunctionInventory_Impl.kt",
+ goldenFileName = "$%s".format("AllPrimitiveInputFunctions_AppFunctionInventory_Impl.KT")
+ )
+ }
+
+ // TODO: Add more tests for legacy index processor.
+ @Test
+ fun testSampleNoParamImp_genLegacyIndexXmlFile_success() {
+ val report =
+ compilationTestHelper.compileAll(
+ sourceFileNames = listOf("FakeNoArgImpl.KT", "FakeSchemas.KT")
+ )
+
+ compilationTestHelper.assertSuccessWithResourceContent(
+ report = report,
+ expectGeneratedResourceFileName = "app_functions.xml",
+ goldenFileName = "fakeNoArgImpl_app_function.xml"
+ )
+ }
}
diff --git a/appfunctions/appfunctions-compiler/src/test/java/androidx/appfunctions/compiler/testings/CompilationTestHelper.kt b/appfunctions/appfunctions-compiler/src/test/java/androidx/appfunctions/compiler/testings/CompilationTestHelper.kt
index 4e6253d..cbb3c4b 100644
--- a/appfunctions/appfunctions-compiler/src/test/java/androidx/appfunctions/compiler/testings/CompilationTestHelper.kt
+++ b/appfunctions/appfunctions-compiler/src/test/java/androidx/appfunctions/compiler/testings/CompilationTestHelper.kt
@@ -17,6 +17,7 @@
package androidx.appfunctions.compiler.testings
import androidx.room.compiler.processing.util.DiagnosticMessage
+import androidx.room.compiler.processing.util.Resource
import androidx.room.compiler.processing.util.Source
import androidx.room.compiler.processing.util.compiler.TestCompilationArguments
import androidx.room.compiler.processing.util.compiler.TestCompilationResult
@@ -99,28 +100,20 @@
}
/**
- * Asserts that the compilation succeeds and contains [expectGeneratedFileName] in generated
- * sources that is identical to the content of [goldenFileName].
+ * Asserts that the compilation succeeds and contains [expectGeneratedSourceFileName] in
+ * generated sources that is identical to the content of [goldenFileName].
*/
- fun assertSuccessWithContent(
+ fun assertSuccessWithSourceContent(
report: CompilationReport,
- expectGeneratedFileName: String,
+ expectGeneratedSourceFileName: String,
goldenFileName: String,
) {
- Truth.assertWithMessage(
- """
- Compile failed with error:
- ${report.printDiagnostics(Diagnostic.Kind.ERROR)}
- """
- .trimIndent()
- )
- .that(report.isSuccess)
- .isTrue()
+ assertCompilationSuccess(report)
val goldenFile = getGoldenFile(goldenFileName)
val generatedSourceFile =
report.generatedSourceFiles.single { sourceFile ->
- sourceFile.source.relativePath.contains(expectGeneratedFileName)
+ sourceFile.source.relativePath.contains(expectGeneratedSourceFileName)
}
Truth.assertWithMessage(
"""
@@ -136,6 +129,48 @@
.isEqualTo(goldenFile.readText())
}
+ /**
+ * Asserts that the compilation succeeds and contains [expectGeneratedResourceFileName] in
+ * generated resources that is identical to the content of [goldenFileName].
+ */
+ fun assertSuccessWithResourceContent(
+ report: CompilationReport,
+ expectGeneratedResourceFileName: String,
+ goldenFileName: String,
+ ) {
+ assertCompilationSuccess(report)
+
+ val goldenFile = getGoldenFile(goldenFileName)
+ val generatedResourceFile =
+ report.generatedResourceFiles.single { resourceFile ->
+ resourceFile.resource.relativePath.contains(expectGeneratedResourceFileName)
+ }
+ Truth.assertWithMessage(
+ """
+ Content of generated file [${generatedResourceFile.resource.relativePath}] does not match
+ the content of golden file [${goldenFile.path}].
+
+ To update the golden file,
+ run `cp ${generatedResourceFile.resourceFilePath} ${goldenFile.absolutePath}`
+ """
+ .trimIndent()
+ )
+ .that(generatedResourceFile.resource.getContents())
+ .isEqualTo(goldenFile.readText())
+ }
+
+ private fun assertCompilationSuccess(report: CompilationReport) {
+ Truth.assertWithMessage(
+ """
+ Compile failed with error:
+ ${report.printDiagnostics(Diagnostic.Kind.ERROR)}
+ """
+ .trimIndent()
+ )
+ .that(report.isSuccess)
+ .isTrue()
+ }
+
fun assertErrorWithMessage(report: CompilationReport, expectedErrorMessage: String) {
Truth.assertWithMessage("Compile succeed").that(report.isSuccess).isFalse()
@@ -196,6 +231,8 @@
val generatedSourceFiles: List<GeneratedSourceFile>,
/** A map of diagnostics results. */
val diagnostics: Map<Diagnostic.Kind, List<DiagnosticMessage>>,
+ /** A list of generated source files. */
+ val generatedResourceFiles: List<GeneratedResourceFile>,
) {
/** Print the diagnostics result of type [kind]. */
fun printDiagnostics(kind: Diagnostic.Kind): String {
@@ -216,7 +253,11 @@
result.generatedSources.map { source ->
GeneratedSourceFile.create(source, outputDir)
},
- diagnostics = result.diagnostics
+ diagnostics = result.diagnostics,
+ generatedResourceFiles =
+ result.generatedResources.map { resource ->
+ GeneratedResourceFile.create(resource, outputDir)
+ }
)
}
}
@@ -236,4 +277,22 @@
}
}
}
+
+ /** A wrapper class contains [Resource] with its file path. */
+ data class GeneratedResourceFile(val resource: Resource, val resourceFilePath: Path) {
+ companion object {
+ internal fun create(resource: Resource, outputDir: Path): GeneratedResourceFile {
+ val filePath =
+ outputDir.resolve(resource.relativePath).apply {
+ parent?.createDirectories()
+ createFile()
+ writeText(resource.getContents())
+ }
+ return GeneratedResourceFile(resource, filePath)
+ }
+ }
+ }
}
+
+private fun Resource.getContents(): String =
+ openInputStream().bufferedReader().use { it.readText() }
diff --git a/appfunctions/appfunctions-compiler/src/test/test-data/input/AllPrimitiveInputFunctions.KT b/appfunctions/appfunctions-compiler/src/test/test-data/input/AllPrimitiveInputFunctions.KT
new file mode 100644
index 0000000..c1fda4a
--- /dev/null
+++ b/appfunctions/appfunctions-compiler/src/test/test-data/input/AllPrimitiveInputFunctions.KT
@@ -0,0 +1,55 @@
+package com.testdata
+
+import androidx.appfunctions.AppFunction
+import androidx.appfunctions.AppFunctionContext
+
+class AllPrimitiveInputFunctions {
+ @AppFunction fun simpleFunctionInt(appFunctionContext: AppFunctionContext, intParam: Int) {}
+
+ @AppFunction fun simpleFunctionLong(appFunctionContext: AppFunctionContext, longParam: Long) {}
+
+ @AppFunction
+ fun simpleFunctionFloat(appFunctionContext: AppFunctionContext, floatParam: Float) {}
+
+ @AppFunction
+ fun simpleFunctionDouble(appFunctionContext: AppFunctionContext, doubleParam: Double) {}
+
+ @AppFunction
+ fun simpleFunctionBoolean(appFunctionContext: AppFunctionContext, booleanParam: Boolean) {}
+
+ @AppFunction
+ fun simpleFunctionString(appFunctionContext: AppFunctionContext, stringParam: String) {}
+
+ @AppFunction
+ fun simpleFunctionIntArray(appFunctionContext: AppFunctionContext, intArrayParam: IntArray) {}
+
+ @AppFunction
+ fun simpleFunctionLongArray(
+ appFunctionContext: AppFunctionContext,
+ longArrayParam: LongArray
+ ) {}
+
+ @AppFunction
+ fun simpleFunctionFloatArray(
+ appFunctionContext: AppFunctionContext,
+ floatArrayParam: FloatArray
+ ) {}
+
+ @AppFunction
+ fun simpleFunctionDoubleArray(
+ appFunctionContext: AppFunctionContext,
+ doubleArrayParam: DoubleArray
+ ) {}
+
+ @AppFunction
+ fun simpleFunctionBooleanArray(
+ appFunctionContext: AppFunctionContext,
+ booleanArrayParam: BooleanArray
+ ) {}
+
+ @AppFunction
+ fun simpleFunctionByteArray(
+ appFunctionContext: AppFunctionContext,
+ byteArrayParam: ByteArray
+ ) {}
+}
diff --git a/appfunctions/appfunctions-compiler/src/test/test-data/input/FakeNoArgImpl.KT b/appfunctions/appfunctions-compiler/src/test/test-data/input/FakeNoArgImpl.KT
new file mode 100644
index 0000000..4b09194
--- /dev/null
+++ b/appfunctions/appfunctions-compiler/src/test/test-data/input/FakeNoArgImpl.KT
@@ -0,0 +1,8 @@
+package com.testdata
+
+import androidx.appfunctions.AppFunction
+import androidx.appfunctions.AppFunctionContext
+
+class FakeNoArgImpl : FakeNoArg {
+ @AppFunction override fun noArg(appFunctionContext: AppFunctionContext) {}
+}
diff --git a/appfunctions/appfunctions-compiler/src/test/test-data/input/FakeSchemas.KT b/appfunctions/appfunctions-compiler/src/test/test-data/input/FakeSchemas.KT
new file mode 100644
index 0000000..a59c737
--- /dev/null
+++ b/appfunctions/appfunctions-compiler/src/test/test-data/input/FakeSchemas.KT
@@ -0,0 +1,11 @@
+package com.testdata
+
+import androidx.appfunctions.AppFunctionContext
+import androidx.appfunctions.AppFunctionSchemaDefinition
+
+private const val FAKE_CATEGORY = "fake_schema_category"
+
+@AppFunctionSchemaDefinition(name = "noArg", version = 1, category = FAKE_CATEGORY)
+interface FakeNoArg {
+ fun noArg(appFunctionContext: AppFunctionContext)
+}
diff --git a/appfunctions/appfunctions-compiler/src/test/test-data/output/$AllPrimitiveInputFunctions_AppFunctionInventory_Impl.KT b/appfunctions/appfunctions-compiler/src/test/test-data/output/$AllPrimitiveInputFunctions_AppFunctionInventory_Impl.KT
new file mode 100644
index 0000000..dd650f0
--- /dev/null
+++ b/appfunctions/appfunctions-compiler/src/test/test-data/output/$AllPrimitiveInputFunctions_AppFunctionInventory_Impl.KT
@@ -0,0 +1,12 @@
+package com.testdata
+
+import androidx.appfunctions.`internal`.AppFunctionInventory
+import androidx.appfunctions.metadata.AppFunctionMetadata
+import javax.`annotation`.processing.Generated
+import kotlin.String
+import kotlin.collections.Map
+
+@Generated("androidx.appfunctions.compiler.AppFunctionCompiler")
+public class `$AllPrimitiveInputFunctions_AppFunctionInventory_Impl` : AppFunctionInventory {
+ override val functionIdToMetadataMap: Map<String, AppFunctionMetadata> = mapOf()
+}
diff --git a/appfunctions/appfunctions-compiler/src/test/test-data/output/fakeNoArgImpl_app_function.xml b/appfunctions/appfunctions-compiler/src/test/test-data/output/fakeNoArgImpl_app_function.xml
new file mode 100644
index 0000000..229bae7
--- /dev/null
+++ b/appfunctions/appfunctions-compiler/src/test/test-data/output/fakeNoArgImpl_app_function.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<appfunctions>
+ <appfunction>
+ <function_id>com.testdata.FakeNoArgImpl#noArg</function_id>
+ <schema_category>fake_schema_category</schema_category>
+ <schema_name>noArg</schema_name>
+ <schema_version>1</schema_version>
+ <enabled_by_default>true</enabled_by_default>
+ </appfunction>
+</appfunctions>
diff --git a/appfunctions/appfunctions-runtime/api/current.txt b/appfunctions/appfunctions-runtime/api/current.txt
index 7394582..0c2d890 100644
--- a/appfunctions/appfunctions-runtime/api/current.txt
+++ b/appfunctions/appfunctions-runtime/api/current.txt
@@ -6,5 +6,25 @@
property public abstract boolean isEnabled;
}
+ public final class AppFunctionConfiguration {
+ method public java.util.Map<java.lang.Class<? extends java.lang.Object?>,androidx.appfunctions.AppFunctionFactory<? extends java.lang.Object?>> getFactories();
+ property public final java.util.Map<java.lang.Class<? extends java.lang.Object?>,androidx.appfunctions.AppFunctionFactory<? extends java.lang.Object?>> factories;
+ }
+
+ public static final class AppFunctionConfiguration.Builder {
+ ctor public AppFunctionConfiguration.Builder();
+ method public <T> androidx.appfunctions.AppFunctionConfiguration.Builder addFactory(Class<T> enclosingClass, androidx.appfunctions.AppFunctionFactory<T> factory);
+ method public androidx.appfunctions.AppFunctionConfiguration build();
+ }
+
+ public static interface AppFunctionConfiguration.Provider {
+ method public androidx.appfunctions.AppFunctionConfiguration getAppFunctionConfiguration();
+ property public abstract androidx.appfunctions.AppFunctionConfiguration appFunctionConfiguration;
+ }
+
+ public interface AppFunctionFactory<T> {
+ method public T createEnclosingClass(Class<T> enclosingClass);
+ }
+
}
diff --git a/appfunctions/appfunctions-runtime/api/restricted_current.txt b/appfunctions/appfunctions-runtime/api/restricted_current.txt
index 7394582..0c2d890 100644
--- a/appfunctions/appfunctions-runtime/api/restricted_current.txt
+++ b/appfunctions/appfunctions-runtime/api/restricted_current.txt
@@ -6,5 +6,25 @@
property public abstract boolean isEnabled;
}
+ public final class AppFunctionConfiguration {
+ method public java.util.Map<java.lang.Class<? extends java.lang.Object?>,androidx.appfunctions.AppFunctionFactory<? extends java.lang.Object?>> getFactories();
+ property public final java.util.Map<java.lang.Class<? extends java.lang.Object?>,androidx.appfunctions.AppFunctionFactory<? extends java.lang.Object?>> factories;
+ }
+
+ public static final class AppFunctionConfiguration.Builder {
+ ctor public AppFunctionConfiguration.Builder();
+ method public <T> androidx.appfunctions.AppFunctionConfiguration.Builder addFactory(Class<T> enclosingClass, androidx.appfunctions.AppFunctionFactory<T> factory);
+ method public androidx.appfunctions.AppFunctionConfiguration build();
+ }
+
+ public static interface AppFunctionConfiguration.Provider {
+ method public androidx.appfunctions.AppFunctionConfiguration getAppFunctionConfiguration();
+ property public abstract androidx.appfunctions.AppFunctionConfiguration appFunctionConfiguration;
+ }
+
+ public interface AppFunctionFactory<T> {
+ method public T createEnclosingClass(Class<T> enclosingClass);
+ }
+
}
diff --git a/appfunctions/appfunctions-runtime/build.gradle b/appfunctions/appfunctions-runtime/build.gradle
index 146190f..63e97bb 100644
--- a/appfunctions/appfunctions-runtime/build.gradle
+++ b/appfunctions/appfunctions-runtime/build.gradle
@@ -39,6 +39,7 @@
// Test dependencies
testImplementation(libs.junit)
testImplementation(libs.truth)
+ testImplementation(libs.mockitoCore4)
androidTestImplementation(libs.testCore)
androidTestImplementation(libs.testRules)
diff --git a/appfunctions/appfunctions-runtime/src/main/java/androidx/appfunctions/AppFunctionConfiguration.kt b/appfunctions/appfunctions-runtime/src/main/java/androidx/appfunctions/AppFunctionConfiguration.kt
new file mode 100644
index 0000000..3a8f5ad
--- /dev/null
+++ b/appfunctions/appfunctions-runtime/src/main/java/androidx/appfunctions/AppFunctionConfiguration.kt
@@ -0,0 +1,65 @@
+/*
+ * 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
+
+/** The configuration object used to customize AppFunction setup. */
+public class AppFunctionConfiguration
+internal constructor(
+ /**
+ * A map of [AppFunctionFactory] used to construct the enclosing classes of AppFunctions.
+ *
+ * The keys in this map are the enclosing classes of the AppFunctions to be constructed, and the
+ * values are the corresponding [AppFunctionFactory] instance. If not provided in the map, the
+ * default no-argument constructors will be used to construct the classes.
+ */
+ public val factories: Map<Class<*>, AppFunctionFactory<*>>
+) {
+ /**
+ * A class to provide customized [AppFunctionConfiguration] object.
+ *
+ * To provide the configuration, implements the [AppFunctionConfiguration.Provider] interface on
+ * your [android.app.Application] class.
+ */
+ public interface Provider {
+ /** The [AppFunctionConfiguration] used to customize AppFunction setup. */
+ public val appFunctionConfiguration: AppFunctionConfiguration
+ }
+
+ /** A builder for [AppFunctionConfiguration]. */
+ public class Builder {
+
+ private val factories = mutableMapOf<Class<*>, AppFunctionFactory<*>>()
+
+ /**
+ * Adds a [factory] instance for creating an [enclosingClass].
+ *
+ * If there is already a factory instance set for [enclosingClass], it will be overridden.
+ */
+ public fun <T : Any> addFactory(
+ enclosingClass: Class<T>,
+ factory: AppFunctionFactory<T>
+ ): Builder {
+ factories[enclosingClass] = factory
+ return this
+ }
+
+ /** Builds the [AppFunctionConfiguration]. */
+ public fun build(): AppFunctionConfiguration {
+ return AppFunctionConfiguration(factories.toMap())
+ }
+ }
+}
diff --git a/appfunctions/appfunctions-runtime/src/main/java/androidx/appfunctions/AppFunctionFactory.kt b/appfunctions/appfunctions-runtime/src/main/java/androidx/appfunctions/AppFunctionFactory.kt
new file mode 100644
index 0000000..5e256da
--- /dev/null
+++ b/appfunctions/appfunctions-runtime/src/main/java/androidx/appfunctions/AppFunctionFactory.kt
@@ -0,0 +1,35 @@
+/*
+ * 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
+
+/**
+ * A factory for instantiate the enclosing class of AppFunctions. The factory is invoked every time
+ * a function call to one of the AppFunction within the class is made.
+ *
+ * Implement a custom [AppFunctionFactory] if your AppFunction's enclosing class require constructor
+ * parameter or custom instantiation logic beyond the default no-argument constructor. This allows
+ * you to inject dependencies or handle more complex object creation scenarios.
+ *
+ * @param T The specific type of AppFunction class this factory creates.
+ */
+public interface AppFunctionFactory<T : Any> {
+ /**
+ * Overrides this method to provide your custom creation logic for enclosing class of
+ * AppFunctions.
+ */
+ public fun createEnclosingClass(enclosingClass: Class<T>): T
+}
diff --git a/appfunctions/appfunctions-runtime/src/main/java/androidx/appfunctions/internal/ConfigurableAppFunctionFactory.kt b/appfunctions/appfunctions-runtime/src/main/java/androidx/appfunctions/internal/ConfigurableAppFunctionFactory.kt
new file mode 100644
index 0000000..ddc760b
--- /dev/null
+++ b/appfunctions/appfunctions-runtime/src/main/java/androidx/appfunctions/internal/ConfigurableAppFunctionFactory.kt
@@ -0,0 +1,93 @@
+/*
+ * 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 android.content.Context
+import android.util.Log
+import androidx.annotation.RestrictTo
+import androidx.appfunctions.AppFunctionConfiguration
+import androidx.appfunctions.AppFunctionFactory
+import androidx.appfunctions.internal.Constants.APP_FUNCTIONS_TAG
+import java.lang.reflect.InvocationTargetException
+
+/**
+ * An [AppFunctionFactory] implementation that will incorporate [AppFunctionConfiguration] from
+ * [context] to create AppFunction enclosing classes.
+ *
+ * If the application context from [context] overrides [AppFunctionConfiguration.Provider], the
+ * customize [AppFunctionFactory] will be used to instantiate the enclosing class. Otherwise, it
+ * will use reflection to create the instance assuming the enclosing class has no argument
+ * constructor.
+ *
+ * [createEnclosingClass] will throw [AppFunctionInstantiationException] if unable to instantiate
+ * the enclosing class.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class ConfigurableAppFunctionFactory<T : Any>(
+ private val context: Context,
+) : AppFunctionFactory<T> {
+
+ override fun createEnclosingClass(enclosingClass: Class<T>): T {
+ val configurationProvider = context.applicationContext as? AppFunctionConfiguration.Provider
+ val customFactory =
+ configurationProvider?.appFunctionConfiguration?.factories?.get(enclosingClass)
+ if (customFactory == null) {
+ Log.d(APP_FUNCTIONS_TAG, "Unable to find custom factory for [$enclosingClass]")
+ return getNoArgumentAppFunctionFactory<T>().createEnclosingClass(enclosingClass)
+ }
+
+ @Suppress("UNCHECKED_CAST")
+ return (customFactory as AppFunctionFactory<T>).createEnclosingClass(enclosingClass)
+ }
+
+ /** Thrown when unable to instantiate the AppFunction enclosing class. */
+ public class AppFunctionInstantiationException(errorMessage: String) :
+ RuntimeException(errorMessage)
+
+ private fun <T : Any> getNoArgumentAppFunctionFactory(): AppFunctionFactory<T> {
+ return object : AppFunctionFactory<T> {
+ override fun createEnclosingClass(enclosingClass: Class<T>): T {
+ return try {
+ enclosingClass.getDeclaredConstructor().newInstance()
+ } catch (_: IllegalAccessException) {
+ throw AppFunctionInstantiationException(
+ "Cannot access the constructor of $enclosingClass"
+ )
+ } catch (_: NoSuchMethodException) {
+ throw AppFunctionInstantiationException(
+ "$enclosingClass requires additional parameter to create. " +
+ "Please either remove the additional parameters or implement the " +
+ "${AppFunctionFactory::class.qualifiedName} and provide it in " +
+ "${AppFunctionConfiguration::class.qualifiedName}",
+ )
+ } catch (_: InstantiationException) {
+ throw AppFunctionInstantiationException(
+ "$enclosingClass should have a public no-argument constructor"
+ )
+ } catch (_: InvocationTargetException) {
+ throw AppFunctionInstantiationException(
+ "Something went wrong when creating $enclosingClass"
+ )
+ } catch (_: ExceptionInInitializerError) {
+ throw AppFunctionInstantiationException(
+ "Something went wrong when creating $enclosingClass"
+ )
+ }
+ }
+ }
+ }
+}
diff --git a/appfunctions/appfunctions-runtime/src/test/java/androidx/appfunctions/AppFunctionConfigurationTest.kt b/appfunctions/appfunctions-runtime/src/test/java/androidx/appfunctions/AppFunctionConfigurationTest.kt
new file mode 100644
index 0000000..c192af9
--- /dev/null
+++ b/appfunctions/appfunctions-runtime/src/test/java/androidx/appfunctions/AppFunctionConfigurationTest.kt
@@ -0,0 +1,79 @@
+/*
+ * 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
+
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+class AppFunctionConfigurationTest {
+ @Test
+ fun testEmpty() {
+ val configuration = AppFunctionConfiguration.Builder().build()
+
+ assertThat(configuration.factories).isEmpty()
+ }
+
+ @Test
+ fun testUniqueFactories() {
+ val factory1 = TestAppFunctionClass1.Factory()
+ val factory2 = TestAppFunctionClass2.Factory()
+
+ val configuration =
+ AppFunctionConfiguration.Builder()
+ .addFactory(TestAppFunctionClass1::class.java, factory1)
+ .addFactory(TestAppFunctionClass2::class.java, factory2)
+ .build()
+
+ assertThat(configuration.factories).hasSize(2)
+ assertThat(configuration.factories[TestAppFunctionClass1::class.java]).isEqualTo(factory1)
+ assertThat(configuration.factories[TestAppFunctionClass2::class.java]).isEqualTo(factory2)
+ }
+
+ @Test
+ fun testDuplicatedFactories() {
+ val factory1 = TestAppFunctionClass1.Factory()
+
+ val configuration =
+ AppFunctionConfiguration.Builder()
+ .addFactory(TestAppFunctionClass1::class.java, factory1)
+ .addFactory(TestAppFunctionClass1::class.java, factory1)
+ .build()
+
+ assertThat(configuration.factories).hasSize(1)
+ assertThat(configuration.factories[TestAppFunctionClass1::class.java]).isEqualTo(factory1)
+ }
+
+ internal class TestAppFunctionClass1 {
+ internal class Factory : AppFunctionFactory<TestAppFunctionClass1> {
+ override fun createEnclosingClass(
+ enclosingClass: Class<TestAppFunctionClass1>
+ ): TestAppFunctionClass1 {
+ return TestAppFunctionClass1()
+ }
+ }
+ }
+
+ internal class TestAppFunctionClass2 {
+ internal class Factory : AppFunctionFactory<TestAppFunctionClass2> {
+ override fun createEnclosingClass(
+ enclosingClass: Class<TestAppFunctionClass2>
+ ): TestAppFunctionClass2 {
+ return TestAppFunctionClass2()
+ }
+ }
+ }
+}
diff --git a/appfunctions/appfunctions-runtime/src/test/java/androidx/appfunctions/internal/ConfigurableAppFunctionFactoryTest.kt b/appfunctions/appfunctions-runtime/src/test/java/androidx/appfunctions/internal/ConfigurableAppFunctionFactoryTest.kt
new file mode 100644
index 0000000..3983d25
--- /dev/null
+++ b/appfunctions/appfunctions-runtime/src/test/java/androidx/appfunctions/internal/ConfigurableAppFunctionFactoryTest.kt
@@ -0,0 +1,157 @@
+/*
+ * 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 android.content.Context
+import android.content.ContextWrapper
+import androidx.appfunctions.AppFunctionConfiguration
+import androidx.appfunctions.AppFunctionFactory
+import androidx.appfunctions.internal.ConfigurableAppFunctionFactory.AppFunctionInstantiationException
+import org.junit.Assert.assertThrows
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mockito.mock
+
+class ConfigurableAppFunctionFactoryTest {
+
+ private lateinit var noConfigContext: Context
+
+ private lateinit var configContext: Context
+
+ @Before
+ fun setup() {
+ noConfigContext = FakeNoConfigContext(mock(Context::class.java))
+ configContext = FakeConfigProviderContext(mock(Context::class.java))
+ }
+
+ @Test
+ fun testCreateNoArgEnclosingClass_withoutConfig() {
+ val factory = ConfigurableAppFunctionFactory<NoArgEnclosingClass>(noConfigContext)
+ factory.createEnclosingClass(NoArgEnclosingClass::class.java)
+ }
+
+ @Test
+ fun testCreateNoArgEnclosingClass_withConfig() {
+ val creator = ConfigurableAppFunctionFactory<NoArgEnclosingClass>(configContext)
+ creator.createEnclosingClass(NoArgEnclosingClass::class.java)
+ }
+
+ @Test
+ fun testCreateNoArgEnclosingClass_ifProvideFactory() {
+ val creator = ConfigurableAppFunctionFactory<NoArgWithFactoryEnclosingClass>(configContext)
+ creator.createEnclosingClass(NoArgWithFactoryEnclosingClass::class.java)
+ }
+
+ @Test
+ fun testCreateEnclosingClassRequiredArgs_withoutFactory() {
+ val creator = ConfigurableAppFunctionFactory<RequiredArgsEnclosingClass>(noConfigContext)
+ assertThrows(AppFunctionInstantiationException::class.java) {
+ creator.createEnclosingClass(RequiredArgsEnclosingClass::class.java)
+ }
+ }
+
+ @Test
+ fun testCreateEnclosingClassRequiredArgs_withFactory() {
+ val creator = ConfigurableAppFunctionFactory<RequiredArgsEnclosingClass>(configContext)
+ creator.createEnclosingClass(RequiredArgsEnclosingClass::class.java)
+ }
+
+ @Test
+ fun testCreateEnclosingClassWithPrivateConstructor() {
+ val creator =
+ ConfigurableAppFunctionFactory<PrivateConstructorEnclosingClass>(noConfigContext)
+ assertThrows(AppFunctionInstantiationException::class.java) {
+ creator.createEnclosingClass(PrivateConstructorEnclosingClass::class.java)
+ }
+ }
+
+ @Test
+ fun testCreateEnclosingClass_thatThrowErrorDuringInvocation() {
+ val creator = ConfigurableAppFunctionFactory<InvocationErrorEnclosingClass>(noConfigContext)
+ assertThrows(AppFunctionInstantiationException::class.java) {
+ creator.createEnclosingClass(InvocationErrorEnclosingClass::class.java)
+ }
+ }
+
+ @Test
+ fun testCreateEnclosingClass_thatThrowErrorDuringInitialization() {
+ val creator = ConfigurableAppFunctionFactory<InitializeErrorEnclosingClass>(noConfigContext)
+ assertThrows(AppFunctionInstantiationException::class.java) {
+ creator.createEnclosingClass(InitializeErrorEnclosingClass::class.java)
+ }
+ }
+
+ // Fake context
+ class FakeConfigProviderContext(baseContext: Context) :
+ ContextWrapper(baseContext), AppFunctionConfiguration.Provider {
+ override fun getApplicationContext(): Context? = this
+
+ override val appFunctionConfiguration: AppFunctionConfiguration
+ get() =
+ AppFunctionConfiguration.Builder()
+ .addFactory(
+ NoArgWithFactoryEnclosingClass::class.java,
+ NoArgWithFactoryEnclosingClass.Factory()
+ )
+ .addFactory(
+ RequiredArgsEnclosingClass::class.java,
+ RequiredArgsEnclosingClass.Factory()
+ )
+ .build()
+ }
+
+ class FakeNoConfigContext(baseContext: Context) : ContextWrapper(baseContext) {
+ override fun getApplicationContext(): Context? = this
+ }
+
+ // Test enclosing classes
+ class NoArgEnclosingClass()
+
+ class NoArgWithFactoryEnclosingClass() {
+ class Factory : AppFunctionFactory<NoArgWithFactoryEnclosingClass> {
+ override fun createEnclosingClass(
+ enclosingClass: Class<NoArgWithFactoryEnclosingClass>
+ ): NoArgWithFactoryEnclosingClass {
+ return NoArgWithFactoryEnclosingClass()
+ }
+ }
+ }
+
+ class RequiredArgsEnclosingClass(val x: Int) {
+ class Factory : AppFunctionFactory<RequiredArgsEnclosingClass> {
+ override fun createEnclosingClass(
+ enclosingClass: Class<RequiredArgsEnclosingClass>
+ ): RequiredArgsEnclosingClass {
+ return RequiredArgsEnclosingClass(0)
+ }
+ }
+ }
+
+ class PrivateConstructorEnclosingClass private constructor()
+
+ class InvocationErrorEnclosingClass private constructor(val x: Int) {
+ constructor() : this(0) {
+ throw RuntimeException()
+ }
+ }
+
+ class InitializeErrorEnclosingClass() {
+ init {
+ throw RuntimeException()
+ }
+ }
+}
diff --git a/benchmark/benchmark-common/api/current.txt b/benchmark/benchmark-common/api/current.txt
index 44c9777..6dea054 100644
--- a/benchmark/benchmark-common/api/current.txt
+++ b/benchmark/benchmark-common/api/current.txt
@@ -54,6 +54,11 @@
@SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public final class MicrobenchmarkConfig {
ctor public MicrobenchmarkConfig();
+ ctor public MicrobenchmarkConfig(optional java.util.List<? extends androidx.benchmark.MetricCapture> metrics);
+ ctor public MicrobenchmarkConfig(optional java.util.List<? extends androidx.benchmark.MetricCapture> metrics, optional boolean traceAppTagEnabled);
+ ctor public MicrobenchmarkConfig(optional java.util.List<? extends androidx.benchmark.MetricCapture> metrics, optional boolean traceAppTagEnabled, optional boolean perfettoSdkTracingEnabled);
+ ctor public MicrobenchmarkConfig(optional java.util.List<? extends androidx.benchmark.MetricCapture> metrics, optional boolean traceAppTagEnabled, optional boolean perfettoSdkTracingEnabled, optional androidx.benchmark.ProfilerConfig? profiler);
+ ctor public MicrobenchmarkConfig(optional java.util.List<? extends androidx.benchmark.MetricCapture> metrics, optional boolean traceAppTagEnabled, optional boolean perfettoSdkTracingEnabled, optional androidx.benchmark.ProfilerConfig? profiler, optional Integer? warmupCount);
ctor public MicrobenchmarkConfig(optional java.util.List<? extends androidx.benchmark.MetricCapture> metrics, optional boolean traceAppTagEnabled, optional boolean perfettoSdkTracingEnabled, optional androidx.benchmark.ProfilerConfig? profiler, optional Integer? warmupCount, optional Integer? measurementCount);
method public Integer? getMeasurementCount();
method public java.util.List<androidx.benchmark.MetricCapture> getMetrics();
diff --git a/benchmark/benchmark-common/api/restricted_current.txt b/benchmark/benchmark-common/api/restricted_current.txt
index f25ea8e..c94bb5b 100644
--- a/benchmark/benchmark-common/api/restricted_current.txt
+++ b/benchmark/benchmark-common/api/restricted_current.txt
@@ -57,6 +57,11 @@
@SuppressCompatibility @androidx.benchmark.ExperimentalBenchmarkConfigApi public final class MicrobenchmarkConfig {
ctor public MicrobenchmarkConfig();
+ ctor public MicrobenchmarkConfig(optional java.util.List<? extends androidx.benchmark.MetricCapture> metrics);
+ ctor public MicrobenchmarkConfig(optional java.util.List<? extends androidx.benchmark.MetricCapture> metrics, optional boolean traceAppTagEnabled);
+ ctor public MicrobenchmarkConfig(optional java.util.List<? extends androidx.benchmark.MetricCapture> metrics, optional boolean traceAppTagEnabled, optional boolean perfettoSdkTracingEnabled);
+ ctor public MicrobenchmarkConfig(optional java.util.List<? extends androidx.benchmark.MetricCapture> metrics, optional boolean traceAppTagEnabled, optional boolean perfettoSdkTracingEnabled, optional androidx.benchmark.ProfilerConfig? profiler);
+ ctor public MicrobenchmarkConfig(optional java.util.List<? extends androidx.benchmark.MetricCapture> metrics, optional boolean traceAppTagEnabled, optional boolean perfettoSdkTracingEnabled, optional androidx.benchmark.ProfilerConfig? profiler, optional Integer? warmupCount);
ctor public MicrobenchmarkConfig(optional java.util.List<? extends androidx.benchmark.MetricCapture> metrics, optional boolean traceAppTagEnabled, optional boolean perfettoSdkTracingEnabled, optional androidx.benchmark.ProfilerConfig? profiler, optional Integer? warmupCount, optional Integer? measurementCount);
method public Integer? getMeasurementCount();
method public java.util.List<androidx.benchmark.MetricCapture> getMetrics();
diff --git a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/DeviceInfoTest.kt b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/DeviceInfoTest.kt
index 456635c..61d9775 100644
--- a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/DeviceInfoTest.kt
+++ b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/DeviceInfoTest.kt
@@ -62,6 +62,26 @@
}
@Test
+ fun willMethodTracingAffectMeasurements() {
+ // first clause - 26 through 30 (inclusive) affected
+ assertFalse(DeviceInfo.willMethodTracingAffectMeasurements(25, -1))
+ assertTrue(DeviceInfo.willMethodTracingAffectMeasurements(26, -1L))
+ assertTrue(DeviceInfo.willMethodTracingAffectMeasurements(30, -1L))
+ assertFalse(DeviceInfo.willMethodTracingAffectMeasurements(31, 310000000L))
+
+ // second clause - art API 34 regression
+ assertFalse(DeviceInfo.willMethodTracingAffectMeasurements(33, 330000000L))
+ assertTrue(DeviceInfo.willMethodTracingAffectMeasurements(33, 340000000L))
+ assertTrue(DeviceInfo.willMethodTracingAffectMeasurements(33, 341513000L - 1))
+ assertFalse(DeviceInfo.willMethodTracingAffectMeasurements(33, 341513000L))
+
+ // third clause - art API 34 regression and internal build ID
+ assertFalse(DeviceInfo.willMethodTracingAffectMeasurements(33, 990090000L))
+ assertTrue(DeviceInfo.willMethodTracingAffectMeasurements(34, 990090000L))
+ assertFalse(DeviceInfo.willMethodTracingAffectMeasurements(35, 990090000L))
+ }
+
+ @Test
fun artMainlineVersion() =
validateArtMainlineVersion(artMainlineVersion = DeviceInfo.artMainlineVersion)
}
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/DeviceInfo.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/DeviceInfo.kt
index 3083747..002ab09 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/DeviceInfo.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/DeviceInfo.kt
@@ -302,6 +302,16 @@
private const val ART_MAINLINE_MIN_VERSION_VERIFY_CLEARS_RUNTIME_IMAGE = 350800000L
/**
+ * ART mainline 990090000 means the module is built from source in the system image and isn't
+ * updatable, and thus expectations should be conservative - assume that any potential bug on
+ * the current SDK version may be present on this device.
+ *
+ * Ideally, we'd have a minimum release build ID, but these may not be consistently and easily
+ * sortable.
+ */
+ private const val ART_MAINLINE_INTERNAL_BUILD_MIN = 990000000
+
+ /**
* Used when mainline version failed to detect, but this is accepted due to low API level (<34)
* where presence isn't guaranteed (e.g. go devices)
*/
@@ -320,15 +330,18 @@
else -> ART_MAINLINE_VERSION_UNDETECTED
}
- val methodTracingAffectsMeasurements =
- Build.VERSION.SDK_INT in 26..30 || // b/313868903
- artMainlineVersion in ART_MAINLINE_VERSIONS_AFFECTING_METHOD_TRACING // b/303660864
+ fun willMethodTracingAffectMeasurements(sdkInt: Int, artVersion: Long): Boolean =
+ sdkInt in 26..30 || // b/313868903
+ artVersion in ART_MAINLINE_VERSIONS_AFFECTING_METHOD_TRACING || // b/303660864
+ (sdkInt == 34 && artVersion >= ART_MAINLINE_INTERNAL_BUILD_MIN) // b/303686344#comment31
- fun isClassLoadTracingAvailable(targetApiLevel: Int, targetArtMainlineVersion: Long?): Boolean =
- targetApiLevel >= 35 ||
- (targetApiLevel >= 31 &&
- (targetArtMainlineVersion == null ||
- targetArtMainlineVersion >= ART_MAINLINE_MIN_VERSION_CLASS_LOAD_TRACING))
+ val methodTracingAffectsMeasurements =
+ willMethodTracingAffectMeasurements(Build.VERSION.SDK_INT, artMainlineVersion)
+
+ fun isClassLoadTracingAvailable(sdkInt: Int, artVersion: Long?): Boolean =
+ sdkInt >= 35 ||
+ (sdkInt >= 31 &&
+ (artVersion == null || artVersion >= ART_MAINLINE_MIN_VERSION_CLASS_LOAD_TRACING))
val supportsClassLoadTracing =
isClassLoadTracingAvailable(Build.VERSION.SDK_INT, artMainlineVersion)
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/MicrobenchmarkConfig.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/MicrobenchmarkConfig.kt
index 102de5e..d2dd087 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/MicrobenchmarkConfig.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/MicrobenchmarkConfig.kt
@@ -23,6 +23,7 @@
*/
@ExperimentalBenchmarkConfigApi
class MicrobenchmarkConfig
+@JvmOverloads
constructor(
/**
* Timing metrics for primary phase, post-warmup
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Metric.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Metric.kt
index 4810691..aaa9668 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Metric.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Metric.kt
@@ -23,9 +23,7 @@
import androidx.benchmark.Shell
import androidx.benchmark.macro.BatteryCharge.hasMinimumCharge
import androidx.benchmark.macro.PowerMetric.Companion.deviceSupportsHighPrecisionTracking
-import androidx.benchmark.macro.PowerMetric.Type
import androidx.benchmark.macro.PowerRail.hasMetrics
-import androidx.benchmark.macro.TraceSectionMetric.Mode
import androidx.benchmark.macro.perfetto.BatteryDischargeQuery
import androidx.benchmark.macro.perfetto.FrameTimingQuery
import androidx.benchmark.macro.perfetto.FrameTimingQuery.SubMetric
@@ -730,8 +728,8 @@
.asMeasurements("artVerifyClass") +
if (
DeviceInfo.isClassLoadTracingAvailable(
- targetApiLevel = captureInfo.apiLevel,
- targetArtMainlineVersion = captureInfo.artMainlineVersion
+ sdkInt = captureInfo.apiLevel,
+ artVersion = captureInfo.artMainlineVersion
)
) {
traceSession
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/metalava/GenerateApiTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/metalava/GenerateApiTask.kt
index 885e0de..3ed68f0 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/metalava/GenerateApiTask.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/metalava/GenerateApiTask.kt
@@ -18,7 +18,6 @@
import androidx.build.Version
import androidx.build.checkapi.ApiLocation
-import androidx.build.checkapi.StandardCompilationInputs
import java.io.File
import javax.inject.Inject
import org.gradle.api.file.Directory
@@ -79,13 +78,6 @@
check(compiled.exists()) { "File " + compiled + " does not exist" }
}
- val inputs =
- StandardCompilationInputs(
- sourcePaths = sourcePaths,
- dependencyClasspath = dependencyClasspath,
- bootClasspath = bootClasspath
- )
-
val levelsArgs =
getGenerateApiLevelsArgs(
getPastApiFiles(),
@@ -96,7 +88,7 @@
generateApi(
metalavaClasspath,
createProjectXmlFile(),
- inputs,
+ sourcePaths.files,
apiLocation.get(),
ApiLintMode.CheckBaseline(baselines.get().apiLintFile, targetsJavaConsumers.get()),
generateRestrictToLibraryGroupAPIs,
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/metalava/MetalavaRunner.kt b/buildSrc/private/src/main/kotlin/androidx/build/metalava/MetalavaRunner.kt
index 580c652..f445c79 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/metalava/MetalavaRunner.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/metalava/MetalavaRunner.kt
@@ -18,7 +18,6 @@
import androidx.build.Version
import androidx.build.checkapi.ApiLocation
-import androidx.build.checkapi.CompilationInputs
import androidx.build.getLibraryByName
import androidx.build.logging.TERMINAL_RED
import androidx.build.logging.TERMINAL_RESET
@@ -187,6 +186,12 @@
)
.joinToString()
)
+ // Acronyms that can be used in their all-caps form. "SQ" is included to allow "SQLite".
+ val allowedAcronyms = listOf("SQL", "SQ", "URL", "EGL", "GL", "KHR")
+ for (acronym in allowedAcronyms) {
+ args.add("--api-lint-allowed-acronym")
+ args.add(acronym)
+ }
val javaOnlyIssues = listOf("MissingJvmstatic", "ArrayReturn", "ValueClassDefinition")
val javaOnlyErrorLevel =
if (targetsJavaConsumers) {
@@ -243,8 +248,8 @@
*/
internal fun generateApi(
metalavaClasspath: FileCollection,
- projectXml: File?,
- files: CompilationInputs,
+ projectXml: File,
+ sourcePaths: Collection<File>,
apiLocation: ApiLocation,
apiLintMode: ApiLintMode,
includeRestrictToLibraryGroupApis: Boolean,
@@ -268,7 +273,7 @@
generateApi(
metalavaClasspath,
projectXml,
- files,
+ sourcePaths,
apiLocation,
generateApiMode,
apiLintMode,
@@ -287,8 +292,8 @@
*/
private fun generateApi(
metalavaClasspath: FileCollection,
- projectXml: File?,
- files: CompilationInputs,
+ projectXml: File,
+ sourcePaths: Collection<File>,
outputLocation: ApiLocation,
generateApiMode: GenerateApiMode,
apiLintMode: ApiLintMode,
@@ -300,10 +305,8 @@
) {
val args =
getGenerateApiArgs(
- files.bootClasspath,
- files.dependencyClasspath,
projectXml,
- files.sourcePaths.files,
+ sourcePaths,
outputLocation,
generateApiMode,
apiLintMode,
@@ -318,9 +321,7 @@
* [GenerateApiMode.PublicApi].
*/
fun getGenerateApiArgs(
- bootClasspath: FileCollection,
- dependencyClasspath: FileCollection,
- projectXml: File?,
+ projectXml: File,
sourcePaths: Collection<File>,
outputLocation: ApiLocation?,
generateApiMode: GenerateApiMode,
@@ -333,19 +334,10 @@
mutableListOf(
"--source-path",
sourcePaths.filter { it.exists() }.joinToString(File.pathSeparator),
+ "--project",
+ projectXml.path
)
- // If there's a project xml file, the classpath isn't needed
- args +=
- if (projectXml != null) {
- listOf("--project", projectXml.path)
- } else {
- listOf(
- "--classpath",
- (bootClasspath.files + dependencyClasspath.files).joinToString(File.pathSeparator)
- )
- }
-
args += listOf("--format=v4", "--warnings-as-errors")
pathToManifest?.let { args += listOf("--manifest", pathToManifest) }
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/metalava/MetalavaTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/metalava/MetalavaTask.kt
index cda75c7..a84b92e 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/metalava/MetalavaTask.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/metalava/MetalavaTask.kt
@@ -110,21 +110,22 @@
@get:Input abstract val targetsJavaConsumers: Property<Boolean>
/**
- * Information about all source sets for multiplatform projects. Non-multiplatform projects can
- * be represented as a list with one source set.
+ * Information about all source sets for multiplatform projects. Non-multiplatform projects
+ * should be represented as a list with one source set.
*
* This is marked as [Internal] because [compiledSources] is what should determine whether to
* rerun metalava.
*/
- @get:Internal abstract val optionalSourceSets: ListProperty<SourceSetInputs>
+ @get:Internal abstract val sourceSets: ListProperty<SourceSetInputs>
/**
- * Creates an XML file representing the project structure, if [optionalSourceSets] was set.
+ * Creates an XML file representing the project structure.
*
* This should only be called during task execution.
*/
- protected fun createProjectXmlFile(): File? {
- val sourceSets = optionalSourceSets.get().ifEmpty { null } ?: return null
+ protected fun createProjectXmlFile(): File {
+ val sourceSets = sourceSets.get()
+ check(sourceSets.isNotEmpty()) { "Project must have at least one source set." }
val outputFile = File(temporaryDir, "project.xml")
ProjectXml.create(
sourceSets,
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/metalava/MetalavaTasks.kt b/buildSrc/private/src/main/kotlin/androidx/build/metalava/MetalavaTasks.kt
index e5e949a..f0cea08 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/metalava/MetalavaTasks.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/metalava/MetalavaTasks.kt
@@ -24,6 +24,7 @@
import androidx.build.checkapi.ApiLocation
import androidx.build.checkapi.CompilationInputs
import androidx.build.checkapi.MultiplatformCompilationInputs
+import androidx.build.checkapi.SourceSetInputs
import androidx.build.checkapi.getRequiredCompatibilityApiLocation
import androidx.build.uptodatedness.cacheEvenIfNoOutputs
import androidx.build.version
@@ -218,7 +219,19 @@
task.bootClasspath = inputs.bootClasspath
androidManifest?.let { task.manifestPath.set(it) }
if (inputs is MultiplatformCompilationInputs) {
- task.optionalSourceSets.set(inputs.sourceSets)
+ task.sourceSets.set(inputs.sourceSets)
+ } else {
+ // Represent a non-multiplatform project as one source set.
+ task.sourceSets.set(
+ listOf(
+ SourceSetInputs(
+ sourceSetName = "main",
+ dependsOnSourceSets = emptyList(),
+ sourcePaths = inputs.sourcePaths,
+ dependencyClasspath = inputs.dependencyClasspath
+ )
+ )
+ )
}
}
}
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/metalava/RegenerateOldApisTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/metalava/RegenerateOldApisTask.kt
index 351b606..617dc43 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/metalava/RegenerateOldApisTask.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/metalava/RegenerateOldApisTask.kt
@@ -18,8 +18,7 @@
import androidx.build.Version
import androidx.build.checkapi.ApiLocation
-import androidx.build.checkapi.CompilationInputs
-import androidx.build.checkapi.StandardCompilationInputs
+import androidx.build.checkapi.SourceSetInputs
import androidx.build.checkapi.getApiFileVersion
import androidx.build.checkapi.getRequiredCompatibilityApiLocation
import androidx.build.checkapi.getVersionedApiLocation
@@ -160,20 +159,27 @@
outputApiLocation: ApiLocation,
) {
val mavenId = "$groupId:$artifactId:$version"
- val inputs: CompilationInputs?
- try {
- inputs = getFiles(runnerProject, mavenId)
- } catch (e: TypedResolveException) {
- runnerProject.logger.info("Ignoring missing artifact $mavenId: $e")
- return
- }
+ val (compiledSources, sourceSets) =
+ try {
+ getFiles(runnerProject, mavenId)
+ } catch (e: TypedResolveException) {
+ runnerProject.logger.info("Ignoring missing artifact $mavenId: $e")
+ return
+ }
if (outputApiLocation.publicApiFile.exists()) {
project.logger.lifecycle("Regenerating $mavenId")
+ val projectXml = File(temporaryDir, "$mavenId-project.xml")
+ ProjectXml.create(
+ sourceSets,
+ project.getAndroidJar().files,
+ compiledSources,
+ projectXml
+ )
generateApi(
project.getMetalavaClasspath(),
- null,
- inputs,
+ projectXml,
+ sourceSets.flatMap { it.sourcePaths.files },
outputApiLocation,
ApiLintMode.Skip,
generateRestrictToLibraryGroupAPIs,
@@ -187,16 +193,32 @@
}
}
- private fun getFiles(runnerProject: Project, mavenId: String): CompilationInputs {
+ /**
+ * For the given [mavenId], returns a pair with the source jar as the first element, and
+ * [SourceSetInputs] representing the unzipped sources as the second element.
+ */
+ private fun getFiles(
+ runnerProject: Project,
+ mavenId: String
+ ): Pair<File, List<SourceSetInputs>> {
val jars = getJars(runnerProject, mavenId)
- val sources = getSources(runnerProject, "$mavenId:sources")
+ val sourcesMavenId = "$mavenId:sources"
+ val compiledSources = getCompiledSources(runnerProject, sourcesMavenId)
+ val sources = getSources(runnerProject, sourcesMavenId, compiledSources)
// TODO(b/330721660) parse META-INF/kotlin-project-structure-metadata.json for KMP projects
- return StandardCompilationInputs(
- sourcePaths = sources,
- dependencyClasspath = jars,
- bootClasspath = project.getAndroidJar()
- )
+ // Represent the project as a single source set.
+ return compiledSources to
+ listOf(
+ SourceSetInputs(
+ // Since there's just one source set, the name is arbitrary.
+ sourceSetName = "main",
+ // There are no other source sets to depend on.
+ dependsOnSourceSets = emptyList(),
+ sourcePaths = sources,
+ dependencyClasspath = jars,
+ )
+ )
}
private fun getJars(runnerProject: Project, mavenId: String): FileCollection {
@@ -230,18 +252,27 @@
return runnerProject.files()
}
- private fun getSources(runnerProject: Project, mavenId: String): FileCollection {
+ /** Returns the source jar for the [mavenId]. */
+ private fun getCompiledSources(runnerProject: Project, mavenId: String): File {
val configuration =
runnerProject.configurations.detachedConfiguration(
runnerProject.dependencies.create(mavenId)
)
configuration.isTransitive = false
+ return configuration.singleFile
+ }
+ /** Returns a file collection containing the unzipped sources from [compiledSources]. */
+ private fun getSources(
+ runnerProject: Project,
+ mavenId: String,
+ compiledSources: File
+ ): FileCollection {
val sanitizedMavenId = mavenId.replace(":", "-")
@Suppress("DEPRECATION")
val unzippedDir = File("${runnerProject.buildDir.path}/sources-unzipped/$sanitizedMavenId")
runnerProject.copy { copySpec ->
- copySpec.from(runnerProject.zipTree(configuration.singleFile))
+ copySpec.from(runnerProject.zipTree(compiledSources))
copySpec.into(unzippedDir)
}
return project.files(unzippedDir)
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/metalava/UpdateBaselineTasks.kt b/buildSrc/private/src/main/kotlin/androidx/build/metalava/UpdateBaselineTasks.kt
index 3b6d269..fcc8eaa 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/metalava/UpdateBaselineTasks.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/metalava/UpdateBaselineTasks.kt
@@ -55,8 +55,6 @@
val baselineFile = baselines.get().apiLintFile
val checkArgs =
getGenerateApiArgs(
- bootClasspath,
- dependencyClasspath,
createProjectXmlFile(),
sourcePaths.files.filter { it.exists() },
null,
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraFactoryProvider.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraFactoryProvider.kt
index 2f84338..0c80536 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraFactoryProvider.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraFactoryProvider.kt
@@ -96,9 +96,6 @@
Debug.traceStart { "Create CameraPipe" }
val timeSource = SystemTimeSource()
val start = Timestamps.now(timeSource)
- // Enable pruning device manager when tested in the MH lab.
- val usePruningDeviceManager =
- android.util.Log.isLoggable(CAMERA_PIPE_MH_FLAG, android.util.Log.DEBUG)
val cameraPipe =
CameraPipe(
@@ -110,16 +107,11 @@
sharedInteropCallbacks.sessionStateCallback,
openRetryMaxTimeout
),
- usePruningDeviceManager = usePruningDeviceManager
+ usePruningDeviceManager = true
)
)
Log.debug { "Created CameraPipe in ${start.measureNow(timeSource).formatMs()}" }
Debug.traceStop()
return cameraPipe
}
-
- private companion object {
- // Flag set when being tested in the lab. Refer to CameraPipeConfigTestRule for more info.
- const val CAMERA_PIPE_MH_FLAG = "CameraPipeMH"
- }
}
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraControlImplDeviceTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraControlImplDeviceTest.java
index ab6cf33..3191f91 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraControlImplDeviceTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/Camera2CameraControlImplDeviceTest.java
@@ -455,7 +455,7 @@
int defaultStrength = mCamera.getCameraInfo().getTorchStrengthLevel().getValue();
// If the default strength is the max, set the strength to 1, otherwise, set to max.
int customizedStrength = defaultStrength == maxStrength ? 1 : maxStrength;
- camera2CameraControlImpl.setTorchStrengthLevelAsync(customizedStrength).get();
+ camera2CameraControlImpl.setTorchStrengthLevel(customizedStrength).get();
// Assert: the customized strength is applied
Camera2ImplConfig camera2Config = new Camera2ImplConfig(
@@ -483,7 +483,7 @@
// Act & Assert
try {
- camera2CameraControlImpl.setTorchStrengthLevelAsync(0).get();
+ camera2CameraControlImpl.setTorchStrengthLevel(0).get();
} catch (ExecutionException e) {
assertThat(e.getCause()).isInstanceOf(IllegalArgumentException.class);
return;
@@ -510,7 +510,7 @@
// Act & Assert
try {
- camera2CameraControlImpl.setTorchStrengthLevelAsync(
+ camera2CameraControlImpl.setTorchStrengthLevel(
mCamera.getCameraInfo().getMaxTorchStrengthLevel() + 1).get();
} catch (ExecutionException e) {
assertThat(e.getCause()).isInstanceOf(IllegalArgumentException.class);
@@ -631,7 +631,7 @@
int defaultStrength = mCamera.getCameraInfo().getTorchStrengthLevel().getValue();
// If the default strength is the max, set the strength to 1, otherwise, set to max.
int customizedStrength = defaultStrength == maxStrength ? 1 : maxStrength;
- camera2CameraControlImpl.setTorchStrengthLevelAsync(customizedStrength).get();
+ camera2CameraControlImpl.setTorchStrengthLevel(customizedStrength).get();
// Assert: the capture uses default torch strength
CaptureConfig.Builder captureConfigBuilder = new CaptureConfig.Builder();
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraControlImpl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraControlImpl.java
index d6321f2..fa9b4f7 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraControlImpl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraControlImpl.java
@@ -510,7 +510,7 @@
}
@Override
- public @NonNull ListenableFuture<Void> setTorchStrengthLevelAsync(
+ public @NonNull ListenableFuture<Void> setTorchStrengthLevel(
@IntRange(from = 1) int torchStrengthLevel) {
if (!isControlInUse()) {
return Futures.immediateFailedFuture(
diff --git a/camera/camera-core/api/current.txt b/camera/camera-core/api/current.txt
index ee229d04..002f57a 100644
--- a/camera/camera-core/api/current.txt
+++ b/camera/camera-core/api/current.txt
@@ -18,7 +18,7 @@
method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enableTorch(boolean);
method public com.google.common.util.concurrent.ListenableFuture<java.lang.Integer!> setExposureCompensationIndex(int);
method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setLinearZoom(@FloatRange(from=0.0f, to=1.0f) float);
- method public default com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setTorchStrengthLevelAsync(@IntRange(from=1) int);
+ method public default com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setTorchStrengthLevel(@IntRange(from=1) int);
method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setZoomRatio(float);
method public com.google.common.util.concurrent.ListenableFuture<androidx.camera.core.FocusMeteringResult!> startFocusAndMetering(androidx.camera.core.FocusMeteringAction);
}
diff --git a/camera/camera-core/api/restricted_current.txt b/camera/camera-core/api/restricted_current.txt
index ee229d04..002f57a 100644
--- a/camera/camera-core/api/restricted_current.txt
+++ b/camera/camera-core/api/restricted_current.txt
@@ -18,7 +18,7 @@
method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enableTorch(boolean);
method public com.google.common.util.concurrent.ListenableFuture<java.lang.Integer!> setExposureCompensationIndex(int);
method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setLinearZoom(@FloatRange(from=0.0f, to=1.0f) float);
- method public default com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setTorchStrengthLevelAsync(@IntRange(from=1) int);
+ method public default com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setTorchStrengthLevel(@IntRange(from=1) int);
method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setZoomRatio(float);
method public com.google.common.util.concurrent.ListenableFuture<androidx.camera.core.FocusMeteringResult!> startFocusAndMetering(androidx.camera.core.FocusMeteringAction);
}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/CameraControl.java b/camera/camera-core/src/main/java/androidx/camera/core/CameraControl.java
index 2332ad6..d0ab733 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/CameraControl.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/CameraControl.java
@@ -253,7 +253,8 @@
* @return a {@link ListenableFuture} that is completed when the torch strength has been
* applied.
*/
- default @NonNull ListenableFuture<Void> setTorchStrengthLevelAsync(
+ @SuppressWarnings("AsyncSuffixFuture")
+ default @NonNull ListenableFuture<Void> setTorchStrengthLevel(
@IntRange(from = 1) int torchStrengthLevel) {
return Futures.immediateFailedFuture(new UnsupportedOperationException(
"Setting torch strength is not supported on the device."));
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/CameraInfo.java b/camera/camera-core/src/main/java/androidx/camera/core/CameraInfo.java
index cd2f8a2..a952344 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/CameraInfo.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/CameraInfo.java
@@ -447,7 +447,7 @@
* Returns the {@link LiveData} of the torch strength level.
*
* <p>The value of the {@link LiveData} will be the default torch strength level of this
- * device if {@link CameraControl#setTorchStrengthLevelAsync(int)} hasn't been called.
+ * device if {@link CameraControl#setTorchStrengthLevel(int)} hasn't been called.
*
* <p>The value of the {@link LiveData} will be {@link #TORCH_STRENGTH_LEVEL_UNSUPPORTED} if
* the device doesn't have a flash unit or doesn't support configuring torch strength.
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/ForwardingCameraControl.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/ForwardingCameraControl.java
index b39ebca..f18d111 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/ForwardingCameraControl.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/ForwardingCameraControl.java
@@ -84,9 +84,9 @@
}
@Override
- public @NonNull ListenableFuture<Void> setTorchStrengthLevelAsync(
+ public @NonNull ListenableFuture<Void> setTorchStrengthLevel(
@IntRange(from = 1) int torchStrengthLevel) {
- return mCameraControlInternal.setTorchStrengthLevelAsync(torchStrengthLevel);
+ return mCameraControlInternal.setTorchStrengthLevel(torchStrengthLevel);
}
@Override
diff --git a/compose/animation/animation-core/proguard-rules.pro b/compose/animation/animation-core/proguard-rules.pro
index 67d118b..72f4a6c 100644
--- a/compose/animation/animation-core/proguard-rules.pro
+++ b/compose/animation/animation-core/proguard-rules.pro
@@ -15,7 +15,7 @@
# Keep all the functions created to throw an exception. We don't want these functions to be
# inlined in any way, which R8 will do by default. The whole point of these functions is to
# reduce the amount of code generated at the call site.
--keep,allowshrinking,allowobfuscation class androidx.compose.**.* {
+-keepclassmembers,allowshrinking,allowobfuscation class androidx.compose.**.* {
static void throw*Exception(...);
static void throw*ExceptionForNullCheck(...);
# For methods returning Nothing
diff --git a/compose/foundation/foundation-layout/api/current.txt b/compose/foundation/foundation-layout/api/current.txt
index 0576ad7..7b4c673 100644
--- a/compose/foundation/foundation-layout/api/current.txt
+++ b/compose/foundation/foundation-layout/api/current.txt
@@ -111,81 +111,81 @@
method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier weight(androidx.compose.ui.Modifier, @FloatRange(from=0.0, fromInclusive=false) float weight, optional boolean fill);
}
- @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final class ContextualFlowColumnOverflow extends androidx.compose.foundation.layout.FlowLayoutOverflow {
- field public static final androidx.compose.foundation.layout.ContextualFlowColumnOverflow.Companion Companion;
+ @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final class ContextualFlowColumnOverflow extends androidx.compose.foundation.layout.FlowLayoutOverflow {
+ field @Deprecated public static final androidx.compose.foundation.layout.ContextualFlowColumnOverflow.Companion Companion;
}
- public static final class ContextualFlowColumnOverflow.Companion {
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.ContextualFlowColumnOverflow expandIndicator(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ContextualFlowColumnOverflowScope,kotlin.Unit> content);
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.ContextualFlowColumnOverflow expandOrCollapseIndicator(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ContextualFlowColumnOverflowScope,kotlin.Unit> expandIndicator, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ContextualFlowColumnOverflowScope,kotlin.Unit> collapseIndicator, optional int minColumnsToShowCollapse, optional float minWidthToShowCollapse);
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.ContextualFlowColumnOverflow getClip();
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.ContextualFlowColumnOverflow getVisible();
- property @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final androidx.compose.foundation.layout.ContextualFlowColumnOverflow Clip;
- property @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final androidx.compose.foundation.layout.ContextualFlowColumnOverflow Visible;
+ @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public static final class ContextualFlowColumnOverflow.Companion {
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.ContextualFlowColumnOverflow expandIndicator(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ContextualFlowColumnOverflowScope,kotlin.Unit> content);
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.ContextualFlowColumnOverflow expandOrCollapseIndicator(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ContextualFlowColumnOverflowScope,kotlin.Unit> expandIndicator, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ContextualFlowColumnOverflowScope,kotlin.Unit> collapseIndicator, optional int minColumnsToShowCollapse, optional float minWidthToShowCollapse);
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.ContextualFlowColumnOverflow getClip();
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.ContextualFlowColumnOverflow getVisible();
+ property @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final androidx.compose.foundation.layout.ContextualFlowColumnOverflow Clip;
+ property @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final androidx.compose.foundation.layout.ContextualFlowColumnOverflow Visible;
}
- @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Stable public interface ContextualFlowColumnOverflowScope extends androidx.compose.foundation.layout.FlowColumnOverflowScope {
+ @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Stable public interface ContextualFlowColumnOverflowScope extends androidx.compose.foundation.layout.FlowColumnOverflowScope {
}
- @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Stable public interface ContextualFlowColumnScope extends androidx.compose.foundation.layout.ColumnScope {
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.ui.Modifier fillMaxColumnWidth(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
- method public int getIndexInLine();
- method public int getLineIndex();
- method public float getMaxHeightInLine();
- method public float getMaxWidth();
- property public abstract int indexInLine;
- property public abstract int lineIndex;
- property public abstract float maxHeightInLine;
- property public abstract float maxWidth;
+ @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Stable public interface ContextualFlowColumnScope extends androidx.compose.foundation.layout.ColumnScope {
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.ui.Modifier fillMaxColumnWidth(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
+ method @Deprecated public int getIndexInLine();
+ method @Deprecated public int getLineIndex();
+ method @Deprecated public float getMaxHeightInLine();
+ method @Deprecated public float getMaxWidth();
+ property @Deprecated public abstract int indexInLine;
+ property @Deprecated public abstract int lineIndex;
+ property @Deprecated public abstract float maxHeightInLine;
+ property @Deprecated public abstract float maxWidth;
}
public final class ContextualFlowLayoutKt {
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public static void ContextualFlowColumn(int itemCount, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.ui.Alignment.Horizontal itemHorizontalAlignment, optional int maxItemsInEachColumn, optional int maxLines, optional androidx.compose.foundation.layout.ContextualFlowColumnOverflow overflow, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.layout.ContextualFlowColumnScope,? super java.lang.Integer,kotlin.Unit> content);
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public static void ContextualFlowRow(int itemCount, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.ui.Alignment.Vertical itemVerticalAlignment, optional int maxItemsInEachRow, optional int maxLines, optional androidx.compose.foundation.layout.ContextualFlowRowOverflow overflow, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.layout.ContextualFlowRowScope,? super java.lang.Integer,kotlin.Unit> content);
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public static void ContextualFlowColumn(int itemCount, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.ui.Alignment.Horizontal itemHorizontalAlignment, optional int maxItemsInEachColumn, optional int maxLines, optional androidx.compose.foundation.layout.ContextualFlowColumnOverflow overflow, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.layout.ContextualFlowColumnScope,? super java.lang.Integer,kotlin.Unit> content);
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public static void ContextualFlowRow(int itemCount, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.ui.Alignment.Vertical itemVerticalAlignment, optional int maxItemsInEachRow, optional int maxLines, optional androidx.compose.foundation.layout.ContextualFlowRowOverflow overflow, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.layout.ContextualFlowRowScope,? super java.lang.Integer,kotlin.Unit> content);
}
- @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final class ContextualFlowRowOverflow extends androidx.compose.foundation.layout.FlowLayoutOverflow {
- field public static final androidx.compose.foundation.layout.ContextualFlowRowOverflow.Companion Companion;
+ @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final class ContextualFlowRowOverflow extends androidx.compose.foundation.layout.FlowLayoutOverflow {
+ field @Deprecated public static final androidx.compose.foundation.layout.ContextualFlowRowOverflow.Companion Companion;
}
- public static final class ContextualFlowRowOverflow.Companion {
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.ContextualFlowRowOverflow expandIndicator(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ContextualFlowRowOverflowScope,kotlin.Unit> content);
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.ContextualFlowRowOverflow expandOrCollapseIndicator(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ContextualFlowRowOverflowScope,kotlin.Unit> expandIndicator, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ContextualFlowRowOverflowScope,kotlin.Unit> collapseIndicator, optional int minRowsToShowCollapse, optional float minHeightToShowCollapse);
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.ContextualFlowRowOverflow getClip();
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.ContextualFlowRowOverflow getVisible();
- property @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final androidx.compose.foundation.layout.ContextualFlowRowOverflow Clip;
- property @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final androidx.compose.foundation.layout.ContextualFlowRowOverflow Visible;
+ @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public static final class ContextualFlowRowOverflow.Companion {
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.ContextualFlowRowOverflow expandIndicator(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ContextualFlowRowOverflowScope,kotlin.Unit> content);
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.ContextualFlowRowOverflow expandOrCollapseIndicator(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ContextualFlowRowOverflowScope,kotlin.Unit> expandIndicator, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ContextualFlowRowOverflowScope,kotlin.Unit> collapseIndicator, optional int minRowsToShowCollapse, optional float minHeightToShowCollapse);
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.ContextualFlowRowOverflow getClip();
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.ContextualFlowRowOverflow getVisible();
+ property @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final androidx.compose.foundation.layout.ContextualFlowRowOverflow Clip;
+ property @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final androidx.compose.foundation.layout.ContextualFlowRowOverflow Visible;
}
- @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Stable public interface ContextualFlowRowOverflowScope extends androidx.compose.foundation.layout.FlowRowOverflowScope {
+ @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Stable public interface ContextualFlowRowOverflowScope extends androidx.compose.foundation.layout.FlowRowOverflowScope {
}
- @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Stable public interface ContextualFlowRowScope extends androidx.compose.foundation.layout.RowScope {
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.ui.Modifier fillMaxRowHeight(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
- method public int getIndexInLine();
- method public int getLineIndex();
- method public float getMaxHeight();
- method public float getMaxWidthInLine();
- property public abstract int indexInLine;
- property public abstract int lineIndex;
- property public abstract float maxHeight;
- property public abstract float maxWidthInLine;
+ @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Stable public interface ContextualFlowRowScope extends androidx.compose.foundation.layout.RowScope {
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.ui.Modifier fillMaxRowHeight(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
+ method @Deprecated public int getIndexInLine();
+ method @Deprecated public int getLineIndex();
+ method @Deprecated public float getMaxHeight();
+ method @Deprecated public float getMaxWidthInLine();
+ property @Deprecated public abstract int indexInLine;
+ property @Deprecated public abstract int lineIndex;
+ property @Deprecated public abstract float maxHeight;
+ property @Deprecated public abstract float maxWidthInLine;
}
@SuppressCompatibility @kotlin.RequiresOptIn(message="The API of this layout is experimental and is likely to change in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalLayoutApi {
}
- @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final class FlowColumnOverflow extends androidx.compose.foundation.layout.FlowLayoutOverflow {
- field public static final androidx.compose.foundation.layout.FlowColumnOverflow.Companion Companion;
+ @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final class FlowColumnOverflow extends androidx.compose.foundation.layout.FlowLayoutOverflow {
+ field @Deprecated public static final androidx.compose.foundation.layout.FlowColumnOverflow.Companion Companion;
}
- public static final class FlowColumnOverflow.Companion {
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.FlowColumnOverflow expandIndicator(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowColumnOverflowScope,kotlin.Unit> content);
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.FlowColumnOverflow expandOrCollapseIndicator(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowColumnOverflowScope,kotlin.Unit> expandIndicator, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowColumnOverflowScope,kotlin.Unit> collapseIndicator, optional int minColumnsToShowCollapse, optional float minWidthToShowCollapse);
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.FlowColumnOverflow getClip();
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.FlowColumnOverflow getVisible();
- property @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final androidx.compose.foundation.layout.FlowColumnOverflow Clip;
- property @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final androidx.compose.foundation.layout.FlowColumnOverflow Visible;
+ @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public static final class FlowColumnOverflow.Companion {
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.FlowColumnOverflow expandIndicator(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowColumnOverflowScope,kotlin.Unit> content);
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.FlowColumnOverflow expandOrCollapseIndicator(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowColumnOverflowScope,kotlin.Unit> expandIndicator, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowColumnOverflowScope,kotlin.Unit> collapseIndicator, optional int minColumnsToShowCollapse, optional float minWidthToShowCollapse);
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.FlowColumnOverflow getClip();
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.FlowColumnOverflow getVisible();
+ property @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final androidx.compose.foundation.layout.FlowColumnOverflow Clip;
+ property @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final androidx.compose.foundation.layout.FlowColumnOverflow Visible;
}
@SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Stable public interface FlowColumnOverflowScope extends androidx.compose.foundation.layout.FlowColumnScope {
@@ -200,24 +200,26 @@
}
public final class FlowLayoutKt {
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public static void FlowColumn(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.ui.Alignment.Horizontal itemHorizontalAlignment, optional int maxItemsInEachColumn, optional int maxLines, optional androidx.compose.foundation.layout.FlowColumnOverflow overflow, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowColumnScope,kotlin.Unit> content);
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public static void FlowRow(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.ui.Alignment.Vertical itemVerticalAlignment, optional int maxItemsInEachRow, optional int maxLines, optional androidx.compose.foundation.layout.FlowRowOverflow overflow, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowRowScope,kotlin.Unit> content);
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public static void FlowColumn(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.ui.Alignment.Horizontal itemHorizontalAlignment, optional int maxItemsInEachColumn, optional int maxLines, optional androidx.compose.foundation.layout.FlowColumnOverflow overflow, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowColumnScope,kotlin.Unit> content);
+ method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public static void FlowColumn(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.ui.Alignment.Horizontal itemHorizontalAlignment, optional int maxItemsInEachColumn, optional int maxLines, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowColumnScope,kotlin.Unit> content);
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public static void FlowRow(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.ui.Alignment.Vertical itemVerticalAlignment, optional int maxItemsInEachRow, optional int maxLines, optional androidx.compose.foundation.layout.FlowRowOverflow overflow, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowRowScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void FlowRow(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.ui.Alignment.Vertical itemVerticalAlignment, optional int maxItemsInEachRow, optional int maxLines, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowRowScope,kotlin.Unit> content);
}
- @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public abstract sealed class FlowLayoutOverflow {
+ @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public abstract sealed class FlowLayoutOverflow {
}
- @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final class FlowRowOverflow extends androidx.compose.foundation.layout.FlowLayoutOverflow {
- field public static final androidx.compose.foundation.layout.FlowRowOverflow.Companion Companion;
+ @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final class FlowRowOverflow extends androidx.compose.foundation.layout.FlowLayoutOverflow {
+ field @Deprecated public static final androidx.compose.foundation.layout.FlowRowOverflow.Companion Companion;
}
- public static final class FlowRowOverflow.Companion {
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.FlowRowOverflow expandIndicator(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowRowOverflowScope,kotlin.Unit> content);
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.FlowRowOverflow expandOrCollapseIndicator(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowRowOverflowScope,kotlin.Unit> expandIndicator, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowRowOverflowScope,kotlin.Unit> collapseIndicator, optional int minRowsToShowCollapse, optional float minHeightToShowCollapse);
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.FlowRowOverflow getClip();
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.FlowRowOverflow getVisible();
- property @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final androidx.compose.foundation.layout.FlowRowOverflow Clip;
- property @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final androidx.compose.foundation.layout.FlowRowOverflow Visible;
+ @Deprecated public static final class FlowRowOverflow.Companion {
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.FlowRowOverflow expandIndicator(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowRowOverflowScope,kotlin.Unit> content);
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.FlowRowOverflow expandOrCollapseIndicator(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowRowOverflowScope,kotlin.Unit> expandIndicator, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowRowOverflowScope,kotlin.Unit> collapseIndicator, optional int minRowsToShowCollapse, optional float minHeightToShowCollapse);
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.FlowRowOverflow getClip();
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.FlowRowOverflow getVisible();
+ property @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final androidx.compose.foundation.layout.FlowRowOverflow Clip;
+ property @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final androidx.compose.foundation.layout.FlowRowOverflow Visible;
}
@SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Stable public interface FlowRowOverflowScope extends androidx.compose.foundation.layout.FlowRowScope {
diff --git a/compose/foundation/foundation-layout/api/restricted_current.txt b/compose/foundation/foundation-layout/api/restricted_current.txt
index 1a68ea9..438f4c8 100644
--- a/compose/foundation/foundation-layout/api/restricted_current.txt
+++ b/compose/foundation/foundation-layout/api/restricted_current.txt
@@ -116,81 +116,81 @@
method @androidx.compose.runtime.Stable public androidx.compose.ui.Modifier weight(androidx.compose.ui.Modifier, @FloatRange(from=0.0, fromInclusive=false) float weight, optional boolean fill);
}
- @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final class ContextualFlowColumnOverflow extends androidx.compose.foundation.layout.FlowLayoutOverflow {
- field public static final androidx.compose.foundation.layout.ContextualFlowColumnOverflow.Companion Companion;
+ @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final class ContextualFlowColumnOverflow extends androidx.compose.foundation.layout.FlowLayoutOverflow {
+ field @Deprecated public static final androidx.compose.foundation.layout.ContextualFlowColumnOverflow.Companion Companion;
}
- public static final class ContextualFlowColumnOverflow.Companion {
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.ContextualFlowColumnOverflow expandIndicator(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ContextualFlowColumnOverflowScope,kotlin.Unit> content);
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.ContextualFlowColumnOverflow expandOrCollapseIndicator(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ContextualFlowColumnOverflowScope,kotlin.Unit> expandIndicator, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ContextualFlowColumnOverflowScope,kotlin.Unit> collapseIndicator, optional int minColumnsToShowCollapse, optional float minWidthToShowCollapse);
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.ContextualFlowColumnOverflow getClip();
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.ContextualFlowColumnOverflow getVisible();
- property @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final androidx.compose.foundation.layout.ContextualFlowColumnOverflow Clip;
- property @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final androidx.compose.foundation.layout.ContextualFlowColumnOverflow Visible;
+ @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public static final class ContextualFlowColumnOverflow.Companion {
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.ContextualFlowColumnOverflow expandIndicator(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ContextualFlowColumnOverflowScope,kotlin.Unit> content);
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.ContextualFlowColumnOverflow expandOrCollapseIndicator(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ContextualFlowColumnOverflowScope,kotlin.Unit> expandIndicator, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ContextualFlowColumnOverflowScope,kotlin.Unit> collapseIndicator, optional int minColumnsToShowCollapse, optional float minWidthToShowCollapse);
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.ContextualFlowColumnOverflow getClip();
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.ContextualFlowColumnOverflow getVisible();
+ property @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final androidx.compose.foundation.layout.ContextualFlowColumnOverflow Clip;
+ property @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final androidx.compose.foundation.layout.ContextualFlowColumnOverflow Visible;
}
- @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Stable public interface ContextualFlowColumnOverflowScope extends androidx.compose.foundation.layout.FlowColumnOverflowScope {
+ @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Stable public interface ContextualFlowColumnOverflowScope extends androidx.compose.foundation.layout.FlowColumnOverflowScope {
}
- @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Stable public interface ContextualFlowColumnScope extends androidx.compose.foundation.layout.ColumnScope {
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.ui.Modifier fillMaxColumnWidth(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
- method public int getIndexInLine();
- method public int getLineIndex();
- method public float getMaxHeightInLine();
- method public float getMaxWidth();
- property public abstract int indexInLine;
- property public abstract int lineIndex;
- property public abstract float maxHeightInLine;
- property public abstract float maxWidth;
+ @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Stable public interface ContextualFlowColumnScope extends androidx.compose.foundation.layout.ColumnScope {
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.ui.Modifier fillMaxColumnWidth(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
+ method @Deprecated public int getIndexInLine();
+ method @Deprecated public int getLineIndex();
+ method @Deprecated public float getMaxHeightInLine();
+ method @Deprecated public float getMaxWidth();
+ property @Deprecated public abstract int indexInLine;
+ property @Deprecated public abstract int lineIndex;
+ property @Deprecated public abstract float maxHeightInLine;
+ property @Deprecated public abstract float maxWidth;
}
public final class ContextualFlowLayoutKt {
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public static void ContextualFlowColumn(int itemCount, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.ui.Alignment.Horizontal itemHorizontalAlignment, optional int maxItemsInEachColumn, optional int maxLines, optional androidx.compose.foundation.layout.ContextualFlowColumnOverflow overflow, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.layout.ContextualFlowColumnScope,? super java.lang.Integer,kotlin.Unit> content);
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public static void ContextualFlowRow(int itemCount, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.ui.Alignment.Vertical itemVerticalAlignment, optional int maxItemsInEachRow, optional int maxLines, optional androidx.compose.foundation.layout.ContextualFlowRowOverflow overflow, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.layout.ContextualFlowRowScope,? super java.lang.Integer,kotlin.Unit> content);
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public static void ContextualFlowColumn(int itemCount, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.ui.Alignment.Horizontal itemHorizontalAlignment, optional int maxItemsInEachColumn, optional int maxLines, optional androidx.compose.foundation.layout.ContextualFlowColumnOverflow overflow, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.layout.ContextualFlowColumnScope,? super java.lang.Integer,kotlin.Unit> content);
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public static void ContextualFlowRow(int itemCount, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.ui.Alignment.Vertical itemVerticalAlignment, optional int maxItemsInEachRow, optional int maxLines, optional androidx.compose.foundation.layout.ContextualFlowRowOverflow overflow, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.layout.ContextualFlowRowScope,? super java.lang.Integer,kotlin.Unit> content);
}
- @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final class ContextualFlowRowOverflow extends androidx.compose.foundation.layout.FlowLayoutOverflow {
- field public static final androidx.compose.foundation.layout.ContextualFlowRowOverflow.Companion Companion;
+ @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final class ContextualFlowRowOverflow extends androidx.compose.foundation.layout.FlowLayoutOverflow {
+ field @Deprecated public static final androidx.compose.foundation.layout.ContextualFlowRowOverflow.Companion Companion;
}
- public static final class ContextualFlowRowOverflow.Companion {
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.ContextualFlowRowOverflow expandIndicator(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ContextualFlowRowOverflowScope,kotlin.Unit> content);
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.ContextualFlowRowOverflow expandOrCollapseIndicator(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ContextualFlowRowOverflowScope,kotlin.Unit> expandIndicator, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ContextualFlowRowOverflowScope,kotlin.Unit> collapseIndicator, optional int minRowsToShowCollapse, optional float minHeightToShowCollapse);
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.ContextualFlowRowOverflow getClip();
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.ContextualFlowRowOverflow getVisible();
- property @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final androidx.compose.foundation.layout.ContextualFlowRowOverflow Clip;
- property @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final androidx.compose.foundation.layout.ContextualFlowRowOverflow Visible;
+ @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public static final class ContextualFlowRowOverflow.Companion {
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.ContextualFlowRowOverflow expandIndicator(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ContextualFlowRowOverflowScope,kotlin.Unit> content);
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.ContextualFlowRowOverflow expandOrCollapseIndicator(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ContextualFlowRowOverflowScope,kotlin.Unit> expandIndicator, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ContextualFlowRowOverflowScope,kotlin.Unit> collapseIndicator, optional int minRowsToShowCollapse, optional float minHeightToShowCollapse);
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.ContextualFlowRowOverflow getClip();
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.ContextualFlowRowOverflow getVisible();
+ property @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final androidx.compose.foundation.layout.ContextualFlowRowOverflow Clip;
+ property @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final androidx.compose.foundation.layout.ContextualFlowRowOverflow Visible;
}
- @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Stable public interface ContextualFlowRowOverflowScope extends androidx.compose.foundation.layout.FlowRowOverflowScope {
+ @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Stable public interface ContextualFlowRowOverflowScope extends androidx.compose.foundation.layout.FlowRowOverflowScope {
}
- @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Stable public interface ContextualFlowRowScope extends androidx.compose.foundation.layout.RowScope {
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.ui.Modifier fillMaxRowHeight(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
- method public int getIndexInLine();
- method public int getLineIndex();
- method public float getMaxHeight();
- method public float getMaxWidthInLine();
- property public abstract int indexInLine;
- property public abstract int lineIndex;
- property public abstract float maxHeight;
- property public abstract float maxWidthInLine;
+ @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Stable public interface ContextualFlowRowScope extends androidx.compose.foundation.layout.RowScope {
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.ui.Modifier fillMaxRowHeight(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
+ method @Deprecated public int getIndexInLine();
+ method @Deprecated public int getLineIndex();
+ method @Deprecated public float getMaxHeight();
+ method @Deprecated public float getMaxWidthInLine();
+ property @Deprecated public abstract int indexInLine;
+ property @Deprecated public abstract int lineIndex;
+ property @Deprecated public abstract float maxHeight;
+ property @Deprecated public abstract float maxWidthInLine;
}
@SuppressCompatibility @kotlin.RequiresOptIn(message="The API of this layout is experimental and is likely to change in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalLayoutApi {
}
- @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final class FlowColumnOverflow extends androidx.compose.foundation.layout.FlowLayoutOverflow {
- field public static final androidx.compose.foundation.layout.FlowColumnOverflow.Companion Companion;
+ @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final class FlowColumnOverflow extends androidx.compose.foundation.layout.FlowLayoutOverflow {
+ field @Deprecated public static final androidx.compose.foundation.layout.FlowColumnOverflow.Companion Companion;
}
- public static final class FlowColumnOverflow.Companion {
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.FlowColumnOverflow expandIndicator(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowColumnOverflowScope,kotlin.Unit> content);
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.FlowColumnOverflow expandOrCollapseIndicator(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowColumnOverflowScope,kotlin.Unit> expandIndicator, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowColumnOverflowScope,kotlin.Unit> collapseIndicator, optional int minColumnsToShowCollapse, optional float minWidthToShowCollapse);
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.FlowColumnOverflow getClip();
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.FlowColumnOverflow getVisible();
- property @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final androidx.compose.foundation.layout.FlowColumnOverflow Clip;
- property @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final androidx.compose.foundation.layout.FlowColumnOverflow Visible;
+ @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public static final class FlowColumnOverflow.Companion {
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.FlowColumnOverflow expandIndicator(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowColumnOverflowScope,kotlin.Unit> content);
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.FlowColumnOverflow expandOrCollapseIndicator(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowColumnOverflowScope,kotlin.Unit> expandIndicator, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowColumnOverflowScope,kotlin.Unit> collapseIndicator, optional int minColumnsToShowCollapse, optional float minWidthToShowCollapse);
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.FlowColumnOverflow getClip();
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.FlowColumnOverflow getVisible();
+ property @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final androidx.compose.foundation.layout.FlowColumnOverflow Clip;
+ property @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final androidx.compose.foundation.layout.FlowColumnOverflow Visible;
}
@SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Stable public interface FlowColumnOverflowScope extends androidx.compose.foundation.layout.FlowColumnScope {
@@ -205,26 +205,28 @@
}
public final class FlowLayoutKt {
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public static void FlowColumn(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.ui.Alignment.Horizontal itemHorizontalAlignment, optional int maxItemsInEachColumn, optional int maxLines, optional androidx.compose.foundation.layout.FlowColumnOverflow overflow, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowColumnScope,kotlin.Unit> content);
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public static void FlowRow(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.ui.Alignment.Vertical itemVerticalAlignment, optional int maxItemsInEachRow, optional int maxLines, optional androidx.compose.foundation.layout.FlowRowOverflow overflow, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowRowScope,kotlin.Unit> content);
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public static void FlowColumn(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.ui.Alignment.Horizontal itemHorizontalAlignment, optional int maxItemsInEachColumn, optional int maxLines, optional androidx.compose.foundation.layout.FlowColumnOverflow overflow, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowColumnScope,kotlin.Unit> content);
+ method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public static void FlowColumn(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.ui.Alignment.Horizontal itemHorizontalAlignment, optional int maxItemsInEachColumn, optional int maxLines, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowColumnScope,kotlin.Unit> content);
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public static void FlowRow(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.ui.Alignment.Vertical itemVerticalAlignment, optional int maxItemsInEachRow, optional int maxLines, optional androidx.compose.foundation.layout.FlowRowOverflow overflow, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowRowScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void FlowRow(optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.ui.Alignment.Vertical itemVerticalAlignment, optional int maxItemsInEachRow, optional int maxLines, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowRowScope,kotlin.Unit> content);
method @androidx.compose.runtime.Composable @kotlin.PublishedApi internal static androidx.compose.ui.layout.MeasurePolicy columnMeasurementHelper(androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, int maxItemsInMainAxis);
method @androidx.compose.runtime.Composable @kotlin.PublishedApi internal static androidx.compose.ui.layout.MeasurePolicy rowMeasurementHelper(androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, int maxItemsInMainAxis);
}
- @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public abstract sealed class FlowLayoutOverflow {
+ @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public abstract sealed class FlowLayoutOverflow {
}
- @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final class FlowRowOverflow extends androidx.compose.foundation.layout.FlowLayoutOverflow {
- field public static final androidx.compose.foundation.layout.FlowRowOverflow.Companion Companion;
+ @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final class FlowRowOverflow extends androidx.compose.foundation.layout.FlowLayoutOverflow {
+ field @Deprecated public static final androidx.compose.foundation.layout.FlowRowOverflow.Companion Companion;
}
- public static final class FlowRowOverflow.Companion {
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.FlowRowOverflow expandIndicator(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowRowOverflowScope,kotlin.Unit> content);
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.FlowRowOverflow expandOrCollapseIndicator(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowRowOverflowScope,kotlin.Unit> expandIndicator, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowRowOverflowScope,kotlin.Unit> collapseIndicator, optional int minRowsToShowCollapse, optional float minHeightToShowCollapse);
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.FlowRowOverflow getClip();
- method @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.FlowRowOverflow getVisible();
- property @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final androidx.compose.foundation.layout.FlowRowOverflow Clip;
- property @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final androidx.compose.foundation.layout.FlowRowOverflow Visible;
+ @Deprecated public static final class FlowRowOverflow.Companion {
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.FlowRowOverflow expandIndicator(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowRowOverflowScope,kotlin.Unit> content);
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.FlowRowOverflow expandOrCollapseIndicator(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowRowOverflowScope,kotlin.Unit> expandIndicator, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.FlowRowOverflowScope,kotlin.Unit> collapseIndicator, optional int minRowsToShowCollapse, optional float minHeightToShowCollapse);
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.FlowRowOverflow getClip();
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public androidx.compose.foundation.layout.FlowRowOverflow getVisible();
+ property @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final androidx.compose.foundation.layout.FlowRowOverflow Clip;
+ property @Deprecated @SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi public final androidx.compose.foundation.layout.FlowRowOverflow Visible;
}
@SuppressCompatibility @androidx.compose.foundation.layout.ExperimentalLayoutApi @androidx.compose.foundation.layout.LayoutScopeMarker @androidx.compose.runtime.Stable public interface FlowRowOverflowScope extends androidx.compose.foundation.layout.FlowRowScope {
diff --git a/compose/foundation/foundation-layout/proguard-rules.pro b/compose/foundation/foundation-layout/proguard-rules.pro
index 67d118b..72f4a6c 100644
--- a/compose/foundation/foundation-layout/proguard-rules.pro
+++ b/compose/foundation/foundation-layout/proguard-rules.pro
@@ -15,7 +15,7 @@
# Keep all the functions created to throw an exception. We don't want these functions to be
# inlined in any way, which R8 will do by default. The whole point of these functions is to
# reduce the amount of code generated at the call site.
--keep,allowshrinking,allowobfuscation class androidx.compose.**.* {
+-keepclassmembers,allowshrinking,allowobfuscation class androidx.compose.**.* {
static void throw*Exception(...);
static void throw*ExceptionForNullCheck(...);
# For methods returning Nothing
diff --git a/compose/foundation/foundation-layout/samples/src/main/java/androidx/compose/foundation/layout/samples/ContextualFlowColumnSample.kt b/compose/foundation/foundation-layout/samples/src/main/java/androidx/compose/foundation/layout/samples/ContextualFlowColumnSample.kt
index 6a7c208..96dae13 100644
--- a/compose/foundation/foundation-layout/samples/src/main/java/androidx/compose/foundation/layout/samples/ContextualFlowColumnSample.kt
+++ b/compose/foundation/foundation-layout/samples/src/main/java/androidx/compose/foundation/layout/samples/ContextualFlowColumnSample.kt
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+@file:Suppress("DEPRECATION")
+
package androidx.compose.foundation.layout.samples
import androidx.annotation.Sampled
diff --git a/compose/foundation/foundation-layout/samples/src/main/java/androidx/compose/foundation/layout/samples/ContextualFlowRowSample.kt b/compose/foundation/foundation-layout/samples/src/main/java/androidx/compose/foundation/layout/samples/ContextualFlowRowSample.kt
index f0eda4d..d10e927 100644
--- a/compose/foundation/foundation-layout/samples/src/main/java/androidx/compose/foundation/layout/samples/ContextualFlowRowSample.kt
+++ b/compose/foundation/foundation-layout/samples/src/main/java/androidx/compose/foundation/layout/samples/ContextualFlowRowSample.kt
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+@file:Suppress("DEPRECATION")
+
package androidx.compose.foundation.layout.samples
import androidx.annotation.Sampled
diff --git a/compose/foundation/foundation-layout/samples/src/main/java/androidx/compose/foundation/layout/samples/FlowColumnSample.kt b/compose/foundation/foundation-layout/samples/src/main/java/androidx/compose/foundation/layout/samples/FlowColumnSample.kt
index ca95c71..5b45c21 100644
--- a/compose/foundation/foundation-layout/samples/src/main/java/androidx/compose/foundation/layout/samples/FlowColumnSample.kt
+++ b/compose/foundation/foundation-layout/samples/src/main/java/androidx/compose/foundation/layout/samples/FlowColumnSample.kt
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+@file:Suppress("DEPRECATION")
+
package androidx.compose.foundation.layout.samples
import androidx.annotation.Sampled
diff --git a/compose/foundation/foundation-layout/samples/src/main/java/androidx/compose/foundation/layout/samples/FlowRowSample.kt b/compose/foundation/foundation-layout/samples/src/main/java/androidx/compose/foundation/layout/samples/FlowRowSample.kt
index 15f5304..cd41109 100644
--- a/compose/foundation/foundation-layout/samples/src/main/java/androidx/compose/foundation/layout/samples/FlowRowSample.kt
+++ b/compose/foundation/foundation-layout/samples/src/main/java/androidx/compose/foundation/layout/samples/FlowRowSample.kt
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+@file:Suppress("DEPRECATION")
+
package androidx.compose.foundation.layout.samples
import androidx.annotation.Sampled
diff --git a/compose/foundation/foundation-layout/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/layout/ContextualFlowRowColumnTest.kt b/compose/foundation/foundation-layout/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/layout/ContextualFlowRowColumnTest.kt
index 086f752..1ee8cd1 100644
--- a/compose/foundation/foundation-layout/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/layout/ContextualFlowRowColumnTest.kt
+++ b/compose/foundation/foundation-layout/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/layout/ContextualFlowRowColumnTest.kt
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+@file:Suppress("DEPRECATION")
+
package androidx.compose.foundation.layout
import androidx.compose.foundation.background
diff --git a/compose/foundation/foundation-layout/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/layout/FlowRowColumnTest.kt b/compose/foundation/foundation-layout/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/layout/FlowRowColumnTest.kt
index 9c2a063..f7038c5 100644
--- a/compose/foundation/foundation-layout/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/layout/FlowRowColumnTest.kt
+++ b/compose/foundation/foundation-layout/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/layout/FlowRowColumnTest.kt
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+@file:Suppress("DEPRECATION")
+
package androidx.compose.foundation.layout
import androidx.compose.foundation.background
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/AlignmentLine.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/AlignmentLine.kt
index fb9ac8c05..99d2381 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/AlignmentLine.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/AlignmentLine.kt
@@ -33,6 +33,7 @@
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.TextUnit
+import androidx.compose.ui.unit.isSpecified
import androidx.compose.ui.unit.isUnspecified
import kotlin.math.max
@@ -142,14 +143,14 @@
@Stable
fun Modifier.paddingFromBaseline(top: Dp = Dp.Unspecified, bottom: Dp = Dp.Unspecified) =
this.then(
- if (top != Dp.Unspecified) {
+ if (top.isSpecified) {
Modifier.paddingFrom(FirstBaseline, before = top)
} else {
Modifier
}
)
.then(
- if (bottom != Dp.Unspecified) {
+ if (bottom.isSpecified) {
Modifier.paddingFrom(LastBaseline, after = bottom)
} else {
Modifier
@@ -192,8 +193,8 @@
) : ModifierNodeElement<AlignmentLineOffsetDpNode>() {
init {
requirePrecondition(
- (before.value >= 0f || before == Dp.Unspecified) &&
- (after.value >= 0f || after == Dp.Unspecified)
+ (before.value >= 0f || before.isUnspecified) and
+ (after.value >= 0f || after.isUnspecified)
) {
"Padding from alignment line must be a non-negative number"
}
@@ -319,12 +320,12 @@
val axisMax = if (alignmentLine.horizontal) constraints.maxHeight else constraints.maxWidth
// Compute padding required to satisfy the total before and after offsets.
val paddingBefore =
- ((if (before != Dp.Unspecified) before.roundToPx() else 0) - linePosition).coerceIn(
+ ((if (before.isSpecified) before.roundToPx() else 0) - linePosition).coerceIn(
0,
axisMax - axis
)
val paddingAfter =
- ((if (after != Dp.Unspecified) after.roundToPx() else 0) - axis + linePosition).coerceIn(
+ ((if (after.isSpecified) after.roundToPx() else 0) - axis + linePosition).coerceIn(
0,
axisMax - axis - paddingBefore
)
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/ContextualFlowLayout.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/ContextualFlowLayout.kt
index a22b962..885285d 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/ContextualFlowLayout.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/ContextualFlowLayout.kt
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+@file:Suppress("DEPRECATION")
+
package androidx.compose.foundation.layout
import androidx.annotation.FloatRange
@@ -62,6 +64,7 @@
* @see FlowRow
* @see ContextualFlowColumn
*/
+@Deprecated("ContextualFlowLayouts are no longer maintained")
@Composable
@ExperimentalLayoutApi
fun ContextualFlowRow(
@@ -136,6 +139,7 @@
* @see FlowColumn
* @see ContextualFlowRow
*/
+@Deprecated("ContextualFlowLayouts are no longer maintained")
@Composable
@ExperimentalLayoutApi
fun ContextualFlowColumn(
@@ -181,6 +185,7 @@
}
/** Defines the scope for items within a [ContextualFlowRow]. */
+@Deprecated("ContextualFlowLayouts are no longer maintained")
@LayoutScopeMarker
@Stable
@ExperimentalLayoutApi
@@ -248,18 +253,21 @@
}
/** Scope for the overflow [ContextualFlowRow]. */
+@Deprecated("ContextualFlowLayouts are no longer maintained")
@LayoutScopeMarker
@Stable
@ExperimentalLayoutApi
interface ContextualFlowRowOverflowScope : FlowRowOverflowScope
/** Scope for the overflow [ContextualFlowColumn]. */
+@Deprecated("ContextualFlowLayouts are no longer maintained")
@LayoutScopeMarker
@Stable
@ExperimentalLayoutApi
interface ContextualFlowColumnOverflowScope : FlowColumnOverflowScope
/** Provides a scope for items within a [ContextualFlowColumn]. */
+@Deprecated("ContextualFlowLayouts are no longer maintained")
@LayoutScopeMarker
@Stable
@ExperimentalLayoutApi
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/FlowLayout.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/FlowLayout.kt
index 8417dd1..0b50860 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/FlowLayout.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/FlowLayout.kt
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+@file:Suppress("DEPRECATION")
+
package androidx.compose.foundation.layout
import androidx.annotation.FloatRange
@@ -80,9 +82,9 @@
* @param overflow The strategy to handle overflowing items
* @param content The content as a [RowScope]
* @see FlowColumn
- * @see ContextualFlowRow
* @see [androidx.compose.foundation.layout.Row]
*/
+@Deprecated("The overflow parameter has been deprecated")
@Composable
@ExperimentalLayoutApi
fun FlowRow(
@@ -117,6 +119,58 @@
}
/**
+ * [FlowRow] is a layout that fills items from left to right (ltr) in LTR layouts or right to left
+ * (rtl) in RTL layouts and when it runs out of space, moves to the next "row" or "line" positioned
+ * on the bottom, and then continues filling items until the items run out.
+ *
+ * Example:
+ *
+ * @sample androidx.compose.foundation.layout.samples.SimpleFlowRow
+ *
+ * When a Modifier [RowScope.weight] is provided, it scales the item based on the number items that
+ * fall on the row it was placed in.
+ *
+ * Note that if two or more Text components are placed in a [Row], normally they should be aligned
+ * by their first baselines. [FlowRow] as a general purpose container does not do it automatically
+ * so developers need to handle this manually. This is achieved by adding a
+ * [RowScope.alignByBaseline] modifier to every such Text component. By default this modifier aligns
+ * by [androidx.compose.ui.layout.FirstBaseline]. If, however, you need to align Texts by
+ * [androidx.compose.ui.layout.LastBaseline] for example, use a more general [RowScope.alignBy]
+ * modifier.
+ *
+ * @param modifier The modifier to be applied to the Row.
+ * @param horizontalArrangement The horizontal arrangement of the layout's children.
+ * @param verticalArrangement The vertical arrangement of the layout's virtual rows.
+ * @param itemVerticalAlignment The cross axis/vertical alignment of an item in the column.
+ * @param maxItemsInEachRow The maximum number of items per row
+ * @param maxLines The max number of rows
+ * @param content The content as a [RowScope]
+ * @see FlowColumn
+ * @see [androidx.compose.foundation.layout.Row]
+ */
+@OptIn(ExperimentalLayoutApi::class)
+@Composable
+fun FlowRow(
+ modifier: Modifier = Modifier,
+ horizontalArrangement: Arrangement.Horizontal = Arrangement.Start,
+ verticalArrangement: Arrangement.Vertical = Arrangement.Top,
+ itemVerticalAlignment: Alignment.Vertical = Alignment.Top,
+ maxItemsInEachRow: Int = Int.MAX_VALUE,
+ maxLines: Int = Int.MAX_VALUE,
+ content: @Composable FlowRowScope.() -> Unit
+) =
+ FlowRow(
+ modifier,
+ horizontalArrangement,
+ verticalArrangement,
+ itemVerticalAlignment,
+ maxItemsInEachRow,
+ maxLines,
+ FlowRowOverflow.Clip,
+ content,
+ )
+
+/**
* [FlowColumn] is a layout that fills items from top to bottom, and when it runs out of space on
* the bottom, moves to the next "column" or "line" on the right or left based on ltr or rtl
* layouts, and then continues filling items from top to bottom.
@@ -144,6 +198,7 @@
* @see ContextualFlowColumn
* @see [androidx.compose.foundation.layout.Column]
*/
+@Deprecated("The overflow parameter has been deprecated")
@Composable
@ExperimentalLayoutApi
fun FlowColumn(
@@ -176,6 +231,54 @@
Layout(contents = list, measurePolicy = measurePolicy, modifier = modifier)
}
+/**
+ * [FlowColumn] is a layout that fills items from top to bottom, and when it runs out of space on
+ * the bottom, moves to the next "column" or "line" on the right or left based on ltr or rtl
+ * layouts, and then continues filling items from top to bottom.
+ *
+ * It supports ltr in LTR layouts, by placing the first column to the left, and then moving to the
+ * right It supports rtl in RTL layouts, by placing the first column to the right, and then moving
+ * to the left
+ *
+ * Example:
+ *
+ * @sample androidx.compose.foundation.layout.samples.SimpleFlowColumn
+ *
+ * When a Modifier [ColumnScope.weight] is provided, it scales the item based on the number items
+ * that fall on the column it was placed in.
+ *
+ * @param modifier The modifier to be applied to the Row.
+ * @param verticalArrangement The vertical arrangement of the layout's children.
+ * @param horizontalArrangement The horizontal arrangement of the layout's virtual columns
+ * @param itemHorizontalAlignment The cross axis/horizontal alignment of an item in the column.
+ * @param maxItemsInEachColumn The maximum number of items per column
+ * @param maxLines The max number of rows
+ * @param content The content as a [ColumnScope]
+ * @see FlowRow
+ * @see [androidx.compose.foundation.layout.Column]
+ */
+@Composable
+@ExperimentalLayoutApi
+fun FlowColumn(
+ modifier: Modifier = Modifier,
+ verticalArrangement: Arrangement.Vertical = Arrangement.Top,
+ horizontalArrangement: Arrangement.Horizontal = Arrangement.Start,
+ itemHorizontalAlignment: Alignment.Horizontal = Alignment.Start,
+ maxItemsInEachColumn: Int = Int.MAX_VALUE,
+ maxLines: Int = Int.MAX_VALUE,
+ content: @Composable FlowColumnScope.() -> Unit
+) =
+ FlowColumn(
+ modifier,
+ verticalArrangement,
+ horizontalArrangement,
+ itemHorizontalAlignment,
+ maxItemsInEachColumn,
+ maxLines,
+ FlowColumnOverflow.Clip,
+ content,
+ )
+
/** Scope for the children of [FlowRow]. */
@LayoutScopeMarker
@Stable
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/FlowLayoutBuildingBlocks.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/FlowLayoutBuildingBlocks.kt
index 956bd2c..5f056b2 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/FlowLayoutBuildingBlocks.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/FlowLayoutBuildingBlocks.kt
@@ -64,6 +64,7 @@
return ellipsisInfo
}
+ @Suppress("DEPRECATION")
fun getWrapInfo(
nextItemHasNext: Boolean,
nextIndexInLine: Int,
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/FlowLayoutOverflow.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/FlowLayoutOverflow.kt
index 686fcc8..5912c7c 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/FlowLayoutOverflow.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/FlowLayoutOverflow.kt
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+@file:Suppress("DEPRECATION")
+
package androidx.compose.foundation.layout
import androidx.collection.IntIntPair
@@ -54,6 +56,7 @@
* - [expandOrCollapseIndicator]: Extends the [expandIndicator] functionality by adding a 'Collapse'
* option. After expanding the content, users can choose to collapse it back to the summary view.
*/
+@Deprecated("FlowLayout overflow is no longer maintained")
@ExperimentalLayoutApi
class FlowRowOverflow
private constructor(
@@ -188,6 +191,7 @@
* - [expandOrCollapseIndicator]: Extends the [expandIndicator] functionality by adding a 'Collapse'
* option. After expanding the content, users can choose to collapse it back to the summary view.
*/
+@Deprecated("FlowLayout overflow is no longer maintained")
@ExperimentalLayoutApi
class FlowColumnOverflow
private constructor(
@@ -204,6 +208,8 @@
seeMoreGetter,
collapseGetter
) {
+ @Deprecated("FlowLayout overflow is no longer maintained")
+ @ExperimentalLayoutApi
companion object {
/** Display all content, even if there is not enough space in the specified bounds. */
@Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
@@ -323,6 +329,7 @@
* - [expandOrCollapseIndicator]: Extends the [expandIndicator] functionality by adding a 'Collapse'
* option. After expanding the content, users can choose to collapse it back to the summary view.
*/
+@Deprecated("ContextualFlowLayouts are no longer maintained")
@ExperimentalLayoutApi
class ContextualFlowRowOverflow
private constructor(
@@ -340,6 +347,8 @@
collapseGetter
) {
+ @Deprecated("FlowLayout overflow is no longer maintained")
+ @ExperimentalLayoutApi
companion object {
/** Display all content, even if there is not enough space in the specified bounds. */
@Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
@@ -462,6 +471,7 @@
* - [expandOrCollapseIndicator]: Extends the [expandIndicator] functionality by adding a 'Collapse'
* option. After expanding the content, users can choose to collapse it back to the summary view.
*/
+@Deprecated("ContextualFlowLayouts are no longer maintained")
@ExperimentalLayoutApi
class ContextualFlowColumnOverflow
private constructor(
@@ -479,6 +489,8 @@
collapseGetter
) {
+ @Deprecated("ContextualFlowLayouts are no longer maintained")
+ @ExperimentalLayoutApi
companion object {
/** Display all content, even if there is not enough space in the specified bounds. */
@Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
@@ -596,6 +608,7 @@
* @see [ContextualFlowRowOverflow]
* @see [ContextualFlowColumnOverflow]
*/
+@Deprecated("FlowLayout overflow is no longer maintained")
@ExperimentalLayoutApi
sealed class FlowLayoutOverflow(
internal val type: OverflowType,
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Padding.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Padding.kt
index 24ac686..c038279 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Padding.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Padding.kt
@@ -33,6 +33,7 @@
import androidx.compose.ui.unit.constrainHeight
import androidx.compose.ui.unit.constrainWidth
import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.isUnspecified
import androidx.compose.ui.unit.offset
/**
@@ -203,10 +204,14 @@
) : PaddingValues {
init {
- requirePrecondition(left.value >= 0) { "Left padding must be non-negative" }
- requirePrecondition(top.value >= 0) { "Top padding must be non-negative" }
- requirePrecondition(right.value >= 0) { "Right padding must be non-negative" }
- requirePrecondition(bottom.value >= 0) { "Bottom padding must be non-negative" }
+ requirePrecondition(
+ (left.value >= 0f) and
+ (top.value >= 0f) and
+ (right.value >= 0f) and
+ (bottom.value >= 0f)
+ ) {
+ "Padding must be non-negative"
+ }
}
override fun calculateLeftPadding(layoutDirection: LayoutDirection) = left
@@ -291,10 +296,11 @@
) : PaddingValues {
init {
- requirePrecondition(start.value >= 0) { "Start padding must be non-negative" }
- requirePrecondition(top.value >= 0) { "Top padding must be non-negative" }
- requirePrecondition(end.value >= 0) { "End padding must be non-negative" }
- requirePrecondition(bottom.value >= 0) { "Bottom padding must be non-negative" }
+ requirePrecondition(
+ (start.value >= 0f) and (top.value >= 0f) and (end.value >= 0f) and (bottom.value >= 0f)
+ ) {
+ "Padding must be non-negative"
+ }
}
override fun calculateLeftPadding(layoutDirection: LayoutDirection) =
@@ -332,10 +338,10 @@
init {
requirePrecondition(
- (start.value >= 0f || start == Dp.Unspecified) &&
- (top.value >= 0f || top == Dp.Unspecified) &&
- (end.value >= 0f || end == Dp.Unspecified) &&
- (bottom.value >= 0f || bottom == Dp.Unspecified)
+ (start.value >= 0f || start.isUnspecified) and
+ (top.value >= 0f || top.isUnspecified) and
+ (end.value >= 0f || end.isUnspecified) and
+ (bottom.value >= 0f || bottom.isUnspecified)
) {
"Padding must be non-negative"
}
@@ -436,30 +442,30 @@
measurable: Measurable,
constraints: Constraints
): MeasureResult {
+ val leftPadding = paddingValues.calculateLeftPadding(layoutDirection)
+ val topPadding = paddingValues.calculateTopPadding()
+ val rightPadding = paddingValues.calculateRightPadding(layoutDirection)
+ val bottomPadding = paddingValues.calculateBottomPadding()
+
requirePrecondition(
- paddingValues.calculateLeftPadding(layoutDirection) >= 0.dp &&
- paddingValues.calculateTopPadding() >= 0.dp &&
- paddingValues.calculateRightPadding(layoutDirection) >= 0.dp &&
- paddingValues.calculateBottomPadding() >= 0.dp
+ (leftPadding >= 0.dp) and
+ (topPadding >= 0.dp) and
+ (rightPadding >= 0.dp) and
+ (bottomPadding >= 0.dp)
) {
"Padding must be non-negative"
}
- val horizontal =
- paddingValues.calculateLeftPadding(layoutDirection).roundToPx() +
- paddingValues.calculateRightPadding(layoutDirection).roundToPx()
- val vertical =
- paddingValues.calculateTopPadding().roundToPx() +
- paddingValues.calculateBottomPadding().roundToPx()
+
+ val roundedLeftPadding = leftPadding.roundToPx()
+ val horizontal = roundedLeftPadding + rightPadding.roundToPx()
+
+ val roundedTopPadding = topPadding.roundToPx()
+ val vertical = roundedTopPadding + bottomPadding.roundToPx()
val placeable = measurable.measure(constraints.offset(-horizontal, -vertical))
val width = constraints.constrainWidth(placeable.width + horizontal)
val height = constraints.constrainHeight(placeable.height + vertical)
- return layout(width, height) {
- placeable.place(
- paddingValues.calculateLeftPadding(layoutDirection).roundToPx(),
- paddingValues.calculateTopPadding().roundToPx()
- )
- }
+ return layout(width, height) { placeable.place(roundedLeftPadding, roundedTopPadding) }
}
}
diff --git a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Size.kt b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Size.kt
index b221c8b..52e00de 100644
--- a/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Size.kt
+++ b/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/Size.kt
@@ -39,6 +39,10 @@
import androidx.compose.ui.unit.constrain
import androidx.compose.ui.unit.constrainHeight
import androidx.compose.ui.unit.constrainWidth
+import androidx.compose.ui.unit.isSpecified
+import androidx.compose.ui.util.fastCoerceAtLeast
+import androidx.compose.ui.util.fastCoerceAtMost
+import androidx.compose.ui.util.fastCoerceIn
import androidx.compose.ui.util.fastRoundToInt
/**
@@ -694,7 +698,7 @@
val width =
(constraints.maxWidth * fraction)
.fastRoundToInt()
- .coerceIn(constraints.minWidth, constraints.maxWidth)
+ .fastCoerceIn(constraints.minWidth, constraints.maxWidth)
minWidth = width
maxWidth = width
} else {
@@ -707,7 +711,7 @@
val height =
(constraints.maxHeight * fraction)
.fastRoundToInt()
- .coerceIn(constraints.minHeight, constraints.maxHeight)
+ .fastCoerceIn(constraints.minHeight, constraints.maxHeight)
minHeight = height
maxHeight = height
} else {
@@ -782,28 +786,28 @@
private val Density.targetConstraints: Constraints
get() {
val maxWidth =
- if (maxWidth != Dp.Unspecified) {
- maxWidth.roundToPx().coerceAtLeast(0)
+ if (maxWidth.isSpecified) {
+ maxWidth.roundToPx().fastCoerceAtLeast(0)
} else {
Constraints.Infinity
}
val maxHeight =
- if (maxHeight != Dp.Unspecified) {
- maxHeight.roundToPx().coerceAtLeast(0)
+ if (maxHeight.isSpecified) {
+ maxHeight.roundToPx().fastCoerceAtLeast(0)
} else {
Constraints.Infinity
}
val minWidth =
- if (minWidth != Dp.Unspecified) {
- minWidth.roundToPx().coerceAtMost(maxWidth).coerceAtLeast(0).let {
+ if (minWidth.isSpecified) {
+ minWidth.roundToPx().fastCoerceIn(0, maxWidth).let {
if (it != Constraints.Infinity) it else 0
}
} else {
0
}
val minHeight =
- if (minHeight != Dp.Unspecified) {
- minHeight.roundToPx().coerceAtMost(maxHeight).coerceAtLeast(0).let {
+ if (minHeight.isSpecified) {
+ minHeight.roundToPx().fastCoerceIn(0, maxHeight).let {
if (it != Constraints.Infinity) it else 0
}
} else {
@@ -827,28 +831,28 @@
constraints.constrain(targetConstraints)
} else {
val resolvedMinWidth =
- if (minWidth != Dp.Unspecified) {
+ if (minWidth.isSpecified) {
targetConstraints.minWidth
} else {
- constraints.minWidth.coerceAtMost(targetConstraints.maxWidth)
+ constraints.minWidth.fastCoerceAtMost(targetConstraints.maxWidth)
}
val resolvedMaxWidth =
- if (maxWidth != Dp.Unspecified) {
+ if (maxWidth.isSpecified) {
targetConstraints.maxWidth
} else {
- constraints.maxWidth.coerceAtLeast(targetConstraints.minWidth)
+ constraints.maxWidth.fastCoerceAtLeast(targetConstraints.minWidth)
}
val resolvedMinHeight =
- if (minHeight != Dp.Unspecified) {
+ if (minHeight.isSpecified) {
targetConstraints.minHeight
} else {
- constraints.minHeight.coerceAtMost(targetConstraints.maxHeight)
+ constraints.minHeight.fastCoerceAtMost(targetConstraints.maxHeight)
}
val resolvedMaxHeight =
- if (maxHeight != Dp.Unspecified) {
+ if (maxHeight.isSpecified) {
targetConstraints.maxHeight
} else {
- constraints.maxHeight.coerceAtLeast(targetConstraints.minHeight)
+ constraints.maxHeight.fastCoerceAtLeast(targetConstraints.minHeight)
}
Constraints(
resolvedMinWidth,
@@ -1072,14 +1076,14 @@
): MeasureResult {
val wrappedConstraints =
Constraints(
- if (minWidth != Dp.Unspecified && constraints.minWidth == 0) {
- minWidth.roundToPx().coerceAtMost(constraints.maxWidth).coerceAtLeast(0)
+ if (minWidth.isSpecified && constraints.minWidth == 0) {
+ minWidth.roundToPx().fastCoerceIn(0, constraints.maxWidth)
} else {
constraints.minWidth
},
constraints.maxWidth,
- if (minHeight != Dp.Unspecified && constraints.minHeight == 0) {
- minHeight.roundToPx().coerceAtMost(constraints.maxHeight).coerceAtLeast(0)
+ if (minHeight.isSpecified && constraints.minHeight == 0) {
+ minHeight.roundToPx().fastCoerceIn(0, constraints.maxHeight)
} else {
constraints.minHeight
},
@@ -1095,7 +1099,7 @@
) =
measurable
.minIntrinsicWidth(height)
- .coerceAtLeast(if (minWidth != Dp.Unspecified) minWidth.roundToPx() else 0)
+ .fastCoerceAtLeast(if (minWidth.isSpecified) minWidth.roundToPx() else 0)
override fun IntrinsicMeasureScope.maxIntrinsicWidth(
measurable: IntrinsicMeasurable,
@@ -1103,7 +1107,7 @@
) =
measurable
.maxIntrinsicWidth(height)
- .coerceAtLeast(if (minWidth != Dp.Unspecified) minWidth.roundToPx() else 0)
+ .fastCoerceAtLeast(if (minWidth.isSpecified) minWidth.roundToPx() else 0)
override fun IntrinsicMeasureScope.minIntrinsicHeight(
measurable: IntrinsicMeasurable,
@@ -1111,7 +1115,7 @@
) =
measurable
.minIntrinsicHeight(width)
- .coerceAtLeast(if (minHeight != Dp.Unspecified) minHeight.roundToPx() else 0)
+ .fastCoerceAtLeast(if (minHeight.isSpecified) minHeight.roundToPx() else 0)
override fun IntrinsicMeasureScope.maxIntrinsicHeight(
measurable: IntrinsicMeasurable,
@@ -1119,7 +1123,7 @@
) =
measurable
.maxIntrinsicHeight(width)
- .coerceAtLeast(if (minHeight != Dp.Unspecified) minHeight.roundToPx() else 0)
+ .fastCoerceAtLeast(if (minHeight.isSpecified) minHeight.roundToPx() else 0)
}
internal enum class Direction {
diff --git a/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridSlotsReuseTest.kt b/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridSlotsReuseTest.kt
index d988a6a..5b743a5a 100644
--- a/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridSlotsReuseTest.kt
+++ b/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/grid/LazyGridSlotsReuseTest.kt
@@ -59,7 +59,7 @@
}
}
- val id0 = rule.onNodeWithTag("0").semanticsId()
+ val id0 = rule.onNodeWithTag("0").fetchSemanticsNode().id
rule.onNodeWithTag("0").assertIsDisplayed()
rule.runOnIdle { runBlocking { state.scrollToItem(1) } }
@@ -78,8 +78,8 @@
}
}
// Semantics IDs must be fetched before scrolling.
- val id0 = rule.onNodeWithTag("0").semanticsId()
- val id1 = rule.onNodeWithTag("1").semanticsId()
+ val id0 = rule.onNodeWithTag("0").fetchSemanticsNode().id
+ val id1 = rule.onNodeWithTag("1").fetchSemanticsNode().id
rule.onNodeWithTag("0").assertIsDisplayed()
rule.onNodeWithTag("1").assertIsDisplayed()
@@ -105,7 +105,7 @@
}
val deactivatedIds = mutableListOf<Int>()
repeat(DefaultMaxItemsToRetain) {
- deactivatedIds.add(rule.onNodeWithTag("$it").semanticsId())
+ deactivatedIds.add(rule.onNodeWithTag("$it").fetchSemanticsNode().id)
}
rule.runOnIdle { runBlocking { state.scrollToItem(DefaultMaxItemsToRetain + 1) } }
@@ -127,8 +127,8 @@
}
}
- val id0 = rule.onNodeWithTag("0").semanticsId()
- val id1 = rule.onNodeWithTag("1").semanticsId()
+ val id0 = rule.onNodeWithTag("0").fetchSemanticsNode().id
+ val id1 = rule.onNodeWithTag("1").fetchSemanticsNode().id
rule.onNodeWithTag("0").assertIsDisplayed()
rule.onNodeWithTag("1").assertIsDisplayed()
@@ -172,7 +172,7 @@
}
// 3 should be visible at this point, so save its ID to check later
- val id3 = rule.onNodeWithTag("3").semanticsId()
+ val id3 = rule.onNodeWithTag("3").fetchSemanticsNode().id
rule.runOnIdle {
runBlocking {
@@ -204,8 +204,8 @@
}
}
- val id10 = rule.onNodeWithTag("10").semanticsId()
- val id11 = rule.onNodeWithTag("11").semanticsId()
+ val id10 = rule.onNodeWithTag("10").fetchSemanticsNode().id
+ val id11 = rule.onNodeWithTag("11").fetchSemanticsNode().id
rule.runOnIdle {
runBlocking {
@@ -238,7 +238,7 @@
}
}
// 8 should be visible at this point, so save its ID to check later
- val id8 = rule.onNodeWithTag("8").semanticsId()
+ val id8 = rule.onNodeWithTag("8").fetchSemanticsNode().id
rule.runOnIdle {
runBlocking {
state.scrollToItem(6) // 9 reused, buffer is [8]
@@ -294,8 +294,8 @@
}
// 2 and 3 should be visible at this point, so save its ID to check later
- val id2 = rule.onNodeWithTag("2").semanticsId()
- val id3 = rule.onNodeWithTag("3").semanticsId()
+ val id2 = rule.onNodeWithTag("2").fetchSemanticsNode().id
+ val id3 = rule.onNodeWithTag("3").fetchSemanticsNode().id
rule.runOnIdle {
runBlocking {
@@ -341,7 +341,7 @@
val deactivatedIds = mutableListOf<Int>()
for (i in 0 until visibleItemsCount) {
- deactivatedIds.add(rule.onNodeWithTag("$i").semanticsId())
+ deactivatedIds.add(rule.onNodeWithTag("$i").fetchSemanticsNode().id)
rule.onNodeWithTag("$i").assertIsDisplayed()
}
for (i in startOfType1 until startOfType1 + DefaultMaxItemsToRetain) {
@@ -382,8 +382,8 @@
}
}
- val id0 = rule.onNodeWithTag("0").semanticsId()
- val id1 = rule.onNodeWithTag("1").semanticsId()
+ val id0 = rule.onNodeWithTag("0").fetchSemanticsNode().id
+ val id1 = rule.onNodeWithTag("1").fetchSemanticsNode().id
rule.runOnIdle {
runBlocking {
@@ -418,4 +418,4 @@
}
}
-private val DefaultMaxItemsToRetain = 7
+private const val DefaultMaxItemsToRetain = 7
diff --git a/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListSlotsReuseTest.kt b/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListSlotsReuseTest.kt
index 0ab752c..85af651 100644
--- a/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListSlotsReuseTest.kt
+++ b/compose/foundation/foundation/integration-tests/lazy-tests/src/androidTest/kotlin/androidx/compose/foundation/lazy/list/LazyListSlotsReuseTest.kt
@@ -65,7 +65,7 @@
}
}
- val id0 = rule.onNodeWithTag("0").semanticsId()
+ val id0 = rule.onNodeWithTag("0").fetchSemanticsNode().id
rule.onNodeWithTag("0").assertIsDisplayed()
rule.runOnIdle { runBlocking { state.scrollToItem(1) } }
@@ -86,8 +86,8 @@
}
}
- val id0 = rule.onNodeWithTag("0").semanticsId()
- val id1 = rule.onNodeWithTag("1").semanticsId()
+ val id0 = rule.onNodeWithTag("0").fetchSemanticsNode().id
+ val id1 = rule.onNodeWithTag("1").fetchSemanticsNode().id
rule.onNodeWithTag("0").assertIsDisplayed()
rule.onNodeWithTag("1").assertIsDisplayed()
@@ -112,7 +112,7 @@
// Semantics IDs must be fetched before scrolling.
val deactivatedIds = mutableListOf<Int>()
repeat(DefaultMaxItemsToRetain) {
- deactivatedIds.add(rule.onNodeWithTag("$it").semanticsId())
+ deactivatedIds.add(rule.onNodeWithTag("$it").fetchSemanticsNode().id)
}
rule.runOnIdle { runBlocking { state.scrollToItem(DefaultMaxItemsToRetain + 1) } }
@@ -136,8 +136,8 @@
}
}
- val id0 = rule.onNodeWithTag("0").semanticsId()
- val id1 = rule.onNodeWithTag("1").semanticsId()
+ val id0 = rule.onNodeWithTag("0").fetchSemanticsNode().id
+ val id1 = rule.onNodeWithTag("1").fetchSemanticsNode().id
rule.onNodeWithTag("0").assertIsDisplayed()
rule.onNodeWithTag("1").assertIsDisplayed()
@@ -183,7 +183,7 @@
}
// 3 should be visible at this point, so save its ID to check later
- val id3 = rule.onNodeWithTag("3").semanticsId()
+ val id3 = rule.onNodeWithTag("3").fetchSemanticsNode().id
rule.runOnIdle {
runBlocking {
@@ -217,8 +217,8 @@
}
}
- val id10 = rule.onNodeWithTag("10").semanticsId()
- val id11 = rule.onNodeWithTag("11").semanticsId()
+ val id10 = rule.onNodeWithTag("10").fetchSemanticsNode().id
+ val id11 = rule.onNodeWithTag("11").fetchSemanticsNode().id
rule.runOnIdle {
runBlocking {
@@ -253,7 +253,7 @@
}
}
// 8 should be visible at this point, so save its ID to check later
- val id8 = rule.onNodeWithTag("8").semanticsId()
+ val id8 = rule.onNodeWithTag("8").fetchSemanticsNode().id
rule.runOnIdle {
runBlocking {
state.scrollToItem(6) // 9 reused, buffer is [8]
@@ -314,8 +314,8 @@
}
// 2 and 3 should be visible at this point, so save its ID to check later
- val id2 = rule.onNodeWithTag("2").semanticsId()
- val id3 = rule.onNodeWithTag("3").semanticsId()
+ val id2 = rule.onNodeWithTag("2").fetchSemanticsNode().id
+ val id3 = rule.onNodeWithTag("3").fetchSemanticsNode().id
rule.runOnIdle {
runBlocking {
@@ -357,7 +357,7 @@
val deactivatedIds = mutableListOf<Int>()
for (i in 0 until visibleItemsCount) {
- deactivatedIds.add(rule.onNodeWithTag("$i").semanticsId())
+ deactivatedIds.add(rule.onNodeWithTag("$i").fetchSemanticsNode().id)
rule.onNodeWithTag("$i").assertIsDisplayed()
}
for (i in startOfType1 until startOfType1 + DefaultMaxItemsToRetain) {
@@ -398,8 +398,8 @@
}
}
- val id0 = rule.onNodeWithTag("0").semanticsId()
- val id1 = rule.onNodeWithTag("1").semanticsId()
+ val id0 = rule.onNodeWithTag("0").fetchSemanticsNode().id
+ val id1 = rule.onNodeWithTag("1").fetchSemanticsNode().id
rule.runOnIdle {
runBlocking {
@@ -434,4 +434,4 @@
}
}
-private val DefaultMaxItemsToRetain = 7
+private const val DefaultMaxItemsToRetain = 7
diff --git a/compose/foundation/foundation/proguard-rules.pro b/compose/foundation/foundation/proguard-rules.pro
index d07663a..d2c7182 100644
--- a/compose/foundation/foundation/proguard-rules.pro
+++ b/compose/foundation/foundation/proguard-rules.pro
@@ -15,8 +15,9 @@
# Keep all the functions created to throw an exception. We don't want these functions to be
# inlined in any way, which R8 will do by default. The whole point of these functions is to
# reduce the amount of code generated at the call site.
--keep,allowshrinking,allowobfuscation class androidx.compose.**.* {
+-keepclassmembers,allowshrinking,allowobfuscation class androidx.compose.**.* {
static void throw*Exception(...);
# For methods returning Nothing
static java.lang.Void throw*Exception(...);
}
+
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/textfield/HardwareKeyboardTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/textfield/HardwareKeyboardTest.kt
index bc90119..9757d4a 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/textfield/HardwareKeyboardTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/textfield/HardwareKeyboardTest.kt
@@ -139,6 +139,14 @@
}
@Test
+ fun textField_newLineNumpad() {
+ keysSequenceTest(initText = "hello") {
+ Key.NumPadEnter.downAndUp()
+ expectedText("\nhello")
+ }
+ }
+
+ @Test
fun textField_backspace() {
keysSequenceTest(initText = "hello") {
Key.DirectionRight.downAndUp()
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/KeyMapping.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/KeyMapping.android.kt
index d22ae99..52f59db 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/KeyMapping.android.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/KeyMapping.android.kt
@@ -66,6 +66,7 @@
actual val MoveEnd: Key = Key(AndroidKeyEvent.KEYCODE_MOVE_END)
actual val Insert: Key = Key(AndroidKeyEvent.KEYCODE_INSERT)
actual val Enter: Key = Key(AndroidKeyEvent.KEYCODE_ENTER)
+ actual val NumPadEnter: Key = Key(AndroidKeyEvent.KEYCODE_NUMPAD_ENTER)
actual val Backspace: Key = Key(AndroidKeyEvent.KEYCODE_DEL)
actual val Delete: Key = Key(AndroidKeyEvent.KEYCODE_FORWARD_DEL)
actual val Paste: Key = Key(AndroidKeyEvent.KEYCODE_PASTE)
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Scroll.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Scroll.kt
index 7a034224..7c73fa8 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Scroll.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Scroll.kt
@@ -443,44 +443,28 @@
measurable: IntrinsicMeasurable,
height: Int
): Int {
- return if (isVertical) {
- measurable.minIntrinsicWidth(Constraints.Infinity)
- } else {
- measurable.minIntrinsicWidth(height)
- }
+ return measurable.minIntrinsicWidth(if (isVertical) Constraints.Infinity else height)
}
override fun IntrinsicMeasureScope.minIntrinsicHeight(
measurable: IntrinsicMeasurable,
width: Int
): Int {
- return if (isVertical) {
- measurable.minIntrinsicHeight(width)
- } else {
- measurable.minIntrinsicHeight(Constraints.Infinity)
- }
+ return measurable.minIntrinsicHeight(if (isVertical) width else Constraints.Infinity)
}
override fun IntrinsicMeasureScope.maxIntrinsicWidth(
measurable: IntrinsicMeasurable,
height: Int
): Int {
- return if (isVertical) {
- measurable.maxIntrinsicWidth(Constraints.Infinity)
- } else {
- measurable.maxIntrinsicWidth(height)
- }
+ return measurable.maxIntrinsicWidth(if (isVertical) Constraints.Infinity else height)
}
override fun IntrinsicMeasureScope.maxIntrinsicHeight(
measurable: IntrinsicMeasurable,
width: Int
): Int {
- return if (isVertical) {
- measurable.maxIntrinsicHeight(width)
- } else {
- measurable.maxIntrinsicHeight(Constraints.Infinity)
- }
+ return measurable.maxIntrinsicHeight(if (isVertical) width else Constraints.Infinity)
}
override fun SemanticsPropertyReceiver.applySemantics() {
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/AnchoredDraggable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/AnchoredDraggable.kt
index dd3ef0b..bdbe702 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/AnchoredDraggable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/AnchoredDraggable.kt
@@ -1650,7 +1650,7 @@
}
}
-internal expect inline fun assertOnJvm(statement: Boolean, message: () -> String): Unit
+internal expect inline fun assertOnJvm(statement: Boolean, message: () -> String)
internal val AnchoredDraggableMinFlingVelocity = 125.dp
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/internal/InlineClassHelper.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/internal/InlineClassHelper.kt
index 4af862a..686d885 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/internal/InlineClassHelper.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/internal/InlineClassHelper.kt
@@ -53,7 +53,7 @@
}
}
-@Suppress("NOTHING_TO_INLINE", "BanInlineOptIn")
+@Suppress("NOTHING_TO_INLINE", "BanInlineOptIn", "KotlinRedundantDiagnosticSuppress")
@OptIn(ExperimentalContracts::class)
internal inline fun checkPrecondition(value: Boolean) {
contract { returns() implies value }
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/KeyMapping.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/KeyMapping.kt
index 153b5da..2764e2e 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/KeyMapping.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/KeyMapping.kt
@@ -51,6 +51,7 @@
val MoveEnd: Key
val Insert: Key
val Enter: Key
+ val NumPadEnter: Key
val Backspace: Key
val Delete: Key
val Paste: Key
@@ -104,7 +105,8 @@
MappedKeys.PageDown -> KeyCommand.PAGE_DOWN
MappedKeys.MoveHome -> KeyCommand.LINE_START
MappedKeys.MoveEnd -> KeyCommand.LINE_END
- MappedKeys.Enter -> KeyCommand.NEW_LINE
+ MappedKeys.Enter,
+ MappedKeys.NumPadEnter -> KeyCommand.NEW_LINE
MappedKeys.Backspace -> KeyCommand.DELETE_PREV_CHAR
MappedKeys.Delete -> KeyCommand.DELETE_NEXT_CHAR
MappedKeys.Paste -> KeyCommand.PASTE
@@ -146,8 +148,8 @@
}
event.isShiftPressed ->
when (event.key) {
- MappedKeys.MoveHome -> KeyCommand.SELECT_LINE_LEFT
- MappedKeys.MoveEnd -> KeyCommand.SELECT_LINE_RIGHT
+ MappedKeys.MoveHome -> KeyCommand.SELECT_LINE_START
+ MappedKeys.MoveEnd -> KeyCommand.SELECT_LINE_END
else -> null
}
event.isAltPressed ->
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/OffsetMappingCalculator.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/OffsetMappingCalculator.kt
index b6fb607..eaa2856 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/OffsetMappingCalculator.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/OffsetMappingCalculator.kt
@@ -370,18 +370,18 @@
*/
@kotlin.jvm.JvmInline
private value class OpArray private constructor(private val values: IntArray) {
- constructor(size: Int) : this(IntArray(size * ElementSize))
+ constructor(size: Int) : this(IntArray(size * OpArrayElementSize))
val size: Int
- get() = values.size / ElementSize
+ get() = values.size / OpArrayElementSize
fun set(index: Int, offset: Int, srcLen: Int, destLen: Int) {
- values[index * ElementSize] = offset
- values[index * ElementSize + 1] = srcLen
- values[index * ElementSize + 2] = destLen
+ values[index * OpArrayElementSize] = offset
+ values[index * OpArrayElementSize + 1] = srcLen
+ values[index * OpArrayElementSize + 2] = destLen
}
- fun copyOf(newSize: Int) = OpArray(values.copyOf(newSize * ElementSize))
+ fun copyOf(newSize: Int) = OpArray(values.copyOf(newSize * OpArrayElementSize))
/**
* Loops through the array between 0 and [max] (exclusive). If [reversed] is false (the
@@ -399,22 +399,20 @@
// duplication here keeps the more complicated logic at the callsite more readable.
if (reversed) {
for (i in max - 1 downTo 0) {
- val offset = values[i * ElementSize]
- val srcLen = values[i * ElementSize + 1]
- val destLen = values[i * ElementSize + 2]
+ val offset = values[i * OpArrayElementSize]
+ val srcLen = values[i * OpArrayElementSize + 1]
+ val destLen = values[i * OpArrayElementSize + 2]
block(offset, srcLen, destLen)
}
} else {
for (i in 0 until max) {
- val offset = values[i * ElementSize]
- val srcLen = values[i * ElementSize + 1]
- val destLen = values[i * ElementSize + 2]
+ val offset = values[i * OpArrayElementSize]
+ val srcLen = values[i * OpArrayElementSize + 1]
+ val destLen = values[i * OpArrayElementSize + 2]
block(offset, srcLen, destLen)
}
}
}
-
- private companion object {
- const val ElementSize = 3
- }
}
+
+private const val OpArrayElementSize = 3
diff --git a/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/KeyMapping.commonStubs.kt b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/KeyMapping.commonStubs.kt
index e6450f8..759faf0 100644
--- a/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/KeyMapping.commonStubs.kt
+++ b/compose/foundation/foundation/src/commonStubsMain/kotlin/androidx/compose/foundation/text/KeyMapping.commonStubs.kt
@@ -40,6 +40,7 @@
actual val MoveEnd: Key = implementedInJetBrainsFork()
actual val Insert: Key = implementedInJetBrainsFork()
actual val Enter: Key = implementedInJetBrainsFork()
+ actual val NumPadEnter: Key = implementedInJetBrainsFork()
actual val Backspace: Key = implementedInJetBrainsFork()
actual val Delete: Key = implementedInJetBrainsFork()
actual val Paste: Key = implementedInJetBrainsFork()
diff --git a/compose/material3/adaptive/adaptive-layout/api/1.1.0-beta01.txt b/compose/material3/adaptive/adaptive-layout/api/1.1.0-beta01.txt
new file mode 100644
index 0000000..b4cd5c9
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/api/1.1.0-beta01.txt
@@ -0,0 +1,469 @@
+// Signature format: 4.0
+package androidx.compose.material3.adaptive.layout {
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Stable public sealed interface AdaptStrategy {
+ method public String adapt();
+ field public static final androidx.compose.material3.adaptive.layout.AdaptStrategy.Companion Companion;
+ }
+
+ public static final class AdaptStrategy.Companion {
+ method public androidx.compose.material3.adaptive.layout.AdaptStrategy getHide();
+ property public final androidx.compose.material3.adaptive.layout.AdaptStrategy Hide;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveComponentOverrideApi public interface AnimatedPaneOverride {
+ method @androidx.compose.runtime.Composable public <S, T extends androidx.compose.material3.adaptive.layout.PaneScaffoldValue<S>> void AnimatedPane(androidx.compose.material3.adaptive.layout.AnimatedPaneOverrideContext<S,T>);
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveComponentOverrideApi public final class AnimatedPaneOverrideContext<S, T extends androidx.compose.material3.adaptive.layout.PaneScaffoldValue<S>> {
+ method public androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntRect> getBoundsAnimationSpec();
+ method public kotlin.jvm.functions.Function1<androidx.compose.material3.adaptive.layout.AnimatedPaneScope,kotlin.Unit> getContent();
+ method public androidx.compose.animation.EnterTransition getEnterTransition();
+ method public androidx.compose.animation.ExitTransition getExitTransition();
+ method public androidx.compose.ui.Modifier getModifier();
+ method public androidx.compose.material3.adaptive.layout.ExtendedPaneScaffoldPaneScope<S,T> getScope();
+ property public final androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntRect> boundsAnimationSpec;
+ property public final kotlin.jvm.functions.Function1<androidx.compose.material3.adaptive.layout.AnimatedPaneScope,kotlin.Unit> content;
+ property public final androidx.compose.animation.EnterTransition enterTransition;
+ property public final androidx.compose.animation.ExitTransition exitTransition;
+ property public final androidx.compose.ui.Modifier modifier;
+ property public final androidx.compose.material3.adaptive.layout.ExtendedPaneScaffoldPaneScope<S,T> scope;
+ }
+
+ public sealed interface AnimatedPaneScope extends androidx.compose.animation.AnimatedVisibilityScope {
+ field public static final androidx.compose.material3.adaptive.layout.AnimatedPaneScope.Companion Companion;
+ }
+
+ public static final class AnimatedPaneScope.Companion {
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public androidx.compose.material3.adaptive.layout.AnimatedPaneScope create(androidx.compose.animation.AnimatedVisibilityScope animatedVisibilityScope);
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public sealed interface ExtendedPaneScaffoldPaneScope<Role, ScaffoldValue extends androidx.compose.material3.adaptive.layout.PaneScaffoldValue<Role>> extends androidx.compose.material3.adaptive.layout.ExtendedPaneScaffoldScope<Role,ScaffoldValue> androidx.compose.material3.adaptive.layout.PaneScaffoldPaneScope<Role> {
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public sealed interface ExtendedPaneScaffoldScope<Role, ScaffoldValue extends androidx.compose.material3.adaptive.layout.PaneScaffoldValue<Role>> extends androidx.compose.material3.adaptive.layout.PaneScaffoldScope androidx.compose.ui.layout.LookaheadScope androidx.compose.material3.adaptive.layout.PaneScaffoldTransitionScope<Role,ScaffoldValue> {
+ }
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class HingePolicy {
+ field public static final androidx.compose.material3.adaptive.layout.HingePolicy.Companion Companion;
+ }
+
+ public static final class HingePolicy.Companion {
+ method public int getAlwaysAvoid();
+ method public int getAvoidOccluding();
+ method public int getAvoidSeparating();
+ method public int getNeverAvoid();
+ property public final int AlwaysAvoid;
+ property public final int AvoidOccluding;
+ property public final int AvoidSeparating;
+ property public final int NeverAvoid;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public final class ListDetailPaneScaffoldDefaults {
+ method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies adaptStrategies(optional androidx.compose.material3.adaptive.layout.AdaptStrategy detailPaneAdaptStrategy, optional androidx.compose.material3.adaptive.layout.AdaptStrategy listPaneAdaptStrategy, optional androidx.compose.material3.adaptive.layout.AdaptStrategy extraPaneAdaptStrategy);
+ field public static final androidx.compose.material3.adaptive.layout.ListDetailPaneScaffoldDefaults INSTANCE;
+ }
+
+ public final class ListDetailPaneScaffoldKt {
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void ListDetailPaneScaffold(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective directive, androidx.compose.material3.adaptive.layout.ThreePaneScaffoldState scaffoldState, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> listPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> detailPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit>? extraPane, optional kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.layout.PaneExpansionState,kotlin.Unit>? paneExpansionDragHandle, optional androidx.compose.material3.adaptive.layout.PaneExpansionState? paneExpansionState);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void ListDetailPaneScaffold(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective directive, androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> listPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> detailPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit>? extraPane, optional kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.layout.PaneExpansionState,kotlin.Unit>? paneExpansionDragHandle, optional androidx.compose.material3.adaptive.layout.PaneExpansionState? paneExpansionState);
+ }
+
+ public final class ListDetailPaneScaffoldRole {
+ method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole getDetail();
+ method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole getExtra();
+ method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole getList();
+ property public final androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole Detail;
+ property public final androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole Extra;
+ property public final androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole List;
+ field public static final androidx.compose.material3.adaptive.layout.ListDetailPaneScaffoldRole INSTANCE;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Stable public final class MutableThreePaneScaffoldState extends androidx.compose.material3.adaptive.layout.ThreePaneScaffoldState {
+ ctor public MutableThreePaneScaffoldState(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue initialScaffoldValue);
+ method public suspend Object? animateTo(optional androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue targetState, optional androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float>? animationSpec, optional boolean isPredictiveBackInProgress, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue getCurrentState();
+ method @FloatRange(from=0.0, to=1.0) public float getProgressFraction();
+ method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue getTargetState();
+ method public boolean isPredictiveBackInProgress();
+ method public suspend Object? seekTo(@FloatRange(from=0.0, to=1.0) float fraction, optional androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue targetState, optional boolean isPredictiveBackInProgress, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public suspend Object? snapTo(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue targetState, optional boolean isPredictiveBackInProgress, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ property public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue currentState;
+ property public boolean isPredictiveBackInProgress;
+ property @FloatRange(from=0.0, to=1.0) public float progressFraction;
+ property public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue targetState;
+ }
+
+ @kotlin.jvm.JvmInline public final value class PaneAdaptedValue {
+ field public static final androidx.compose.material3.adaptive.layout.PaneAdaptedValue.Companion Companion;
+ }
+
+ public static final class PaneAdaptedValue.Companion {
+ method public String getExpanded();
+ method public String getHidden();
+ property public final String Expanded;
+ property public final String Hidden;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public abstract sealed class PaneExpansionAnchor {
+ method @androidx.compose.runtime.Composable public abstract String getDescription();
+ property @androidx.compose.runtime.Composable public abstract String description;
+ }
+
+ public abstract static class PaneExpansionAnchor.Offset extends androidx.compose.material3.adaptive.layout.PaneExpansionAnchor {
+ method public final int getDirection();
+ method public final float getOffset();
+ property public final int direction;
+ property public final float offset;
+ field public static final androidx.compose.material3.adaptive.layout.PaneExpansionAnchor.Offset.Companion Companion;
+ }
+
+ public static final class PaneExpansionAnchor.Offset.Companion {
+ method public androidx.compose.material3.adaptive.layout.PaneExpansionAnchor.Offset fromEnd(float offset);
+ method public androidx.compose.material3.adaptive.layout.PaneExpansionAnchor.Offset fromStart(float offset);
+ }
+
+ @kotlin.jvm.JvmInline public static final value class PaneExpansionAnchor.Offset.Direction {
+ field public static final androidx.compose.material3.adaptive.layout.PaneExpansionAnchor.Offset.Direction.Companion Companion;
+ }
+
+ public static final class PaneExpansionAnchor.Offset.Direction.Companion {
+ method public int getFromEnd();
+ method public int getFromStart();
+ property public final int FromEnd;
+ property public final int FromStart;
+ }
+
+ public static final class PaneExpansionAnchor.Proportion extends androidx.compose.material3.adaptive.layout.PaneExpansionAnchor {
+ ctor public PaneExpansionAnchor.Proportion(@FloatRange(from=0.0, to=1.0) float proportion);
+ method @androidx.compose.runtime.Composable public String getDescription();
+ method public float getProportion();
+ property @androidx.compose.runtime.Composable public String description;
+ property @FloatRange(from=0.0, to=1.0) public final float proportion;
+ }
+
+ public final class PaneExpansionDraggableModifierKt {
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static kotlin.jvm.functions.Function1<androidx.compose.ui.semantics.SemanticsPropertyReceiver,kotlin.Unit> defaultDragHandleSemantics(androidx.compose.material3.adaptive.layout.PaneExpansionState);
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Stable public final class PaneExpansionState {
+ method public suspend Object? animateTo(androidx.compose.material3.adaptive.layout.PaneExpansionAnchor anchor, optional float initialVelocity, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public void clear();
+ method public androidx.compose.material3.adaptive.layout.PaneExpansionAnchor? getCurrentAnchor();
+ method public boolean isUnspecified();
+ method public void setFirstPaneProportion(@FloatRange(from=0.0, to=1.0) float firstPaneProportion);
+ method public void setFirstPaneWidth(int firstPaneWidth);
+ property public final androidx.compose.material3.adaptive.layout.PaneExpansionAnchor? currentAnchor;
+ field public static final androidx.compose.material3.adaptive.layout.PaneExpansionState.Companion Companion;
+ field public static final int Unspecified = -1; // 0xffffffff
+ }
+
+ public static final class PaneExpansionState.Companion {
+ property public static final int Unspecified;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Immutable public sealed interface PaneExpansionStateKey {
+ field public static final androidx.compose.material3.adaptive.layout.PaneExpansionStateKey.Companion Companion;
+ }
+
+ public static final class PaneExpansionStateKey.Companion {
+ method public androidx.compose.material3.adaptive.layout.PaneExpansionStateKey getDefault();
+ property public final androidx.compose.material3.adaptive.layout.PaneExpansionStateKey Default;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Stable public sealed interface PaneExpansionStateKeyProvider {
+ method public androidx.compose.material3.adaptive.layout.PaneExpansionStateKey getPaneExpansionStateKey();
+ property public abstract androidx.compose.material3.adaptive.layout.PaneExpansionStateKey paneExpansionStateKey;
+ }
+
+ public final class PaneExpansionStateKt {
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.layout.PaneExpansionState rememberPaneExpansionState(optional androidx.compose.material3.adaptive.layout.PaneExpansionStateKey key, optional java.util.List<? extends androidx.compose.material3.adaptive.layout.PaneExpansionAnchor> anchors, optional int initialAnchoredIndex, optional androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float> anchoringAnimationSpec, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.layout.PaneExpansionState rememberPaneExpansionState(androidx.compose.material3.adaptive.layout.PaneExpansionStateKeyProvider keyProvider, optional java.util.List<? extends androidx.compose.material3.adaptive.layout.PaneExpansionAnchor> anchors, optional int initialAnchoredIndex, optional androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float> anchoringAnimationSpec, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior);
+ }
+
+ public final class PaneKt {
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static <S, T extends androidx.compose.material3.adaptive.layout.PaneScaffoldValue<S>> void AnimatedPane(androidx.compose.material3.adaptive.layout.ExtendedPaneScaffoldPaneScope<S,T>, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.EnterTransition enterTransition, optional androidx.compose.animation.ExitTransition exitTransition, optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntRect> boundsAnimationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.AnimatedPaneScope,kotlin.Unit> content);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveComponentOverrideApi public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.material3.adaptive.layout.AnimatedPaneOverride> getLocalAnimatedPaneOverride();
+ property @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveComponentOverrideApi public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.material3.adaptive.layout.AnimatedPaneOverride> LocalAnimatedPaneOverride;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Stable public sealed interface PaneMotion {
+ method public int getType();
+ property public abstract int type;
+ field public static final androidx.compose.material3.adaptive.layout.PaneMotion.Companion Companion;
+ }
+
+ public static final class PaneMotion.Companion {
+ method public androidx.compose.material3.adaptive.layout.PaneMotion getAnimateBounds();
+ method public androidx.compose.material3.adaptive.layout.PaneMotion getEnterFromLeft();
+ method public androidx.compose.material3.adaptive.layout.PaneMotion getEnterFromLeftDelayed();
+ method public androidx.compose.material3.adaptive.layout.PaneMotion getEnterFromRight();
+ method public androidx.compose.material3.adaptive.layout.PaneMotion getEnterFromRightDelayed();
+ method public androidx.compose.material3.adaptive.layout.PaneMotion getEnterWithExpand();
+ method public androidx.compose.material3.adaptive.layout.PaneMotion getExitToLeft();
+ method public androidx.compose.material3.adaptive.layout.PaneMotion getExitToRight();
+ method public androidx.compose.material3.adaptive.layout.PaneMotion getExitWithShrink();
+ method public androidx.compose.material3.adaptive.layout.PaneMotion getNoMotion();
+ property public final androidx.compose.material3.adaptive.layout.PaneMotion AnimateBounds;
+ property public final androidx.compose.material3.adaptive.layout.PaneMotion EnterFromLeft;
+ property public final androidx.compose.material3.adaptive.layout.PaneMotion EnterFromLeftDelayed;
+ property public final androidx.compose.material3.adaptive.layout.PaneMotion EnterFromRight;
+ property public final androidx.compose.material3.adaptive.layout.PaneMotion EnterFromRightDelayed;
+ property public final androidx.compose.material3.adaptive.layout.PaneMotion EnterWithExpand;
+ property public final androidx.compose.material3.adaptive.layout.PaneMotion ExitToLeft;
+ property public final androidx.compose.material3.adaptive.layout.PaneMotion ExitToRight;
+ property public final androidx.compose.material3.adaptive.layout.PaneMotion ExitWithShrink;
+ property public final androidx.compose.material3.adaptive.layout.PaneMotion NoMotion;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @kotlin.jvm.JvmInline public static final value class PaneMotion.Type {
+ method public int getValue();
+ property public final int value;
+ field public static final androidx.compose.material3.adaptive.layout.PaneMotion.Type.Companion Companion;
+ }
+
+ public static final class PaneMotion.Type.Companion {
+ method public int getEntering();
+ method public int getExiting();
+ method public int getHidden();
+ method public int getShown();
+ property public final int Entering;
+ property public final int Exiting;
+ property public final int Hidden;
+ property public final int Shown;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public final class PaneMotionData {
+ method public androidx.compose.material3.adaptive.layout.PaneMotion getMotion();
+ method public long getOriginPosition();
+ method public long getOriginSize();
+ method public long getTargetPosition();
+ method public long getTargetSize();
+ property public final androidx.compose.material3.adaptive.layout.PaneMotion motion;
+ property public final long originPosition;
+ property public final long originSize;
+ property public final long targetPosition;
+ property public final long targetSize;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public final class PaneMotionDefaults {
+ method public androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntRect> getAnimationSpec();
+ method public androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntRect> getDelayedAnimationSpec();
+ method public androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> getDelayedOffsetAnimationSpec();
+ method public androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntSize> getDelayedSizeAnimationSpec();
+ method public androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> getOffsetAnimationSpec();
+ method public androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntSize> getSizeAnimationSpec();
+ property public final androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntRect> AnimationSpec;
+ property public final androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntRect> DelayedAnimationSpec;
+ property public final androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> DelayedOffsetAnimationSpec;
+ property public final androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntSize> DelayedSizeAnimationSpec;
+ property public final androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> OffsetAnimationSpec;
+ property public final androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntSize> SizeAnimationSpec;
+ field public static final androidx.compose.material3.adaptive.layout.PaneMotionDefaults INSTANCE;
+ }
+
+ public final class PaneMotionKt {
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public static <Role> androidx.compose.animation.EnterTransition calculateDefaultEnterTransition(androidx.compose.material3.adaptive.layout.PaneScaffoldMotionDataProvider<Role>, Role role);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public static <Role> androidx.compose.animation.ExitTransition calculateDefaultExitTransition(androidx.compose.material3.adaptive.layout.PaneScaffoldMotionDataProvider<Role>, Role role);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public static inline <Role> void forEach(androidx.compose.material3.adaptive.layout.PaneScaffoldMotionDataProvider<Role>, kotlin.jvm.functions.Function2<? super Role,? super androidx.compose.material3.adaptive.layout.PaneMotionData,kotlin.Unit> action);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public static inline <Role> void forEachReversed(androidx.compose.material3.adaptive.layout.PaneScaffoldMotionDataProvider<Role>, kotlin.jvm.functions.Function2<? super Role,? super androidx.compose.material3.adaptive.layout.PaneMotionData,kotlin.Unit> action);
+ }
+
+ @androidx.compose.runtime.Immutable public final class PaneScaffoldDirective {
+ ctor public PaneScaffoldDirective(int maxHorizontalPartitions, float horizontalPartitionSpacerSize, int maxVerticalPartitions, float verticalPartitionSpacerSize, float defaultPanePreferredWidth, java.util.List<androidx.compose.ui.geometry.Rect> excludedBounds);
+ method public androidx.compose.material3.adaptive.layout.PaneScaffoldDirective copy(optional int maxHorizontalPartitions, optional float horizontalPartitionSpacerSize, optional int maxVerticalPartitions, optional float verticalPartitionSpacerSize, optional float defaultPanePreferredWidth, optional java.util.List<androidx.compose.ui.geometry.Rect> excludedBounds);
+ method public float getDefaultPanePreferredWidth();
+ method public java.util.List<androidx.compose.ui.geometry.Rect> getExcludedBounds();
+ method public float getHorizontalPartitionSpacerSize();
+ method public int getMaxHorizontalPartitions();
+ method public int getMaxVerticalPartitions();
+ method public float getVerticalPartitionSpacerSize();
+ property public final float defaultPanePreferredWidth;
+ property public final java.util.List<androidx.compose.ui.geometry.Rect> excludedBounds;
+ property public final float horizontalPartitionSpacerSize;
+ property public final int maxHorizontalPartitions;
+ property public final int maxVerticalPartitions;
+ property public final float verticalPartitionSpacerSize;
+ field public static final androidx.compose.material3.adaptive.layout.PaneScaffoldDirective.Companion Companion;
+ }
+
+ public static final class PaneScaffoldDirective.Companion {
+ method public androidx.compose.material3.adaptive.layout.PaneScaffoldDirective getDefault();
+ property public final androidx.compose.material3.adaptive.layout.PaneScaffoldDirective Default;
+ }
+
+ public final class PaneScaffoldDirectiveKt {
+ method public static androidx.compose.material3.adaptive.layout.PaneScaffoldDirective calculatePaneScaffoldDirective(androidx.compose.material3.adaptive.WindowAdaptiveInfo windowAdaptiveInfo, optional int verticalHingePolicy);
+ method public static androidx.compose.material3.adaptive.layout.PaneScaffoldDirective calculatePaneScaffoldDirectiveWithTwoPanesOnMediumWidth(androidx.compose.material3.adaptive.WindowAdaptiveInfo windowAdaptiveInfo, optional int verticalHingePolicy);
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public sealed interface PaneScaffoldMotionDataProvider<Role> {
+ method public operator androidx.compose.material3.adaptive.layout.PaneMotionData get(int index);
+ method public operator androidx.compose.material3.adaptive.layout.PaneMotionData get(Role role);
+ method public int getCount();
+ method public Role getRoleAt(int index);
+ method public long getScaffoldSize();
+ property public abstract int count;
+ property public abstract long scaffoldSize;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public sealed interface PaneScaffoldPaneScope<Role> {
+ method public androidx.compose.material3.adaptive.layout.PaneMotion getPaneMotion();
+ method public Role getPaneRole();
+ property public abstract androidx.compose.material3.adaptive.layout.PaneMotion paneMotion;
+ property public abstract Role paneRole;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public sealed interface PaneScaffoldParentData {
+ method public float getMinTouchTargetSize();
+ method public float getPreferredWidth();
+ method public boolean isAnimatedPane();
+ property public abstract boolean isAnimatedPane;
+ property public abstract float minTouchTargetSize;
+ property public abstract float preferredWidth;
+ }
+
+ public sealed interface PaneScaffoldScope {
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public androidx.compose.ui.Modifier paneExpansionDraggable(androidx.compose.ui.Modifier, androidx.compose.material3.adaptive.layout.PaneExpansionState state, float minTouchTargetSize, androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.ui.semantics.SemanticsPropertyReceiver,kotlin.Unit> semanticsProperties);
+ method public androidx.compose.ui.Modifier preferredWidth(androidx.compose.ui.Modifier, float width);
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public sealed interface PaneScaffoldTransitionScope<Role, ScaffoldValue extends androidx.compose.material3.adaptive.layout.PaneScaffoldValue<Role>> {
+ method public androidx.compose.material3.adaptive.layout.PaneScaffoldMotionDataProvider<Role> getMotionDataProvider();
+ method @FloatRange(from=0.0, to=1.0) public float getMotionProgress();
+ method public androidx.compose.animation.core.Transition<ScaffoldValue> getScaffoldStateTransition();
+ property public abstract androidx.compose.material3.adaptive.layout.PaneScaffoldMotionDataProvider<Role> motionDataProvider;
+ property @FloatRange(from=0.0, to=1.0) public abstract float motionProgress;
+ property public abstract androidx.compose.animation.core.Transition<ScaffoldValue> scaffoldStateTransition;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public sealed interface PaneScaffoldValue<T> {
+ method public operator String get(T role);
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public final class SupportingPaneScaffoldDefaults {
+ method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies adaptStrategies(optional androidx.compose.material3.adaptive.layout.AdaptStrategy mainPaneAdaptStrategy, optional androidx.compose.material3.adaptive.layout.AdaptStrategy supportingPaneAdaptStrategy, optional androidx.compose.material3.adaptive.layout.AdaptStrategy extraPaneAdaptStrategy);
+ field public static final androidx.compose.material3.adaptive.layout.SupportingPaneScaffoldDefaults INSTANCE;
+ }
+
+ public final class SupportingPaneScaffoldKt {
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void SupportingPaneScaffold(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective directive, androidx.compose.material3.adaptive.layout.ThreePaneScaffoldState scaffoldState, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> mainPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> supportingPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit>? extraPane, optional kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.layout.PaneExpansionState,kotlin.Unit>? paneExpansionDragHandle, optional androidx.compose.material3.adaptive.layout.PaneExpansionState? paneExpansionState);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void SupportingPaneScaffold(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective directive, androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> mainPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> supportingPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit>? extraPane, optional kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.layout.PaneExpansionState,kotlin.Unit>? paneExpansionDragHandle, optional androidx.compose.material3.adaptive.layout.PaneExpansionState? paneExpansionState);
+ }
+
+ public final class SupportingPaneScaffoldRole {
+ method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole getExtra();
+ method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole getMain();
+ method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole getSupporting();
+ property public final androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole Extra;
+ property public final androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole Main;
+ property public final androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole Supporting;
+ field public static final androidx.compose.material3.adaptive.layout.SupportingPaneScaffoldRole INSTANCE;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Immutable public final class ThreePaneMotion {
+ method public operator androidx.compose.material3.adaptive.layout.PaneMotion get(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole role);
+ field public static final androidx.compose.material3.adaptive.layout.ThreePaneMotion.Companion Companion;
+ }
+
+ public static final class ThreePaneMotion.Companion {
+ method public androidx.compose.material3.adaptive.layout.ThreePaneMotion getNoMotion();
+ property public final androidx.compose.material3.adaptive.layout.ThreePaneMotion NoMotion;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public final class ThreePaneScaffoldAdaptStrategies {
+ ctor public ThreePaneScaffoldAdaptStrategies(androidx.compose.material3.adaptive.layout.AdaptStrategy primaryPaneAdaptStrategy, androidx.compose.material3.adaptive.layout.AdaptStrategy secondaryPaneAdaptStrategy, androidx.compose.material3.adaptive.layout.AdaptStrategy tertiaryPaneAdaptStrategy);
+ method public operator androidx.compose.material3.adaptive.layout.AdaptStrategy get(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole role);
+ }
+
+ public final class ThreePaneScaffoldDestinationItem<T> {
+ ctor public ThreePaneScaffoldDestinationItem(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole pane, optional T? contentKey);
+ method public T? getContentKey();
+ method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole getPane();
+ property public final T? contentKey;
+ property public final androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole pane;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Immutable public final class ThreePaneScaffoldHorizontalOrder {
+ method public void forEach(kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole,kotlin.Unit> action);
+ method public void forEachIndexed(kotlin.jvm.functions.Function2<? super java.lang.Integer,? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole,kotlin.Unit> action);
+ method public void forEachIndexedReversed(kotlin.jvm.functions.Function2<? super java.lang.Integer,? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole,kotlin.Unit> action);
+ method public operator androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole get(int index);
+ method public int getSize();
+ method public int indexOf(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole role);
+ property public int size;
+ }
+
+ public final class ThreePaneScaffoldKt {
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveComponentOverrideApi public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.material3.adaptive.layout.ThreePaneScaffoldOverride> getLocalThreePaneScaffoldOverride();
+ property @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveComponentOverrideApi public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.material3.adaptive.layout.ThreePaneScaffoldOverride> LocalThreePaneScaffoldOverride;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveComponentOverrideApi public interface ThreePaneScaffoldOverride {
+ method @androidx.compose.runtime.Composable public void ThreePaneScaffold(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldOverrideContext);
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveComponentOverrideApi public final class ThreePaneScaffoldOverrideContext {
+ method public androidx.compose.ui.Modifier getModifier();
+ method public kotlin.jvm.functions.Function1<androidx.compose.material3.adaptive.layout.PaneExpansionState,kotlin.Unit>? getPaneExpansionDragHandle();
+ method public androidx.compose.material3.adaptive.layout.PaneExpansionState getPaneExpansionState();
+ method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldHorizontalOrder getPaneOrder();
+ method public kotlin.jvm.functions.Function0<kotlin.Unit> getPrimaryPane();
+ method public androidx.compose.material3.adaptive.layout.PaneScaffoldDirective getScaffoldDirective();
+ method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldState getScaffoldState();
+ method public kotlin.jvm.functions.Function0<kotlin.Unit> getSecondaryPane();
+ method public kotlin.jvm.functions.Function0<kotlin.Unit>? getTertiaryPane();
+ property public final androidx.compose.ui.Modifier modifier;
+ property public final kotlin.jvm.functions.Function1<androidx.compose.material3.adaptive.layout.PaneExpansionState,kotlin.Unit>? paneExpansionDragHandle;
+ property public final androidx.compose.material3.adaptive.layout.PaneExpansionState paneExpansionState;
+ property public final androidx.compose.material3.adaptive.layout.ThreePaneScaffoldHorizontalOrder paneOrder;
+ property public final kotlin.jvm.functions.Function0<kotlin.Unit> primaryPane;
+ property public final androidx.compose.material3.adaptive.layout.PaneScaffoldDirective scaffoldDirective;
+ property public final androidx.compose.material3.adaptive.layout.ThreePaneScaffoldState scaffoldState;
+ property public final kotlin.jvm.functions.Function0<kotlin.Unit> secondaryPane;
+ property public final kotlin.jvm.functions.Function0<kotlin.Unit>? tertiaryPane;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public sealed interface ThreePaneScaffoldPaneScope extends androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope androidx.compose.material3.adaptive.layout.ExtendedPaneScaffoldPaneScope<androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole,androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue> {
+ }
+
+ public enum ThreePaneScaffoldRole {
+ enum_constant public static final androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole Primary;
+ enum_constant public static final androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole Secondary;
+ enum_constant public static final androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole Tertiary;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public sealed interface ThreePaneScaffoldScope extends androidx.compose.material3.adaptive.layout.ExtendedPaneScaffoldScope<androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole,androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue> {
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Stable public abstract sealed class ThreePaneScaffoldState {
+ method public abstract androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue getCurrentState();
+ method @FloatRange(from=0.0, to=1.0) public abstract float getProgressFraction();
+ method public abstract androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue getTargetState();
+ method public abstract boolean isPredictiveBackInProgress();
+ property public abstract androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue currentState;
+ property public abstract boolean isPredictiveBackInProgress;
+ property @FloatRange(from=0.0, to=1.0) public abstract float progressFraction;
+ property public abstract androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue targetState;
+ }
+
+ @androidx.compose.runtime.Immutable public final class ThreePaneScaffoldValue implements androidx.compose.material3.adaptive.layout.PaneExpansionStateKeyProvider androidx.compose.material3.adaptive.layout.PaneScaffoldValue<androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole> {
+ ctor public ThreePaneScaffoldValue(String primary, String secondary, String tertiary);
+ method public operator String get(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole role);
+ method public androidx.compose.material3.adaptive.layout.PaneExpansionStateKey getPaneExpansionStateKey();
+ method public String getPrimary();
+ method public String getSecondary();
+ method public String getTertiary();
+ property public androidx.compose.material3.adaptive.layout.PaneExpansionStateKey paneExpansionStateKey;
+ property public final String primary;
+ property public final String secondary;
+ property public final String tertiary;
+ }
+
+ public final class ThreePaneScaffoldValueKt {
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public static androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue calculateThreePaneScaffoldValue(int maxHorizontalPartitions, androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies adaptStrategies, androidx.compose.material3.adaptive.layout.ThreePaneScaffoldDestinationItem<? extends java.lang.Object?>? currentDestination);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public static androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue calculateThreePaneScaffoldValue(int maxHorizontalPartitions, androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies adaptStrategies, java.util.List<? extends androidx.compose.material3.adaptive.layout.ThreePaneScaffoldDestinationItem<? extends java.lang.Object?>> destinationHistory);
+ }
+
+}
+
diff --git a/compose/material3/adaptive/adaptive-layout/api/res-1.1.0-beta01.txt b/compose/material3/adaptive/adaptive-layout/api/res-1.1.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/api/res-1.1.0-beta01.txt
diff --git a/compose/material3/adaptive/adaptive-layout/api/restricted_1.1.0-beta01.txt b/compose/material3/adaptive/adaptive-layout/api/restricted_1.1.0-beta01.txt
new file mode 100644
index 0000000..b4cd5c9
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/api/restricted_1.1.0-beta01.txt
@@ -0,0 +1,469 @@
+// Signature format: 4.0
+package androidx.compose.material3.adaptive.layout {
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Stable public sealed interface AdaptStrategy {
+ method public String adapt();
+ field public static final androidx.compose.material3.adaptive.layout.AdaptStrategy.Companion Companion;
+ }
+
+ public static final class AdaptStrategy.Companion {
+ method public androidx.compose.material3.adaptive.layout.AdaptStrategy getHide();
+ property public final androidx.compose.material3.adaptive.layout.AdaptStrategy Hide;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveComponentOverrideApi public interface AnimatedPaneOverride {
+ method @androidx.compose.runtime.Composable public <S, T extends androidx.compose.material3.adaptive.layout.PaneScaffoldValue<S>> void AnimatedPane(androidx.compose.material3.adaptive.layout.AnimatedPaneOverrideContext<S,T>);
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveComponentOverrideApi public final class AnimatedPaneOverrideContext<S, T extends androidx.compose.material3.adaptive.layout.PaneScaffoldValue<S>> {
+ method public androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntRect> getBoundsAnimationSpec();
+ method public kotlin.jvm.functions.Function1<androidx.compose.material3.adaptive.layout.AnimatedPaneScope,kotlin.Unit> getContent();
+ method public androidx.compose.animation.EnterTransition getEnterTransition();
+ method public androidx.compose.animation.ExitTransition getExitTransition();
+ method public androidx.compose.ui.Modifier getModifier();
+ method public androidx.compose.material3.adaptive.layout.ExtendedPaneScaffoldPaneScope<S,T> getScope();
+ property public final androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntRect> boundsAnimationSpec;
+ property public final kotlin.jvm.functions.Function1<androidx.compose.material3.adaptive.layout.AnimatedPaneScope,kotlin.Unit> content;
+ property public final androidx.compose.animation.EnterTransition enterTransition;
+ property public final androidx.compose.animation.ExitTransition exitTransition;
+ property public final androidx.compose.ui.Modifier modifier;
+ property public final androidx.compose.material3.adaptive.layout.ExtendedPaneScaffoldPaneScope<S,T> scope;
+ }
+
+ public sealed interface AnimatedPaneScope extends androidx.compose.animation.AnimatedVisibilityScope {
+ field public static final androidx.compose.material3.adaptive.layout.AnimatedPaneScope.Companion Companion;
+ }
+
+ public static final class AnimatedPaneScope.Companion {
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public androidx.compose.material3.adaptive.layout.AnimatedPaneScope create(androidx.compose.animation.AnimatedVisibilityScope animatedVisibilityScope);
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public sealed interface ExtendedPaneScaffoldPaneScope<Role, ScaffoldValue extends androidx.compose.material3.adaptive.layout.PaneScaffoldValue<Role>> extends androidx.compose.material3.adaptive.layout.ExtendedPaneScaffoldScope<Role,ScaffoldValue> androidx.compose.material3.adaptive.layout.PaneScaffoldPaneScope<Role> {
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public sealed interface ExtendedPaneScaffoldScope<Role, ScaffoldValue extends androidx.compose.material3.adaptive.layout.PaneScaffoldValue<Role>> extends androidx.compose.material3.adaptive.layout.PaneScaffoldScope androidx.compose.ui.layout.LookaheadScope androidx.compose.material3.adaptive.layout.PaneScaffoldTransitionScope<Role,ScaffoldValue> {
+ }
+
+ @androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class HingePolicy {
+ field public static final androidx.compose.material3.adaptive.layout.HingePolicy.Companion Companion;
+ }
+
+ public static final class HingePolicy.Companion {
+ method public int getAlwaysAvoid();
+ method public int getAvoidOccluding();
+ method public int getAvoidSeparating();
+ method public int getNeverAvoid();
+ property public final int AlwaysAvoid;
+ property public final int AvoidOccluding;
+ property public final int AvoidSeparating;
+ property public final int NeverAvoid;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public final class ListDetailPaneScaffoldDefaults {
+ method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies adaptStrategies(optional androidx.compose.material3.adaptive.layout.AdaptStrategy detailPaneAdaptStrategy, optional androidx.compose.material3.adaptive.layout.AdaptStrategy listPaneAdaptStrategy, optional androidx.compose.material3.adaptive.layout.AdaptStrategy extraPaneAdaptStrategy);
+ field public static final androidx.compose.material3.adaptive.layout.ListDetailPaneScaffoldDefaults INSTANCE;
+ }
+
+ public final class ListDetailPaneScaffoldKt {
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void ListDetailPaneScaffold(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective directive, androidx.compose.material3.adaptive.layout.ThreePaneScaffoldState scaffoldState, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> listPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> detailPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit>? extraPane, optional kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.layout.PaneExpansionState,kotlin.Unit>? paneExpansionDragHandle, optional androidx.compose.material3.adaptive.layout.PaneExpansionState? paneExpansionState);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void ListDetailPaneScaffold(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective directive, androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> listPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> detailPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit>? extraPane, optional kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.layout.PaneExpansionState,kotlin.Unit>? paneExpansionDragHandle, optional androidx.compose.material3.adaptive.layout.PaneExpansionState? paneExpansionState);
+ }
+
+ public final class ListDetailPaneScaffoldRole {
+ method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole getDetail();
+ method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole getExtra();
+ method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole getList();
+ property public final androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole Detail;
+ property public final androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole Extra;
+ property public final androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole List;
+ field public static final androidx.compose.material3.adaptive.layout.ListDetailPaneScaffoldRole INSTANCE;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Stable public final class MutableThreePaneScaffoldState extends androidx.compose.material3.adaptive.layout.ThreePaneScaffoldState {
+ ctor public MutableThreePaneScaffoldState(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue initialScaffoldValue);
+ method public suspend Object? animateTo(optional androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue targetState, optional androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float>? animationSpec, optional boolean isPredictiveBackInProgress, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue getCurrentState();
+ method @FloatRange(from=0.0, to=1.0) public float getProgressFraction();
+ method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue getTargetState();
+ method public boolean isPredictiveBackInProgress();
+ method public suspend Object? seekTo(@FloatRange(from=0.0, to=1.0) float fraction, optional androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue targetState, optional boolean isPredictiveBackInProgress, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public suspend Object? snapTo(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue targetState, optional boolean isPredictiveBackInProgress, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ property public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue currentState;
+ property public boolean isPredictiveBackInProgress;
+ property @FloatRange(from=0.0, to=1.0) public float progressFraction;
+ property public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue targetState;
+ }
+
+ @kotlin.jvm.JvmInline public final value class PaneAdaptedValue {
+ field public static final androidx.compose.material3.adaptive.layout.PaneAdaptedValue.Companion Companion;
+ }
+
+ public static final class PaneAdaptedValue.Companion {
+ method public String getExpanded();
+ method public String getHidden();
+ property public final String Expanded;
+ property public final String Hidden;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public abstract sealed class PaneExpansionAnchor {
+ method @androidx.compose.runtime.Composable public abstract String getDescription();
+ property @androidx.compose.runtime.Composable public abstract String description;
+ }
+
+ public abstract static class PaneExpansionAnchor.Offset extends androidx.compose.material3.adaptive.layout.PaneExpansionAnchor {
+ method public final int getDirection();
+ method public final float getOffset();
+ property public final int direction;
+ property public final float offset;
+ field public static final androidx.compose.material3.adaptive.layout.PaneExpansionAnchor.Offset.Companion Companion;
+ }
+
+ public static final class PaneExpansionAnchor.Offset.Companion {
+ method public androidx.compose.material3.adaptive.layout.PaneExpansionAnchor.Offset fromEnd(float offset);
+ method public androidx.compose.material3.adaptive.layout.PaneExpansionAnchor.Offset fromStart(float offset);
+ }
+
+ @kotlin.jvm.JvmInline public static final value class PaneExpansionAnchor.Offset.Direction {
+ field public static final androidx.compose.material3.adaptive.layout.PaneExpansionAnchor.Offset.Direction.Companion Companion;
+ }
+
+ public static final class PaneExpansionAnchor.Offset.Direction.Companion {
+ method public int getFromEnd();
+ method public int getFromStart();
+ property public final int FromEnd;
+ property public final int FromStart;
+ }
+
+ public static final class PaneExpansionAnchor.Proportion extends androidx.compose.material3.adaptive.layout.PaneExpansionAnchor {
+ ctor public PaneExpansionAnchor.Proportion(@FloatRange(from=0.0, to=1.0) float proportion);
+ method @androidx.compose.runtime.Composable public String getDescription();
+ method public float getProportion();
+ property @androidx.compose.runtime.Composable public String description;
+ property @FloatRange(from=0.0, to=1.0) public final float proportion;
+ }
+
+ public final class PaneExpansionDraggableModifierKt {
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static kotlin.jvm.functions.Function1<androidx.compose.ui.semantics.SemanticsPropertyReceiver,kotlin.Unit> defaultDragHandleSemantics(androidx.compose.material3.adaptive.layout.PaneExpansionState);
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Stable public final class PaneExpansionState {
+ method public suspend Object? animateTo(androidx.compose.material3.adaptive.layout.PaneExpansionAnchor anchor, optional float initialVelocity, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public void clear();
+ method public androidx.compose.material3.adaptive.layout.PaneExpansionAnchor? getCurrentAnchor();
+ method public boolean isUnspecified();
+ method public void setFirstPaneProportion(@FloatRange(from=0.0, to=1.0) float firstPaneProportion);
+ method public void setFirstPaneWidth(int firstPaneWidth);
+ property public final androidx.compose.material3.adaptive.layout.PaneExpansionAnchor? currentAnchor;
+ field public static final androidx.compose.material3.adaptive.layout.PaneExpansionState.Companion Companion;
+ field public static final int Unspecified = -1; // 0xffffffff
+ }
+
+ public static final class PaneExpansionState.Companion {
+ property public static final int Unspecified;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Immutable public sealed interface PaneExpansionStateKey {
+ field public static final androidx.compose.material3.adaptive.layout.PaneExpansionStateKey.Companion Companion;
+ }
+
+ public static final class PaneExpansionStateKey.Companion {
+ method public androidx.compose.material3.adaptive.layout.PaneExpansionStateKey getDefault();
+ property public final androidx.compose.material3.adaptive.layout.PaneExpansionStateKey Default;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Stable public sealed interface PaneExpansionStateKeyProvider {
+ method public androidx.compose.material3.adaptive.layout.PaneExpansionStateKey getPaneExpansionStateKey();
+ property public abstract androidx.compose.material3.adaptive.layout.PaneExpansionStateKey paneExpansionStateKey;
+ }
+
+ public final class PaneExpansionStateKt {
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.layout.PaneExpansionState rememberPaneExpansionState(optional androidx.compose.material3.adaptive.layout.PaneExpansionStateKey key, optional java.util.List<? extends androidx.compose.material3.adaptive.layout.PaneExpansionAnchor> anchors, optional int initialAnchoredIndex, optional androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float> anchoringAnimationSpec, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.layout.PaneExpansionState rememberPaneExpansionState(androidx.compose.material3.adaptive.layout.PaneExpansionStateKeyProvider keyProvider, optional java.util.List<? extends androidx.compose.material3.adaptive.layout.PaneExpansionAnchor> anchors, optional int initialAnchoredIndex, optional androidx.compose.animation.core.FiniteAnimationSpec<java.lang.Float> anchoringAnimationSpec, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior);
+ }
+
+ public final class PaneKt {
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static <S, T extends androidx.compose.material3.adaptive.layout.PaneScaffoldValue<S>> void AnimatedPane(androidx.compose.material3.adaptive.layout.ExtendedPaneScaffoldPaneScope<S,T>, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.animation.EnterTransition enterTransition, optional androidx.compose.animation.ExitTransition exitTransition, optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntRect> boundsAnimationSpec, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.AnimatedPaneScope,kotlin.Unit> content);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveComponentOverrideApi public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.material3.adaptive.layout.AnimatedPaneOverride> getLocalAnimatedPaneOverride();
+ property @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveComponentOverrideApi public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.material3.adaptive.layout.AnimatedPaneOverride> LocalAnimatedPaneOverride;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Stable public sealed interface PaneMotion {
+ method public int getType();
+ property public abstract int type;
+ field public static final androidx.compose.material3.adaptive.layout.PaneMotion.Companion Companion;
+ }
+
+ public static final class PaneMotion.Companion {
+ method public androidx.compose.material3.adaptive.layout.PaneMotion getAnimateBounds();
+ method public androidx.compose.material3.adaptive.layout.PaneMotion getEnterFromLeft();
+ method public androidx.compose.material3.adaptive.layout.PaneMotion getEnterFromLeftDelayed();
+ method public androidx.compose.material3.adaptive.layout.PaneMotion getEnterFromRight();
+ method public androidx.compose.material3.adaptive.layout.PaneMotion getEnterFromRightDelayed();
+ method public androidx.compose.material3.adaptive.layout.PaneMotion getEnterWithExpand();
+ method public androidx.compose.material3.adaptive.layout.PaneMotion getExitToLeft();
+ method public androidx.compose.material3.adaptive.layout.PaneMotion getExitToRight();
+ method public androidx.compose.material3.adaptive.layout.PaneMotion getExitWithShrink();
+ method public androidx.compose.material3.adaptive.layout.PaneMotion getNoMotion();
+ property public final androidx.compose.material3.adaptive.layout.PaneMotion AnimateBounds;
+ property public final androidx.compose.material3.adaptive.layout.PaneMotion EnterFromLeft;
+ property public final androidx.compose.material3.adaptive.layout.PaneMotion EnterFromLeftDelayed;
+ property public final androidx.compose.material3.adaptive.layout.PaneMotion EnterFromRight;
+ property public final androidx.compose.material3.adaptive.layout.PaneMotion EnterFromRightDelayed;
+ property public final androidx.compose.material3.adaptive.layout.PaneMotion EnterWithExpand;
+ property public final androidx.compose.material3.adaptive.layout.PaneMotion ExitToLeft;
+ property public final androidx.compose.material3.adaptive.layout.PaneMotion ExitToRight;
+ property public final androidx.compose.material3.adaptive.layout.PaneMotion ExitWithShrink;
+ property public final androidx.compose.material3.adaptive.layout.PaneMotion NoMotion;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @kotlin.jvm.JvmInline public static final value class PaneMotion.Type {
+ method public int getValue();
+ property public final int value;
+ field public static final androidx.compose.material3.adaptive.layout.PaneMotion.Type.Companion Companion;
+ }
+
+ public static final class PaneMotion.Type.Companion {
+ method public int getEntering();
+ method public int getExiting();
+ method public int getHidden();
+ method public int getShown();
+ property public final int Entering;
+ property public final int Exiting;
+ property public final int Hidden;
+ property public final int Shown;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public final class PaneMotionData {
+ method public androidx.compose.material3.adaptive.layout.PaneMotion getMotion();
+ method public long getOriginPosition();
+ method public long getOriginSize();
+ method public long getTargetPosition();
+ method public long getTargetSize();
+ property public final androidx.compose.material3.adaptive.layout.PaneMotion motion;
+ property public final long originPosition;
+ property public final long originSize;
+ property public final long targetPosition;
+ property public final long targetSize;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public final class PaneMotionDefaults {
+ method public androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntRect> getAnimationSpec();
+ method public androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntRect> getDelayedAnimationSpec();
+ method public androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> getDelayedOffsetAnimationSpec();
+ method public androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntSize> getDelayedSizeAnimationSpec();
+ method public androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> getOffsetAnimationSpec();
+ method public androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntSize> getSizeAnimationSpec();
+ property public final androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntRect> AnimationSpec;
+ property public final androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntRect> DelayedAnimationSpec;
+ property public final androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> DelayedOffsetAnimationSpec;
+ property public final androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntSize> DelayedSizeAnimationSpec;
+ property public final androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> OffsetAnimationSpec;
+ property public final androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntSize> SizeAnimationSpec;
+ field public static final androidx.compose.material3.adaptive.layout.PaneMotionDefaults INSTANCE;
+ }
+
+ public final class PaneMotionKt {
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public static <Role> androidx.compose.animation.EnterTransition calculateDefaultEnterTransition(androidx.compose.material3.adaptive.layout.PaneScaffoldMotionDataProvider<Role>, Role role);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public static <Role> androidx.compose.animation.ExitTransition calculateDefaultExitTransition(androidx.compose.material3.adaptive.layout.PaneScaffoldMotionDataProvider<Role>, Role role);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public static inline <Role> void forEach(androidx.compose.material3.adaptive.layout.PaneScaffoldMotionDataProvider<Role>, kotlin.jvm.functions.Function2<? super Role,? super androidx.compose.material3.adaptive.layout.PaneMotionData,kotlin.Unit> action);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public static inline <Role> void forEachReversed(androidx.compose.material3.adaptive.layout.PaneScaffoldMotionDataProvider<Role>, kotlin.jvm.functions.Function2<? super Role,? super androidx.compose.material3.adaptive.layout.PaneMotionData,kotlin.Unit> action);
+ }
+
+ @androidx.compose.runtime.Immutable public final class PaneScaffoldDirective {
+ ctor public PaneScaffoldDirective(int maxHorizontalPartitions, float horizontalPartitionSpacerSize, int maxVerticalPartitions, float verticalPartitionSpacerSize, float defaultPanePreferredWidth, java.util.List<androidx.compose.ui.geometry.Rect> excludedBounds);
+ method public androidx.compose.material3.adaptive.layout.PaneScaffoldDirective copy(optional int maxHorizontalPartitions, optional float horizontalPartitionSpacerSize, optional int maxVerticalPartitions, optional float verticalPartitionSpacerSize, optional float defaultPanePreferredWidth, optional java.util.List<androidx.compose.ui.geometry.Rect> excludedBounds);
+ method public float getDefaultPanePreferredWidth();
+ method public java.util.List<androidx.compose.ui.geometry.Rect> getExcludedBounds();
+ method public float getHorizontalPartitionSpacerSize();
+ method public int getMaxHorizontalPartitions();
+ method public int getMaxVerticalPartitions();
+ method public float getVerticalPartitionSpacerSize();
+ property public final float defaultPanePreferredWidth;
+ property public final java.util.List<androidx.compose.ui.geometry.Rect> excludedBounds;
+ property public final float horizontalPartitionSpacerSize;
+ property public final int maxHorizontalPartitions;
+ property public final int maxVerticalPartitions;
+ property public final float verticalPartitionSpacerSize;
+ field public static final androidx.compose.material3.adaptive.layout.PaneScaffoldDirective.Companion Companion;
+ }
+
+ public static final class PaneScaffoldDirective.Companion {
+ method public androidx.compose.material3.adaptive.layout.PaneScaffoldDirective getDefault();
+ property public final androidx.compose.material3.adaptive.layout.PaneScaffoldDirective Default;
+ }
+
+ public final class PaneScaffoldDirectiveKt {
+ method public static androidx.compose.material3.adaptive.layout.PaneScaffoldDirective calculatePaneScaffoldDirective(androidx.compose.material3.adaptive.WindowAdaptiveInfo windowAdaptiveInfo, optional int verticalHingePolicy);
+ method public static androidx.compose.material3.adaptive.layout.PaneScaffoldDirective calculatePaneScaffoldDirectiveWithTwoPanesOnMediumWidth(androidx.compose.material3.adaptive.WindowAdaptiveInfo windowAdaptiveInfo, optional int verticalHingePolicy);
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public sealed interface PaneScaffoldMotionDataProvider<Role> {
+ method public operator androidx.compose.material3.adaptive.layout.PaneMotionData get(int index);
+ method public operator androidx.compose.material3.adaptive.layout.PaneMotionData get(Role role);
+ method public int getCount();
+ method public Role getRoleAt(int index);
+ method public long getScaffoldSize();
+ property public abstract int count;
+ property public abstract long scaffoldSize;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public sealed interface PaneScaffoldPaneScope<Role> {
+ method public androidx.compose.material3.adaptive.layout.PaneMotion getPaneMotion();
+ method public Role getPaneRole();
+ property public abstract androidx.compose.material3.adaptive.layout.PaneMotion paneMotion;
+ property public abstract Role paneRole;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public sealed interface PaneScaffoldParentData {
+ method public float getMinTouchTargetSize();
+ method public float getPreferredWidth();
+ method public boolean isAnimatedPane();
+ property public abstract boolean isAnimatedPane;
+ property public abstract float minTouchTargetSize;
+ property public abstract float preferredWidth;
+ }
+
+ public sealed interface PaneScaffoldScope {
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public androidx.compose.ui.Modifier paneExpansionDraggable(androidx.compose.ui.Modifier, androidx.compose.material3.adaptive.layout.PaneExpansionState state, float minTouchTargetSize, androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.ui.semantics.SemanticsPropertyReceiver,kotlin.Unit> semanticsProperties);
+ method public androidx.compose.ui.Modifier preferredWidth(androidx.compose.ui.Modifier, float width);
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public sealed interface PaneScaffoldTransitionScope<Role, ScaffoldValue extends androidx.compose.material3.adaptive.layout.PaneScaffoldValue<Role>> {
+ method public androidx.compose.material3.adaptive.layout.PaneScaffoldMotionDataProvider<Role> getMotionDataProvider();
+ method @FloatRange(from=0.0, to=1.0) public float getMotionProgress();
+ method public androidx.compose.animation.core.Transition<ScaffoldValue> getScaffoldStateTransition();
+ property public abstract androidx.compose.material3.adaptive.layout.PaneScaffoldMotionDataProvider<Role> motionDataProvider;
+ property @FloatRange(from=0.0, to=1.0) public abstract float motionProgress;
+ property public abstract androidx.compose.animation.core.Transition<ScaffoldValue> scaffoldStateTransition;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public sealed interface PaneScaffoldValue<T> {
+ method public operator String get(T role);
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public final class SupportingPaneScaffoldDefaults {
+ method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies adaptStrategies(optional androidx.compose.material3.adaptive.layout.AdaptStrategy mainPaneAdaptStrategy, optional androidx.compose.material3.adaptive.layout.AdaptStrategy supportingPaneAdaptStrategy, optional androidx.compose.material3.adaptive.layout.AdaptStrategy extraPaneAdaptStrategy);
+ field public static final androidx.compose.material3.adaptive.layout.SupportingPaneScaffoldDefaults INSTANCE;
+ }
+
+ public final class SupportingPaneScaffoldKt {
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void SupportingPaneScaffold(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective directive, androidx.compose.material3.adaptive.layout.ThreePaneScaffoldState scaffoldState, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> mainPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> supportingPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit>? extraPane, optional kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.layout.PaneExpansionState,kotlin.Unit>? paneExpansionDragHandle, optional androidx.compose.material3.adaptive.layout.PaneExpansionState? paneExpansionState);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void SupportingPaneScaffold(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective directive, androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> mainPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> supportingPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit>? extraPane, optional kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.layout.PaneExpansionState,kotlin.Unit>? paneExpansionDragHandle, optional androidx.compose.material3.adaptive.layout.PaneExpansionState? paneExpansionState);
+ }
+
+ public final class SupportingPaneScaffoldRole {
+ method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole getExtra();
+ method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole getMain();
+ method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole getSupporting();
+ property public final androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole Extra;
+ property public final androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole Main;
+ property public final androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole Supporting;
+ field public static final androidx.compose.material3.adaptive.layout.SupportingPaneScaffoldRole INSTANCE;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Immutable public final class ThreePaneMotion {
+ method public operator androidx.compose.material3.adaptive.layout.PaneMotion get(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole role);
+ field public static final androidx.compose.material3.adaptive.layout.ThreePaneMotion.Companion Companion;
+ }
+
+ public static final class ThreePaneMotion.Companion {
+ method public androidx.compose.material3.adaptive.layout.ThreePaneMotion getNoMotion();
+ property public final androidx.compose.material3.adaptive.layout.ThreePaneMotion NoMotion;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public final class ThreePaneScaffoldAdaptStrategies {
+ ctor public ThreePaneScaffoldAdaptStrategies(androidx.compose.material3.adaptive.layout.AdaptStrategy primaryPaneAdaptStrategy, androidx.compose.material3.adaptive.layout.AdaptStrategy secondaryPaneAdaptStrategy, androidx.compose.material3.adaptive.layout.AdaptStrategy tertiaryPaneAdaptStrategy);
+ method public operator androidx.compose.material3.adaptive.layout.AdaptStrategy get(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole role);
+ }
+
+ public final class ThreePaneScaffoldDestinationItem<T> {
+ ctor public ThreePaneScaffoldDestinationItem(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole pane, optional T? contentKey);
+ method public T? getContentKey();
+ method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole getPane();
+ property public final T? contentKey;
+ property public final androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole pane;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Immutable public final class ThreePaneScaffoldHorizontalOrder {
+ method public void forEach(kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole,kotlin.Unit> action);
+ method public void forEachIndexed(kotlin.jvm.functions.Function2<? super java.lang.Integer,? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole,kotlin.Unit> action);
+ method public void forEachIndexedReversed(kotlin.jvm.functions.Function2<? super java.lang.Integer,? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole,kotlin.Unit> action);
+ method public operator androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole get(int index);
+ method public int getSize();
+ method public int indexOf(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole role);
+ property public int size;
+ }
+
+ public final class ThreePaneScaffoldKt {
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveComponentOverrideApi public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.material3.adaptive.layout.ThreePaneScaffoldOverride> getLocalThreePaneScaffoldOverride();
+ property @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveComponentOverrideApi public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.material3.adaptive.layout.ThreePaneScaffoldOverride> LocalThreePaneScaffoldOverride;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveComponentOverrideApi public interface ThreePaneScaffoldOverride {
+ method @androidx.compose.runtime.Composable public void ThreePaneScaffold(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldOverrideContext);
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveComponentOverrideApi public final class ThreePaneScaffoldOverrideContext {
+ method public androidx.compose.ui.Modifier getModifier();
+ method public kotlin.jvm.functions.Function1<androidx.compose.material3.adaptive.layout.PaneExpansionState,kotlin.Unit>? getPaneExpansionDragHandle();
+ method public androidx.compose.material3.adaptive.layout.PaneExpansionState getPaneExpansionState();
+ method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldHorizontalOrder getPaneOrder();
+ method public kotlin.jvm.functions.Function0<kotlin.Unit> getPrimaryPane();
+ method public androidx.compose.material3.adaptive.layout.PaneScaffoldDirective getScaffoldDirective();
+ method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldState getScaffoldState();
+ method public kotlin.jvm.functions.Function0<kotlin.Unit> getSecondaryPane();
+ method public kotlin.jvm.functions.Function0<kotlin.Unit>? getTertiaryPane();
+ property public final androidx.compose.ui.Modifier modifier;
+ property public final kotlin.jvm.functions.Function1<androidx.compose.material3.adaptive.layout.PaneExpansionState,kotlin.Unit>? paneExpansionDragHandle;
+ property public final androidx.compose.material3.adaptive.layout.PaneExpansionState paneExpansionState;
+ property public final androidx.compose.material3.adaptive.layout.ThreePaneScaffoldHorizontalOrder paneOrder;
+ property public final kotlin.jvm.functions.Function0<kotlin.Unit> primaryPane;
+ property public final androidx.compose.material3.adaptive.layout.PaneScaffoldDirective scaffoldDirective;
+ property public final androidx.compose.material3.adaptive.layout.ThreePaneScaffoldState scaffoldState;
+ property public final kotlin.jvm.functions.Function0<kotlin.Unit> secondaryPane;
+ property public final kotlin.jvm.functions.Function0<kotlin.Unit>? tertiaryPane;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public sealed interface ThreePaneScaffoldPaneScope extends androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope androidx.compose.material3.adaptive.layout.ExtendedPaneScaffoldPaneScope<androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole,androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue> {
+ }
+
+ public enum ThreePaneScaffoldRole {
+ enum_constant public static final androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole Primary;
+ enum_constant public static final androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole Secondary;
+ enum_constant public static final androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole Tertiary;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public sealed interface ThreePaneScaffoldScope extends androidx.compose.material3.adaptive.layout.ExtendedPaneScaffoldScope<androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole,androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue> {
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Stable public abstract sealed class ThreePaneScaffoldState {
+ method public abstract androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue getCurrentState();
+ method @FloatRange(from=0.0, to=1.0) public abstract float getProgressFraction();
+ method public abstract androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue getTargetState();
+ method public abstract boolean isPredictiveBackInProgress();
+ property public abstract androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue currentState;
+ property public abstract boolean isPredictiveBackInProgress;
+ property @FloatRange(from=0.0, to=1.0) public abstract float progressFraction;
+ property public abstract androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue targetState;
+ }
+
+ @androidx.compose.runtime.Immutable public final class ThreePaneScaffoldValue implements androidx.compose.material3.adaptive.layout.PaneExpansionStateKeyProvider androidx.compose.material3.adaptive.layout.PaneScaffoldValue<androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole> {
+ ctor public ThreePaneScaffoldValue(String primary, String secondary, String tertiary);
+ method public operator String get(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole role);
+ method public androidx.compose.material3.adaptive.layout.PaneExpansionStateKey getPaneExpansionStateKey();
+ method public String getPrimary();
+ method public String getSecondary();
+ method public String getTertiary();
+ property public androidx.compose.material3.adaptive.layout.PaneExpansionStateKey paneExpansionStateKey;
+ property public final String primary;
+ property public final String secondary;
+ property public final String tertiary;
+ }
+
+ public final class ThreePaneScaffoldValueKt {
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public static androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue calculateThreePaneScaffoldValue(int maxHorizontalPartitions, androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies adaptStrategies, androidx.compose.material3.adaptive.layout.ThreePaneScaffoldDestinationItem<? extends java.lang.Object?>? currentDestination);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public static androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue calculateThreePaneScaffoldValue(int maxHorizontalPartitions, androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies adaptStrategies, java.util.List<? extends androidx.compose.material3.adaptive.layout.ThreePaneScaffoldDestinationItem<? extends java.lang.Object?>> destinationHistory);
+ }
+
+}
+
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-af/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-af/strings.xml
new file mode 100644
index 0000000..8908ff9
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-af/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Sleephandvatsel van paneeluitbreiding"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Verander paneelverdeling na %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d persent"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-am/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-am/strings.xml
new file mode 100644
index 0000000..f3ec9b8
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-am/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"የፔን መዘርጋት መያዣ ይጎትቱ"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"ፔን መከፋፈልን ወደ %s ይለውጡ"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d መቶኛ"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ar/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ar/strings.xml
new file mode 100644
index 0000000..fa48e19
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ar/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"مقبض السحب لتوسيع اللوحة"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"تغيير نسبة تقسيم اللوحة إلى %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d في المئة"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-as/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-as/strings.xml
index 120b08a..b96fec7 100644
--- a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-as/strings.xml
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-as/strings.xml
@@ -20,4 +20,8 @@
<string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"পে’ন সম্প্ৰসাৰণ কৰিবলৈ টনা হেণ্ডেল"</string>
<string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"পে’নৰ বিভাজন %sলৈ সলনি কৰক"</string>
<string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d শতাংশ"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-az/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-az/strings.xml
new file mode 100644
index 0000000..4f33001
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-az/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Panelin genişləndirilməsi üçün sürükləmə tutacağı"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Panel bölgüsünü %s olaraq dəyişin"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d faiz"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-b+sr+Latn/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..b7fb312
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Marker za prevlačenje kojim se proširuje okno"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Promenite podeljeno okno na: %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"Procenat: %d"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-be/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-be/strings.xml
new file mode 100644
index 0000000..9287ae5
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-be/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Маркер перацягвання для разгортвання панэлі"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Змяніць раздзяленне панэлі на %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d працэнтаў"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-bg/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-bg/strings.xml
new file mode 100644
index 0000000..9ea24d5
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-bg/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Манипулатор за преместване с плъзгане за разширяване на панела"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Промяна на разделянето на панела на %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"Процент: %d"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-bn/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-bn/strings.xml
new file mode 100644
index 0000000..88277ab
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-bn/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"প্যানেল বড় করার টেনে আনার হ্যান্ডেল"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"প্যানেল স্প্লিট %s-এ পরিবর্তন করুন"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d শতাংশ"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-bs/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-bs/strings.xml
new file mode 100644
index 0000000..1e37f66
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-bs/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Ručica za prevlačenje radi proširenja okna"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Promjena podijeljenog okna na %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d posto"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ca/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ca/strings.xml
new file mode 100644
index 0000000..c01e276a
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ca/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Ansa per arrossegar l\'expansió de la subfinestra"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Canvia la divisió de la subfinestra a %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"percentatge de %d"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-cs/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-cs/strings.xml
new file mode 100644
index 0000000..7b7b1f5
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-cs/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Úchyt pro přetažení a rozbalení panelu"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Změnit rozdělení panelu na %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d procent"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-da/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-da/strings.xml
new file mode 100644
index 0000000..b063bfa
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-da/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Håndtag til udvidelse af rude"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Skift rudeopdeling til %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d procent"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-de/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-de/strings.xml
new file mode 100644
index 0000000..1288d4d
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-de/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Ziehpunkt zum Maximieren des Bereichs"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"„Fenster teilen“ in %s ändern"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d Prozent"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-el/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-el/strings.xml
new file mode 100644
index 0000000..205f65d
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-el/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Λαβή μεταφοράς επέκτασης πλαισίου"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Αλλαγή επιμερισμού πλαισίου σε %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d τοις εκατό"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-en-rAU/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-en-rAU/strings.xml
new file mode 100644
index 0000000..4ce37ce
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-en-rAU/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Pane expansion drag handle"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Change pane split to %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d per cent"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-en-rCA/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-en-rCA/strings.xml
index c1e08b3..566041a 100644
--- a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-en-rCA/strings.xml
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-en-rCA/strings.xml
@@ -20,4 +20,6 @@
<string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Pane expansion drag handle"</string>
<string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Change pane split to %s"</string>
<string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d percent"</string>
+ <string name="m3_adaptive_default_pane_expansion_start_offset_anchor_description" msgid="5056616348537665604">"%d DPs from start"</string>
+ <string name="m3_adaptive_default_pane_expansion_end_offset_anchor_description" msgid="6412636251656811002">"%d DPs from end"</string>
</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-en-rGB/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-en-rGB/strings.xml
new file mode 100644
index 0000000..4ce37ce
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-en-rGB/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Pane expansion drag handle"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Change pane split to %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d per cent"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-en-rIN/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-en-rIN/strings.xml
new file mode 100644
index 0000000..4ce37ce
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-en-rIN/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Pane expansion drag handle"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Change pane split to %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d per cent"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-es-rUS/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..0b2032d
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-es-rUS/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Controlador de arrastre para expandir el panel"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Cambiar la división del panel a %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d por ciento"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-es/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-es/strings.xml
new file mode 100644
index 0000000..1156a91
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-es/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Controlador de arrastre para expandir el panel"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Cambiar división de panel a %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d por ciento"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-et/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-et/strings.xml
new file mode 100644
index 0000000..b4867e7
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-et/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Paani laiendamise lohistamispide"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Muutke jaotatud paan väärtusele %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d protsenti"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-eu/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-eu/strings.xml
new file mode 100644
index 0000000..ab819fa
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-eu/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Panelaren zabalera arrastatzeko kontrol-puntua"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Aldatu panelaren zatiketa eta ezarri %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"Ehuneko %d"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-fa/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-fa/strings.xml
new file mode 100644
index 0000000..b5becf3
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-fa/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"دستگیره کشاندن برای از هم بازکردن اندازه قاب"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"تقسیمبندی قاب به %s تغییر کند"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d درصد"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-fi/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-fi/strings.xml
new file mode 100644
index 0000000..76852627
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-fi/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Ruudun laajennusvetokahva"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Muuta ruudun jaoksi %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d prosenttia"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-fr-rCA/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-fr-rCA/strings.xml
new file mode 100644
index 0000000..bfcfa4f
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-fr-rCA/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Poignée de déplacement d\'extension du volet"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Modifiez la division du volet à %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d pour cent"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-fr/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-fr/strings.xml
new file mode 100644
index 0000000..958e4dc
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-fr/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Poignée de déplacement pour développer les volets"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Passer la répartition des volets sur %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d pour cent"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-gl/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-gl/strings.xml
new file mode 100644
index 0000000..98d43ed
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-gl/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Controlador de arrastre para despregar o panel"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Cambia o panel dividido a %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d por cento"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-gu/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-gu/strings.xml
new file mode 100644
index 0000000..4d0ea30
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-gu/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"વિભાગ વિસ્તરણ માટે ઑબ્જેક્ટ ખેંચવાનું હૅન્ડલ"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"વિભાગ વિભાજનને %s પર બદલો"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d ટકા"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-hi/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-hi/strings.xml
index f333cf2..106bb8f 100644
--- a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-hi/strings.xml
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-hi/strings.xml
@@ -20,4 +20,8 @@
<string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"पैनल को बड़ा करने के लिए, खींचकर छोड़ने वाला हैंडल"</string>
<string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"पैनल स्प्लिट को %s में बदलें"</string>
<string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d प्रतिशत"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-hr/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-hr/strings.xml
new file mode 100644
index 0000000..011ce1e
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-hr/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Marker za povlačenje proširenja okna"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Promijeni podjelu okna u: %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d posto"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-hu/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-hu/strings.xml
new file mode 100644
index 0000000..3c5b474
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-hu/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Panel kibontásának fogópontja"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Panelfelosztás módosítása a következőre: %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d százalék"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-hy/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-hy/strings.xml
new file mode 100644
index 0000000..be56efd
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-hy/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Փեղկի ծավալման տեղափոխման նշիչ"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Փեղկի բաժանումը դարձնել %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d տոկոս"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-in/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-in/strings.xml
new file mode 100644
index 0000000..e2015af
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-in/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Handel geser perluasan panel"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Ubah luas panel ganda menjadi %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d persen"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-is/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-is/strings.xml
new file mode 100644
index 0000000..0b5b73b
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-is/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Dragkló gluggastækkunar"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Breyta gluggaskiptingu í %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d prósent"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-it/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-it/strings.xml
new file mode 100644
index 0000000..fd0934c
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-it/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Punto di trascinamento per l\'espansione del riquadro"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Modifica la divisione del riquadro in %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d percentuale"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-iw/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-iw/strings.xml
new file mode 100644
index 0000000..22f606e
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-iw/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"נקודת אחיזה לגרירה להרחבת החלונית"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"שינוי של פיצול החלונית ל-%s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d אחוז"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ja/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ja/strings.xml
index bfbceb0..e82e606 100644
--- a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ja/strings.xml
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ja/strings.xml
@@ -20,4 +20,8 @@
<string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"ペインの展開のドラッグ ハンドル"</string>
<string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"ペインの分割を %s に変更"</string>
<string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d パーセント"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ka/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ka/strings.xml
index f9f4e65..1153733 100644
--- a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ka/strings.xml
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ka/strings.xml
@@ -20,4 +20,8 @@
<string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"არეს გაფართოების სახელური ჩავლებისთვის"</string>
<string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"არეს გაყოფის შეცვლა %s-ით"</string>
<string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d პროცენტი"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-kk/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-kk/strings.xml
new file mode 100644
index 0000000..807f362
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-kk/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Панельді жаюға арналған сүйрейтін тетік"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Панельді бөлу деңгейін %s етіп өзгерту"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d пайыз"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-km/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-km/strings.xml
new file mode 100644
index 0000000..096f5d0
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-km/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"ដងអូសពង្រីកផ្ទាំង"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"ប្ដូរការបំបែកផ្ទាំងទៅ %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d ភាគរយ"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-kn/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-kn/strings.xml
new file mode 100644
index 0000000..585e425
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-kn/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"ಪೇನ್ ಅನ್ನು ವಿಸ್ತೃತಗೊಳಿಸುವುದಕ್ಕೆ ನೆರವಾಗುವ ಡ್ರ್ಯಾಗ್ ಹ್ಯಾಂಡಲ್"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"ಪೇನ್ ವಿಭಜನೆಯನ್ನು %s ಗೆ ಬದಲಾಯಿಸಿ"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"ಶೇಕಡಾ %d"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ko/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ko/strings.xml
new file mode 100644
index 0000000..6f78f96
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ko/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"창 확장 드래그 핸들"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"창 분할을 %s(으)로 변경"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d퍼센트"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ky/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ky/strings.xml
new file mode 100644
index 0000000..dce8ec7
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ky/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Тактаны кеңейтүү үчүн сүйрөө маркери"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Тактанын бөлүнүшүн %s деп өзгөртүү"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d пайыз"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-lo/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-lo/strings.xml
new file mode 100644
index 0000000..c2e618f
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-lo/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"ບ່ອນຈັບລາກເພື່ອຂະຫຍາຍແຖບ"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"ປ່ຽນການແບ່ງແຖບເປັນ %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d ເປີເຊັນ"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-lt/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-lt/strings.xml
new file mode 100644
index 0000000..333a455
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-lt/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Srities išplėtimo vilkimo rankenėlė"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Keisti srities skaidymą į %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d proc."</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-lv/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-lv/strings.xml
new file mode 100644
index 0000000..4e1a0fb
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-lv/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Rūts izvēršanas vilkšanas turis"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Mainīt rūts sadalījumu uz šādu: %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d procents(-i)"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-mk/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-mk/strings.xml
new file mode 100644
index 0000000..51afd11
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-mk/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Рачка за влечење за проширување на окното"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Променете го поделеното окно на %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d насто"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ml/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ml/strings.xml
index 2b63179c..df0ae0a 100644
--- a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ml/strings.xml
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ml/strings.xml
@@ -20,4 +20,8 @@
<string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"പെയിൻ വികസിപ്പിക്കാനായി വലിച്ചിടുന്നതിനുള്ള ഹാൻഡിൽ"</string>
<string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"പെയിൻ വിഭജനം %s ആയി മാറ്റുക"</string>
<string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d ശതമാനം"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-mn/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-mn/strings.xml
new file mode 100644
index 0000000..3ccb872
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-mn/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Хэсгийн өргөтгөлийг чирэх бариул"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Хэсгийн хуваалтыг %s болгож өөрчлөх"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d хувь"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-mr/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-mr/strings.xml
new file mode 100644
index 0000000..7579a02
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-mr/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"पेन विस्तार ड्रॅग हँडल"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"स्प्लिट पेन %s वर बदला"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d टक्के"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ms/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ms/strings.xml
index 889db66e..ad90a10 100644
--- a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ms/strings.xml
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ms/strings.xml
@@ -20,4 +20,8 @@
<string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Pemegang seret pengembangan anak tetingkap"</string>
<string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Tukar anak tetingkap terpisah kepada %s"</string>
<string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d peratus"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-my/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-my/strings.xml
new file mode 100644
index 0000000..0b33798
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-my/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"အကန့်တိုးချဲ့မှု ဖိဆွဲအထိန်း"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"အကန့်အခွဲကို %s သို့ ပြောင်းနိုင်သည်"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d ရာခိုင်နှုန်း"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-nb/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-nb/strings.xml
new file mode 100644
index 0000000..c2b4ce3
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-nb/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Håndtak for utvidelse av feltet"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Endre feltdelingen til %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d prosent"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ne/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ne/strings.xml
new file mode 100644
index 0000000..b0560f5
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ne/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"पेन एक्स्पान्सनको ड्र्याग ह्यान्डल"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"पेन स्प्लिट परिवर्तन गरी %s बनाउनुहोस्"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d प्रतिशत"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-nl/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-nl/strings.xml
new file mode 100644
index 0000000..82a9d5c
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-nl/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Handgreep voor slepen om deelvenster uit te breiden"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Deelvenstersplitsing wijzigen in %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d procent"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-or/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-or/strings.xml
new file mode 100644
index 0000000..f84b2f5
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-or/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"ପେନ ବିସ୍ତାର ଡ୍ରାଗ ହେଣ୍ଡେଲ"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"ପେନ ସ୍ପ୍ଲିଟକୁ %sରେ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d ଶତକଡ଼ା"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-pa/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-pa/strings.xml
new file mode 100644
index 0000000..d139af9
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-pa/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"ਪੂਰਵ-ਝਲਕ ਦਾ ਵਿਸਤਾਰ ਕਰਨ ਲਈ ਘਸੀਟਣ ਵਾਲਾ ਹੈਂਡਲ"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"ਸਪਲਿਟ ਪੂਰਵ-ਝਲਕ ਨੂੰ %s \'ਤੇ ਬਦਲੋ"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d ਫ਼ੀਸਦ"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-pl/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-pl/strings.xml
index ec85843..d9f3cd1 100644
--- a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-pl/strings.xml
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-pl/strings.xml
@@ -20,4 +20,8 @@
<string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Uchwyt do przeciągania panelu"</string>
<string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Zmień podział panelu na %s"</string>
<string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d procent"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-pt-rBR/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-pt-rBR/strings.xml
new file mode 100644
index 0000000..740a3f9
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-pt-rBR/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Alça de arrastar para expandir o painel"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Mudar a divisão do painel para %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d por cento"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-pt-rPT/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-pt-rPT/strings.xml
index 115f3c0..96cd773 100644
--- a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-pt-rPT/strings.xml
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-pt-rPT/strings.xml
@@ -20,4 +20,8 @@
<string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Indicador para arrastar de expansão do painel"</string>
<string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Altere a divisão do painel para %s"</string>
<string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d por cento"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-pt/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-pt/strings.xml
new file mode 100644
index 0000000..740a3f9
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-pt/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Alça de arrastar para expandir o painel"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Mudar a divisão do painel para %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d por cento"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ro/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ro/strings.xml
new file mode 100644
index 0000000..8c1397e
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ro/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Ghidaj de tragere pentru extinderea panoului"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Modifică împărțirea panoului la %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d procent"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ru/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ru/strings.xml
new file mode 100644
index 0000000..d04f3ca
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ru/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Маркер перемещения для расширения панели"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Изменить пропорцию разделения панелей на %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"Значение в процентах: %d"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-si/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-si/strings.xml
new file mode 100644
index 0000000..1c5a14a1
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-si/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"පැනල ප්රසාරණ ඇදීම් හැඬලය"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"කවුළු බෙදීම %s ලෙස වෙනස් කරන්න"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"සියයට %d"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-sk/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-sk/strings.xml
new file mode 100644
index 0000000..1bcb79a
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-sk/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Presúvadlo na rozšírenie panela"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Zmeniť rozdelenie panela na %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"Počet percent: %d"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-sl/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-sl/strings.xml
new file mode 100644
index 0000000..c7ca0b6
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-sl/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Ročica za vlečenje za razširitev podokna"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Sprememba razdelitve podokna na %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d odstotkov"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-sq/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-sq/strings.xml
new file mode 100644
index 0000000..27b0a73
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-sq/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Doreza e zvarritjes për zgjerimin e panelit"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Ndrysho ndarjen e panelit në %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d për qind"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-sr/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-sr/strings.xml
new file mode 100644
index 0000000..8c51385
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-sr/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Маркер за превлачење којим се проширује окно"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Промените подељено окно на: %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"Проценат: %d"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-sv/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-sv/strings.xml
new file mode 100644
index 0000000..010c964
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-sv/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Handtag för rutexpansion"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Ändra rutdelning till %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d procent"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-sw/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-sw/strings.xml
new file mode 100644
index 0000000..06d401c
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-sw/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Aikoni ya buruta ili kupanua kijisehemu"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Badilisha hali ya kugawanya kijisehemu iwe %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"Asilimia %d"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ta/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ta/strings.xml
new file mode 100644
index 0000000..dfc1028
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ta/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"பெட்டியை இழுத்து விரிவாக்குவதற்கான ஹேண்டில்"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"பெட்டிப் பிரித்தலை %s ஆக மாற்றும்"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d சதவீதம்"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-te/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-te/strings.xml
new file mode 100644
index 0000000..63c702b
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-te/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"పేన్ను విస్తరించడానికి లాగే హ్యాండిల్"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"పేన్ విభజనను %sకు మార్చండి"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d శాతం"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-th/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-th/strings.xml
new file mode 100644
index 0000000..94ce1e8
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-th/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"แฮนเดิลการลากเพื่อขยายแผง"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"เปลี่ยนการแบ่งแผงเป็น %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d เปอร์เซ็นต์"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-tl/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-tl/strings.xml
new file mode 100644
index 0000000..b459c2a
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-tl/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Handle sa pag-drag sa pagpapalawak ng pane"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Gawing %s ang pane split"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d (na) porsyento"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-tr/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-tr/strings.xml
new file mode 100644
index 0000000..d478356
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-tr/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Bölmeyi genişletmek için sürükleme tutamacı"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Bölme oranını %s olarak değiştirin"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"Yüzde %d"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-uk/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-uk/strings.xml
new file mode 100644
index 0000000..27680e2b
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-uk/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Маркер переміщення для розгортання панелі"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Змінити розділення панелі на %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d%%"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ur/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ur/strings.xml
new file mode 100644
index 0000000..3acbaeeb
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-ur/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"پین کو پھیلانے کے لیے گھسیٹنے کا ہینڈل"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"پین اسپلٹ کو %s میں تبدیل کریں"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d فیصد"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-uz/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-uz/strings.xml
new file mode 100644
index 0000000..36d8660
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-uz/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Panelni kengaytirish uchun surish dastagi"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Ajratilgan panelni %s sifatida oʻzgartirish"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d foiz"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-vi/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-vi/strings.xml
new file mode 100644
index 0000000..e5cf8f1
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-vi/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Nút kéo mở rộng ngăn"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Thay đổi chế độ tách ngăn thành %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d phần trăm"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-zh-rCN/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..671e560
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-zh-rCN/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"窗格展开拖动手柄"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"将窗格分割更改为%s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"百分之 %d"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-zh-rHK/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-zh-rHK/strings.xml
new file mode 100644
index 0000000..6309aa1
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-zh-rHK/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"展開窗格拖曳控點"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"將分割窗格轉做 %s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"百分之 %d"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-zh-rTW/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..4da1690
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-zh-rTW/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"窗格擴展拖曳控點"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"將窗格分割變更為「%s」"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"百分之 %d"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-zu/strings.xml b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-zu/strings.xml
new file mode 100644
index 0000000..c549752
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-layout/src/androidMain/res/values-zu/strings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_content_description" msgid="9058489142432490820">"Isibambo sokuhudula isandiso sepane"</string>
+ <string name="m3_adaptive_default_pane_expansion_drag_handle_action_description" msgid="9031621431415014327">"Shintsha ukuhlukana kwepane kube ku-%s"</string>
+ <string name="m3_adaptive_default_pane_expansion_proportion_anchor_description" msgid="1205294531112795522">"%d iphesenti"</string>
+ <!-- no translation found for m3_adaptive_default_pane_expansion_start_offset_anchor_description (5056616348537665604) -->
+ <skip />
+ <!-- no translation found for m3_adaptive_default_pane_expansion_end_offset_anchor_description (6412636251656811002) -->
+ <skip />
+</resources>
diff --git a/compose/material3/adaptive/adaptive-navigation/api/1.1.0-beta01.txt b/compose/material3/adaptive/adaptive-navigation/api/1.1.0-beta01.txt
new file mode 100644
index 0000000..ba0712b
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-navigation/api/1.1.0-beta01.txt
@@ -0,0 +1,55 @@
+// Signature format: 4.0
+package androidx.compose.material3.adaptive.navigation {
+
+ public final class AndroidThreePaneScaffold_androidKt {
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static <T> void NavigableListDetailPaneScaffold(androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator<T> navigator, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> listPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> detailPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit>? extraPane, optional String defaultBackBehavior, optional kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.layout.PaneExpansionState,kotlin.Unit>? paneExpansionDragHandle, optional androidx.compose.material3.adaptive.layout.PaneExpansionState? paneExpansionState);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static <T> void NavigableSupportingPaneScaffold(androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator<T> navigator, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> mainPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> supportingPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit>? extraPane, optional String defaultBackBehavior, optional kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.layout.PaneExpansionState,kotlin.Unit>? paneExpansionDragHandle, optional androidx.compose.material3.adaptive.layout.PaneExpansionState? paneExpansionState);
+ }
+
+ @kotlin.jvm.JvmInline public final value class BackNavigationBehavior {
+ field public static final androidx.compose.material3.adaptive.navigation.BackNavigationBehavior.Companion Companion;
+ }
+
+ public static final class BackNavigationBehavior.Companion {
+ method public String getPopLatest();
+ method public String getPopUntilContentChange();
+ method public String getPopUntilCurrentDestinationChange();
+ method public String getPopUntilScaffoldValueChange();
+ property public final String PopLatest;
+ property public final String PopUntilContentChange;
+ property public final String PopUntilCurrentDestinationChange;
+ property public final String PopUntilScaffoldValueChange;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Stable public interface ThreePaneScaffoldNavigator<T> {
+ method public boolean canNavigateBack(optional String backNavigationBehavior);
+ method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldDestinationItem<T>? getCurrentDestination();
+ method public androidx.compose.material3.adaptive.layout.PaneScaffoldDirective getScaffoldDirective();
+ method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldState getScaffoldState();
+ method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue getScaffoldValue();
+ method public boolean isDestinationHistoryAware();
+ method public suspend Object? navigateBack(optional String backNavigationBehavior, kotlin.coroutines.Continuation<? super java.lang.Boolean>);
+ method public suspend Object? navigateTo(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole pane, optional T? contentKey, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue peekPreviousScaffoldValue(optional String backNavigationBehavior);
+ method public suspend Object? seekBack(optional String backNavigationBehavior, optional @FloatRange(from=0.0, to=1.0) float fraction, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public void setDestinationHistoryAware(boolean);
+ property public abstract androidx.compose.material3.adaptive.layout.ThreePaneScaffoldDestinationItem<T>? currentDestination;
+ property public abstract boolean isDestinationHistoryAware;
+ property public abstract androidx.compose.material3.adaptive.layout.PaneScaffoldDirective scaffoldDirective;
+ property public abstract androidx.compose.material3.adaptive.layout.ThreePaneScaffoldState scaffoldState;
+ property public abstract androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue scaffoldValue;
+ }
+
+ public final class ThreePaneScaffoldNavigatorKt {
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator<java.lang.Object> rememberListDetailPaneScaffoldNavigator(optional androidx.compose.material3.adaptive.layout.PaneScaffoldDirective scaffoldDirective, optional androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional boolean isDestinationHistoryAware);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static <T> androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator<T> rememberListDetailPaneScaffoldNavigator(optional androidx.compose.material3.adaptive.layout.PaneScaffoldDirective scaffoldDirective, optional androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional boolean isDestinationHistoryAware, optional java.util.List<? extends androidx.compose.material3.adaptive.layout.ThreePaneScaffoldDestinationItem<? extends T>> initialDestinationHistory);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator<java.lang.Object> rememberSupportingPaneScaffoldNavigator(optional androidx.compose.material3.adaptive.layout.PaneScaffoldDirective scaffoldDirective, optional androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional boolean isDestinationHistoryAware);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static <T> androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator<T> rememberSupportingPaneScaffoldNavigator(optional androidx.compose.material3.adaptive.layout.PaneScaffoldDirective scaffoldDirective, optional androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional boolean isDestinationHistoryAware, optional java.util.List<? extends androidx.compose.material3.adaptive.layout.ThreePaneScaffoldDestinationItem<? extends T>> initialDestinationHistory);
+ }
+
+ public final class ThreePaneScaffoldPredictiveBackHandler_androidKt {
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static <T> void ThreePaneScaffoldPredictiveBackHandler(androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator<T> navigator, String backBehavior);
+ }
+
+}
+
diff --git a/compose/material3/adaptive/adaptive-navigation/api/res-1.1.0-beta01.txt b/compose/material3/adaptive/adaptive-navigation/api/res-1.1.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-navigation/api/res-1.1.0-beta01.txt
diff --git a/compose/material3/adaptive/adaptive-navigation/api/restricted_1.1.0-beta01.txt b/compose/material3/adaptive/adaptive-navigation/api/restricted_1.1.0-beta01.txt
new file mode 100644
index 0000000..ba0712b
--- /dev/null
+++ b/compose/material3/adaptive/adaptive-navigation/api/restricted_1.1.0-beta01.txt
@@ -0,0 +1,55 @@
+// Signature format: 4.0
+package androidx.compose.material3.adaptive.navigation {
+
+ public final class AndroidThreePaneScaffold_androidKt {
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static <T> void NavigableListDetailPaneScaffold(androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator<T> navigator, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> listPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> detailPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit>? extraPane, optional String defaultBackBehavior, optional kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.layout.PaneExpansionState,kotlin.Unit>? paneExpansionDragHandle, optional androidx.compose.material3.adaptive.layout.PaneExpansionState? paneExpansionState);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static <T> void NavigableSupportingPaneScaffold(androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator<T> navigator, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> mainPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit> supportingPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,kotlin.Unit>? extraPane, optional String defaultBackBehavior, optional kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.layout.PaneExpansionState,kotlin.Unit>? paneExpansionDragHandle, optional androidx.compose.material3.adaptive.layout.PaneExpansionState? paneExpansionState);
+ }
+
+ @kotlin.jvm.JvmInline public final value class BackNavigationBehavior {
+ field public static final androidx.compose.material3.adaptive.navigation.BackNavigationBehavior.Companion Companion;
+ }
+
+ public static final class BackNavigationBehavior.Companion {
+ method public String getPopLatest();
+ method public String getPopUntilContentChange();
+ method public String getPopUntilCurrentDestinationChange();
+ method public String getPopUntilScaffoldValueChange();
+ property public final String PopLatest;
+ property public final String PopUntilContentChange;
+ property public final String PopUntilCurrentDestinationChange;
+ property public final String PopUntilScaffoldValueChange;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Stable public interface ThreePaneScaffoldNavigator<T> {
+ method public boolean canNavigateBack(optional String backNavigationBehavior);
+ method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldDestinationItem<T>? getCurrentDestination();
+ method public androidx.compose.material3.adaptive.layout.PaneScaffoldDirective getScaffoldDirective();
+ method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldState getScaffoldState();
+ method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue getScaffoldValue();
+ method public boolean isDestinationHistoryAware();
+ method public suspend Object? navigateBack(optional String backNavigationBehavior, kotlin.coroutines.Continuation<? super java.lang.Boolean>);
+ method public suspend Object? navigateTo(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole pane, optional T? contentKey, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue peekPreviousScaffoldValue(optional String backNavigationBehavior);
+ method public suspend Object? seekBack(optional String backNavigationBehavior, optional @FloatRange(from=0.0, to=1.0) float fraction, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public void setDestinationHistoryAware(boolean);
+ property public abstract androidx.compose.material3.adaptive.layout.ThreePaneScaffoldDestinationItem<T>? currentDestination;
+ property public abstract boolean isDestinationHistoryAware;
+ property public abstract androidx.compose.material3.adaptive.layout.PaneScaffoldDirective scaffoldDirective;
+ property public abstract androidx.compose.material3.adaptive.layout.ThreePaneScaffoldState scaffoldState;
+ property public abstract androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue scaffoldValue;
+ }
+
+ public final class ThreePaneScaffoldNavigatorKt {
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator<java.lang.Object> rememberListDetailPaneScaffoldNavigator(optional androidx.compose.material3.adaptive.layout.PaneScaffoldDirective scaffoldDirective, optional androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional boolean isDestinationHistoryAware);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static <T> androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator<T> rememberListDetailPaneScaffoldNavigator(optional androidx.compose.material3.adaptive.layout.PaneScaffoldDirective scaffoldDirective, optional androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional boolean isDestinationHistoryAware, optional java.util.List<? extends androidx.compose.material3.adaptive.layout.ThreePaneScaffoldDestinationItem<? extends T>> initialDestinationHistory);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator<java.lang.Object> rememberSupportingPaneScaffoldNavigator(optional androidx.compose.material3.adaptive.layout.PaneScaffoldDirective scaffoldDirective, optional androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional boolean isDestinationHistoryAware);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static <T> androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator<T> rememberSupportingPaneScaffoldNavigator(optional androidx.compose.material3.adaptive.layout.PaneScaffoldDirective scaffoldDirective, optional androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional boolean isDestinationHistoryAware, optional java.util.List<? extends androidx.compose.material3.adaptive.layout.ThreePaneScaffoldDestinationItem<? extends T>> initialDestinationHistory);
+ }
+
+ public final class ThreePaneScaffoldPredictiveBackHandler_androidKt {
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static <T> void ThreePaneScaffoldPredictiveBackHandler(androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator<T> navigator, String backBehavior);
+ }
+
+}
+
diff --git a/compose/material3/adaptive/adaptive/api/1.1.0-beta01.txt b/compose/material3/adaptive/adaptive/api/1.1.0-beta01.txt
new file mode 100644
index 0000000..a93216b
--- /dev/null
+++ b/compose/material3/adaptive/adaptive/api/1.1.0-beta01.txt
@@ -0,0 +1,62 @@
+// Signature format: 4.0
+package androidx.compose.material3.adaptive {
+
+ public final class AndroidPosture_androidKt {
+ method public static androidx.compose.material3.adaptive.Posture calculatePosture(java.util.List<? extends androidx.window.layout.FoldingFeature> foldingFeatures);
+ }
+
+ public final class AndroidWindowAdaptiveInfo_androidKt {
+ method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<java.util.List<androidx.window.layout.FoldingFeature>> collectFoldingFeaturesAsState();
+ method @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.WindowAdaptiveInfo currentWindowAdaptiveInfo();
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static long currentWindowDpSize();
+ method @androidx.compose.runtime.Composable public static long currentWindowSize();
+ }
+
+ @SuppressCompatibility @kotlin.RequiresOptIn(message="This material3 adaptive API is experimental and is likely to change or to be" + "removed in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalMaterial3AdaptiveApi {
+ }
+
+ @SuppressCompatibility @kotlin.RequiresOptIn(message="This material3 adaptive API is experimental and is likely to change or to be" + "removed in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalMaterial3AdaptiveComponentOverrideApi {
+ }
+
+ @androidx.compose.runtime.Immutable public final class HingeInfo {
+ ctor public HingeInfo(androidx.compose.ui.geometry.Rect bounds, boolean isFlat, boolean isVertical, boolean isSeparating, boolean isOccluding);
+ method public androidx.compose.ui.geometry.Rect getBounds();
+ method public boolean isFlat();
+ method public boolean isOccluding();
+ method public boolean isSeparating();
+ method public boolean isVertical();
+ property public final androidx.compose.ui.geometry.Rect bounds;
+ property public final boolean isFlat;
+ property public final boolean isOccluding;
+ property public final boolean isSeparating;
+ property public final boolean isVertical;
+ }
+
+ @androidx.compose.runtime.Immutable public final class Posture {
+ ctor public Posture();
+ ctor public Posture(optional boolean isTabletop, optional java.util.List<androidx.compose.material3.adaptive.HingeInfo> hingeList);
+ method public java.util.List<androidx.compose.material3.adaptive.HingeInfo> getHingeList();
+ method public boolean isTabletop();
+ property public final java.util.List<androidx.compose.material3.adaptive.HingeInfo> hingeList;
+ property public final boolean isTabletop;
+ }
+
+ public final class PostureKt {
+ method public static java.util.List<androidx.compose.ui.geometry.Rect> getAllHorizontalHingeBounds(androidx.compose.material3.adaptive.Posture);
+ method public static java.util.List<androidx.compose.ui.geometry.Rect> getAllVerticalHingeBounds(androidx.compose.material3.adaptive.Posture);
+ method public static java.util.List<androidx.compose.ui.geometry.Rect> getOccludingHorizontalHingeBounds(androidx.compose.material3.adaptive.Posture);
+ method public static java.util.List<androidx.compose.ui.geometry.Rect> getOccludingVerticalHingeBounds(androidx.compose.material3.adaptive.Posture);
+ method public static java.util.List<androidx.compose.ui.geometry.Rect> getSeparatingHorizontalHingeBounds(androidx.compose.material3.adaptive.Posture);
+ method public static java.util.List<androidx.compose.ui.geometry.Rect> getSeparatingVerticalHingeBounds(androidx.compose.material3.adaptive.Posture);
+ }
+
+ @androidx.compose.runtime.Immutable public final class WindowAdaptiveInfo {
+ ctor public WindowAdaptiveInfo(androidx.window.core.layout.WindowSizeClass windowSizeClass, androidx.compose.material3.adaptive.Posture windowPosture);
+ method public androidx.compose.material3.adaptive.Posture getWindowPosture();
+ method public androidx.window.core.layout.WindowSizeClass getWindowSizeClass();
+ property public final androidx.compose.material3.adaptive.Posture windowPosture;
+ property public final androidx.window.core.layout.WindowSizeClass windowSizeClass;
+ }
+
+}
+
diff --git a/compose/material3/adaptive/adaptive/api/res-1.1.0-beta01.txt b/compose/material3/adaptive/adaptive/api/res-1.1.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/compose/material3/adaptive/adaptive/api/res-1.1.0-beta01.txt
diff --git a/compose/material3/adaptive/adaptive/api/restricted_1.1.0-beta01.txt b/compose/material3/adaptive/adaptive/api/restricted_1.1.0-beta01.txt
new file mode 100644
index 0000000..a93216b
--- /dev/null
+++ b/compose/material3/adaptive/adaptive/api/restricted_1.1.0-beta01.txt
@@ -0,0 +1,62 @@
+// Signature format: 4.0
+package androidx.compose.material3.adaptive {
+
+ public final class AndroidPosture_androidKt {
+ method public static androidx.compose.material3.adaptive.Posture calculatePosture(java.util.List<? extends androidx.window.layout.FoldingFeature> foldingFeatures);
+ }
+
+ public final class AndroidWindowAdaptiveInfo_androidKt {
+ method @androidx.compose.runtime.Composable public static androidx.compose.runtime.State<java.util.List<androidx.window.layout.FoldingFeature>> collectFoldingFeaturesAsState();
+ method @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.WindowAdaptiveInfo currentWindowAdaptiveInfo();
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static long currentWindowDpSize();
+ method @androidx.compose.runtime.Composable public static long currentWindowSize();
+ }
+
+ @SuppressCompatibility @kotlin.RequiresOptIn(message="This material3 adaptive API is experimental and is likely to change or to be" + "removed in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalMaterial3AdaptiveApi {
+ }
+
+ @SuppressCompatibility @kotlin.RequiresOptIn(message="This material3 adaptive API is experimental and is likely to change or to be" + "removed in the future.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface ExperimentalMaterial3AdaptiveComponentOverrideApi {
+ }
+
+ @androidx.compose.runtime.Immutable public final class HingeInfo {
+ ctor public HingeInfo(androidx.compose.ui.geometry.Rect bounds, boolean isFlat, boolean isVertical, boolean isSeparating, boolean isOccluding);
+ method public androidx.compose.ui.geometry.Rect getBounds();
+ method public boolean isFlat();
+ method public boolean isOccluding();
+ method public boolean isSeparating();
+ method public boolean isVertical();
+ property public final androidx.compose.ui.geometry.Rect bounds;
+ property public final boolean isFlat;
+ property public final boolean isOccluding;
+ property public final boolean isSeparating;
+ property public final boolean isVertical;
+ }
+
+ @androidx.compose.runtime.Immutable public final class Posture {
+ ctor public Posture();
+ ctor public Posture(optional boolean isTabletop, optional java.util.List<androidx.compose.material3.adaptive.HingeInfo> hingeList);
+ method public java.util.List<androidx.compose.material3.adaptive.HingeInfo> getHingeList();
+ method public boolean isTabletop();
+ property public final java.util.List<androidx.compose.material3.adaptive.HingeInfo> hingeList;
+ property public final boolean isTabletop;
+ }
+
+ public final class PostureKt {
+ method public static java.util.List<androidx.compose.ui.geometry.Rect> getAllHorizontalHingeBounds(androidx.compose.material3.adaptive.Posture);
+ method public static java.util.List<androidx.compose.ui.geometry.Rect> getAllVerticalHingeBounds(androidx.compose.material3.adaptive.Posture);
+ method public static java.util.List<androidx.compose.ui.geometry.Rect> getOccludingHorizontalHingeBounds(androidx.compose.material3.adaptive.Posture);
+ method public static java.util.List<androidx.compose.ui.geometry.Rect> getOccludingVerticalHingeBounds(androidx.compose.material3.adaptive.Posture);
+ method public static java.util.List<androidx.compose.ui.geometry.Rect> getSeparatingHorizontalHingeBounds(androidx.compose.material3.adaptive.Posture);
+ method public static java.util.List<androidx.compose.ui.geometry.Rect> getSeparatingVerticalHingeBounds(androidx.compose.material3.adaptive.Posture);
+ }
+
+ @androidx.compose.runtime.Immutable public final class WindowAdaptiveInfo {
+ ctor public WindowAdaptiveInfo(androidx.window.core.layout.WindowSizeClass windowSizeClass, androidx.compose.material3.adaptive.Posture windowPosture);
+ method public androidx.compose.material3.adaptive.Posture getWindowPosture();
+ method public androidx.window.core.layout.WindowSizeClass getWindowSizeClass();
+ property public final androidx.compose.material3.adaptive.Posture windowPosture;
+ property public final androidx.window.core.layout.WindowSizeClass windowSizeClass;
+ }
+
+}
+
diff --git a/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/BottomAppBarBenchmark.kt b/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/BottomAppBarBenchmark.kt
index 96f3a4c..a589092 100644
--- a/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/BottomAppBarBenchmark.kt
+++ b/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/BottomAppBarBenchmark.kt
@@ -21,7 +21,6 @@
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.automirrored.filled.ArrowForward
import androidx.compose.material3.BottomAppBar
-import androidx.compose.material3.BottomAppBarDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon
@@ -80,7 +79,6 @@
@Composable
override fun MeasuredContent() {
BottomAppBar(
- horizontalArrangement = BottomAppBarDefaults.HorizontalArrangement,
modifier = Modifier.fillMaxWidth(),
) {
IconButton(onClick = { /* doSomething() */ }) {
diff --git a/compose/material3/material3/api/current.txt b/compose/material3/material3/api/current.txt
index 273f29f..b4c8ef0 100644
--- a/compose/material3/material3/api/current.txt
+++ b/compose/material3/material3/api/current.txt
@@ -34,7 +34,6 @@
}
public final class AppBarKt {
- method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void BottomAppBar(androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long contentColor, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.layout.WindowInsets windowInsets, optional androidx.compose.material3.BottomAppBarScrollBehavior? scrollBehavior, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void BottomAppBar(optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long contentColor, optional float tonalElevation, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.layout.WindowInsets windowInsets, optional androidx.compose.material3.BottomAppBarScrollBehavior? scrollBehavior, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void BottomAppBar(optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long contentColor, optional float tonalElevation, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.layout.WindowInsets windowInsets, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void BottomAppBar(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> actions, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? floatingActionButton, optional long containerColor, optional long contentColor, optional float tonalElevation, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.layout.WindowInsets windowInsets);
@@ -42,6 +41,7 @@
method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public static androidx.compose.material3.BottomAppBarState BottomAppBarState(float initialHeightOffsetLimit, float initialHeightOffset, float initialContentOffset);
method @Deprecated @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void CenterAlignedTopAppBar(kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> navigationIcon, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> actions, optional androidx.compose.foundation.layout.WindowInsets windowInsets, optional androidx.compose.material3.TopAppBarColors colors, optional androidx.compose.material3.TopAppBarScrollBehavior? scrollBehavior);
method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void CenterAlignedTopAppBar(kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> navigationIcon, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> actions, optional float expandedHeight, optional androidx.compose.foundation.layout.WindowInsets windowInsets, optional androidx.compose.material3.TopAppBarColors colors, optional androidx.compose.material3.TopAppBarScrollBehavior? scrollBehavior);
+ method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void FlexibleBottomAppBar(optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long contentColor, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional float expandedHeight, optional androidx.compose.foundation.layout.WindowInsets windowInsets, optional androidx.compose.material3.BottomAppBarScrollBehavior? scrollBehavior, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void LargeFlexibleTopAppBar(kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? subtitle, optional kotlin.jvm.functions.Function0<kotlin.Unit> navigationIcon, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> actions, optional androidx.compose.ui.Alignment.Horizontal titleHorizontalAlignment, optional float collapsedHeight, optional float expandedHeight, optional androidx.compose.foundation.layout.WindowInsets windowInsets, optional androidx.compose.material3.TopAppBarColors colors, optional androidx.compose.material3.TopAppBarScrollBehavior? scrollBehavior);
method @Deprecated @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void LargeTopAppBar(kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> navigationIcon, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> actions, optional androidx.compose.foundation.layout.WindowInsets windowInsets, optional androidx.compose.material3.TopAppBarColors colors, optional androidx.compose.material3.TopAppBarScrollBehavior? scrollBehavior);
method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void LargeTopAppBar(kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> navigationIcon, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> actions, optional float collapsedHeight, optional float expandedHeight, optional androidx.compose.foundation.layout.WindowInsets windowInsets, optional androidx.compose.material3.TopAppBarColors colors, optional androidx.compose.material3.TopAppBarScrollBehavior? scrollBehavior);
@@ -91,11 +91,17 @@
method @androidx.compose.runtime.Composable public long getContainerColor();
method public float getContainerElevation();
method public androidx.compose.foundation.layout.PaddingValues getContentPadding();
- method public androidx.compose.foundation.layout.Arrangement.Horizontal getHorizontalArrangement();
+ method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public float getFlexibleBottomAppBarHeight();
+ method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public androidx.compose.foundation.layout.PaddingValues getFlexibleContentPadding();
+ method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public androidx.compose.foundation.layout.Arrangement.Horizontal getFlexibleFixedHorizontalArrangement();
+ method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public androidx.compose.foundation.layout.Arrangement.Horizontal getFlexibleHorizontalArrangement();
method @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.WindowInsets getWindowInsets();
property public final float ContainerElevation;
property public final androidx.compose.foundation.layout.PaddingValues ContentPadding;
- property public final androidx.compose.foundation.layout.Arrangement.Horizontal HorizontalArrangement;
+ property @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public final float FlexibleBottomAppBarHeight;
+ property @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public final androidx.compose.foundation.layout.PaddingValues FlexibleContentPadding;
+ property @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public final androidx.compose.foundation.layout.Arrangement.Horizontal FlexibleFixedHorizontalArrangement;
+ property @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public final androidx.compose.foundation.layout.Arrangement.Horizontal FlexibleHorizontalArrangement;
property @androidx.compose.runtime.Composable public final long bottomAppBarFabColor;
property @androidx.compose.runtime.Composable public final long containerColor;
property @androidx.compose.runtime.Composable public final androidx.compose.foundation.layout.WindowInsets windowInsets;
@@ -396,11 +402,15 @@
public final class CheckboxDefaults {
method @androidx.compose.runtime.Composable public androidx.compose.material3.CheckboxColors colors();
method @androidx.compose.runtime.Composable public androidx.compose.material3.CheckboxColors colors(optional long checkedColor, optional long uncheckedColor, optional long checkmarkColor, optional long disabledCheckedColor, optional long disabledUncheckedColor, optional long disabledIndeterminateColor);
+ method public float getStrokeWidth();
+ property public final float StrokeWidth;
field public static final androidx.compose.material3.CheckboxDefaults INSTANCE;
}
public final class CheckboxKt {
+ method @androidx.compose.runtime.Composable public static void Checkbox(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit>? onCheckedChange, androidx.compose.ui.graphics.drawscope.Stroke checkmarkStroke, androidx.compose.ui.graphics.drawscope.Stroke outlineStroke, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.CheckboxColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
method @androidx.compose.runtime.Composable public static void Checkbox(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit>? onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.CheckboxColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
+ method @androidx.compose.runtime.Composable public static void TriStateCheckbox(androidx.compose.ui.state.ToggleableState state, kotlin.jvm.functions.Function0<kotlin.Unit>? onClick, androidx.compose.ui.graphics.drawscope.Stroke checkmarkStroke, androidx.compose.ui.graphics.drawscope.Stroke outlineStroke, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.CheckboxColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
method @androidx.compose.runtime.Composable public static void TriStateCheckbox(androidx.compose.ui.state.ToggleableState state, kotlin.jvm.functions.Function0<kotlin.Unit>? onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.CheckboxColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
}
diff --git a/compose/material3/material3/api/restricted_current.txt b/compose/material3/material3/api/restricted_current.txt
index 273f29f..b4c8ef0 100644
--- a/compose/material3/material3/api/restricted_current.txt
+++ b/compose/material3/material3/api/restricted_current.txt
@@ -34,7 +34,6 @@
}
public final class AppBarKt {
- method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void BottomAppBar(androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long contentColor, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.layout.WindowInsets windowInsets, optional androidx.compose.material3.BottomAppBarScrollBehavior? scrollBehavior, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void BottomAppBar(optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long contentColor, optional float tonalElevation, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.layout.WindowInsets windowInsets, optional androidx.compose.material3.BottomAppBarScrollBehavior? scrollBehavior, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void BottomAppBar(optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long contentColor, optional float tonalElevation, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.layout.WindowInsets windowInsets, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void BottomAppBar(kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> actions, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? floatingActionButton, optional long containerColor, optional long contentColor, optional float tonalElevation, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.layout.WindowInsets windowInsets);
@@ -42,6 +41,7 @@
method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api public static androidx.compose.material3.BottomAppBarState BottomAppBarState(float initialHeightOffsetLimit, float initialHeightOffset, float initialContentOffset);
method @Deprecated @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void CenterAlignedTopAppBar(kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> navigationIcon, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> actions, optional androidx.compose.foundation.layout.WindowInsets windowInsets, optional androidx.compose.material3.TopAppBarColors colors, optional androidx.compose.material3.TopAppBarScrollBehavior? scrollBehavior);
method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void CenterAlignedTopAppBar(kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> navigationIcon, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> actions, optional float expandedHeight, optional androidx.compose.foundation.layout.WindowInsets windowInsets, optional androidx.compose.material3.TopAppBarColors colors, optional androidx.compose.material3.TopAppBarScrollBehavior? scrollBehavior);
+ method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void FlexibleBottomAppBar(optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long contentColor, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.layout.Arrangement.Horizontal horizontalArrangement, optional float expandedHeight, optional androidx.compose.foundation.layout.WindowInsets windowInsets, optional androidx.compose.material3.BottomAppBarScrollBehavior? scrollBehavior, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void LargeFlexibleTopAppBar(kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? subtitle, optional kotlin.jvm.functions.Function0<kotlin.Unit> navigationIcon, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> actions, optional androidx.compose.ui.Alignment.Horizontal titleHorizontalAlignment, optional float collapsedHeight, optional float expandedHeight, optional androidx.compose.foundation.layout.WindowInsets windowInsets, optional androidx.compose.material3.TopAppBarColors colors, optional androidx.compose.material3.TopAppBarScrollBehavior? scrollBehavior);
method @Deprecated @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void LargeTopAppBar(kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> navigationIcon, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> actions, optional androidx.compose.foundation.layout.WindowInsets windowInsets, optional androidx.compose.material3.TopAppBarColors colors, optional androidx.compose.material3.TopAppBarScrollBehavior? scrollBehavior);
method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public static void LargeTopAppBar(kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit> navigationIcon, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> actions, optional float collapsedHeight, optional float expandedHeight, optional androidx.compose.foundation.layout.WindowInsets windowInsets, optional androidx.compose.material3.TopAppBarColors colors, optional androidx.compose.material3.TopAppBarScrollBehavior? scrollBehavior);
@@ -91,11 +91,17 @@
method @androidx.compose.runtime.Composable public long getContainerColor();
method public float getContainerElevation();
method public androidx.compose.foundation.layout.PaddingValues getContentPadding();
- method public androidx.compose.foundation.layout.Arrangement.Horizontal getHorizontalArrangement();
+ method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public float getFlexibleBottomAppBarHeight();
+ method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public androidx.compose.foundation.layout.PaddingValues getFlexibleContentPadding();
+ method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public androidx.compose.foundation.layout.Arrangement.Horizontal getFlexibleFixedHorizontalArrangement();
+ method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public androidx.compose.foundation.layout.Arrangement.Horizontal getFlexibleHorizontalArrangement();
method @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.WindowInsets getWindowInsets();
property public final float ContainerElevation;
property public final androidx.compose.foundation.layout.PaddingValues ContentPadding;
- property public final androidx.compose.foundation.layout.Arrangement.Horizontal HorizontalArrangement;
+ property @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public final float FlexibleBottomAppBarHeight;
+ property @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public final androidx.compose.foundation.layout.PaddingValues FlexibleContentPadding;
+ property @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public final androidx.compose.foundation.layout.Arrangement.Horizontal FlexibleFixedHorizontalArrangement;
+ property @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public final androidx.compose.foundation.layout.Arrangement.Horizontal FlexibleHorizontalArrangement;
property @androidx.compose.runtime.Composable public final long bottomAppBarFabColor;
property @androidx.compose.runtime.Composable public final long containerColor;
property @androidx.compose.runtime.Composable public final androidx.compose.foundation.layout.WindowInsets windowInsets;
@@ -396,11 +402,15 @@
public final class CheckboxDefaults {
method @androidx.compose.runtime.Composable public androidx.compose.material3.CheckboxColors colors();
method @androidx.compose.runtime.Composable public androidx.compose.material3.CheckboxColors colors(optional long checkedColor, optional long uncheckedColor, optional long checkmarkColor, optional long disabledCheckedColor, optional long disabledUncheckedColor, optional long disabledIndeterminateColor);
+ method public float getStrokeWidth();
+ property public final float StrokeWidth;
field public static final androidx.compose.material3.CheckboxDefaults INSTANCE;
}
public final class CheckboxKt {
+ method @androidx.compose.runtime.Composable public static void Checkbox(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit>? onCheckedChange, androidx.compose.ui.graphics.drawscope.Stroke checkmarkStroke, androidx.compose.ui.graphics.drawscope.Stroke outlineStroke, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.CheckboxColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
method @androidx.compose.runtime.Composable public static void Checkbox(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit>? onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.CheckboxColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
+ method @androidx.compose.runtime.Composable public static void TriStateCheckbox(androidx.compose.ui.state.ToggleableState state, kotlin.jvm.functions.Function0<kotlin.Unit>? onClick, androidx.compose.ui.graphics.drawscope.Stroke checkmarkStroke, androidx.compose.ui.graphics.drawscope.Stroke outlineStroke, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.CheckboxColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
method @androidx.compose.runtime.Composable public static void TriStateCheckbox(androidx.compose.ui.state.ToggleableState state, kotlin.jvm.functions.Function0<kotlin.Unit>? onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.CheckboxColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource);
}
diff --git a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Examples.kt b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Examples.kt
index 6ab1a76..16faf52 100644
--- a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Examples.kt
+++ b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Examples.kt
@@ -43,6 +43,7 @@
import androidx.compose.material3.samples.CardSample
import androidx.compose.material3.samples.CenteredHorizontalFloatingToolbarWithFabSample
import androidx.compose.material3.samples.CenteredVerticalFloatingToolbarWithFabSample
+import androidx.compose.material3.samples.CheckboxRoundedStrokesSample
import androidx.compose.material3.samples.CheckboxSample
import androidx.compose.material3.samples.CheckboxWithTextSample
import androidx.compose.material3.samples.ChipGroupReflowSample
@@ -248,6 +249,7 @@
import androidx.compose.material3.samples.ToggleButtonWithIconSample
import androidx.compose.material3.samples.TonalSplitButtonSample
import androidx.compose.material3.samples.TonalToggleButtonSample
+import androidx.compose.material3.samples.TriStateCheckboxRoundedStrokesSample
import androidx.compose.material3.samples.TriStateCheckboxSample
import androidx.compose.material3.samples.TwoLineListItem
import androidx.compose.material3.samples.VerticalFloatingToolbarWithFabSample
@@ -585,12 +587,26 @@
CheckboxWithTextSample()
},
Example(
+ name = "CheckboxRoundedStrokesSample",
+ description = CheckboxesExampleDescription,
+ sourceUrl = CheckboxesExampleSourceUrl
+ ) {
+ CheckboxRoundedStrokesSample()
+ },
+ Example(
name = "TriStateCheckboxSample",
description = CheckboxesExampleDescription,
sourceUrl = CheckboxesExampleSourceUrl
) {
TriStateCheckboxSample()
- }
+ },
+ Example(
+ name = "TriStateCheckboxRoundedStrokesSample",
+ description = CheckboxesExampleDescription,
+ sourceUrl = CheckboxesExampleSourceUrl
+ ) {
+ TriStateCheckboxRoundedStrokesSample()
+ },
)
private const val ChipsExampleDescription = "Chips examples"
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/AppBarSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/AppBarSamples.kt
index 3c08716..1986cce 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/AppBarSamples.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/AppBarSamples.kt
@@ -44,6 +44,7 @@
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.FabPosition
import androidx.compose.material3.FilledIconButton
+import androidx.compose.material3.FlexibleBottomAppBar
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.FloatingActionButtonDefaults
import androidx.compose.material3.Icon
@@ -784,8 +785,8 @@
}
/**
- * A sample for a [BottomAppBar] that collapses when the content is scrolled up, and appears when
- * the content scrolled down. The content is spaced around.
+ * A sample for a [FlexibleBottomAppBar] that collapses when the content is scrolled up, and appears
+ * when the content scrolled down. The content is spaced around.
*/
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@Preview
@@ -801,7 +802,7 @@
Scaffold(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
bottomBar = {
- BottomAppBar(
+ FlexibleBottomAppBar(
horizontalArrangement = Arrangement.SpaceAround,
contentPadding = PaddingValues(horizontal = 0.dp),
scrollBehavior = if (!isTouchExplorationEnabled) scrollBehavior else null,
@@ -852,8 +853,8 @@
}
/**
- * A sample for a [BottomAppBar] that collapses when the content is scrolled up, and appears when
- * the content scrolled down. The content is spaced between.
+ * A sample for a [FlexibleBottomAppBar] that collapses when the content is scrolled up, and appears
+ * when the content scrolled down. The content is spaced between.
*/
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@Preview
@@ -869,7 +870,7 @@
Scaffold(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
bottomBar = {
- BottomAppBar(
+ FlexibleBottomAppBar(
horizontalArrangement = Arrangement.SpaceBetween,
scrollBehavior = if (!isTouchExplorationEnabled) scrollBehavior else null,
content = {
@@ -919,8 +920,8 @@
}
/**
- * A sample for a [BottomAppBar] that collapses when the content is scrolled up, and appears when
- * the content scrolled down. The content is spaced evenly.
+ * A sample for a [FlexibleBottomAppBar] that collapses when the content is scrolled up, and appears
+ * when the content scrolled down. The content is spaced evenly.
*/
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@Preview
@@ -936,7 +937,7 @@
Scaffold(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
bottomBar = {
- BottomAppBar(
+ FlexibleBottomAppBar(
horizontalArrangement = Arrangement.SpaceEvenly,
contentPadding = PaddingValues(horizontal = 0.dp),
scrollBehavior = if (!isTouchExplorationEnabled) scrollBehavior else null,
@@ -987,8 +988,8 @@
}
/**
- * A sample for a [BottomAppBar] that collapses when the content is scrolled up, and appears when
- * the content scrolled down. The content arrangement is fixed.
+ * A sample for a [FlexibleBottomAppBar] that collapses when the content is scrolled up, and appears
+ * when the content scrolled down. The content arrangement is fixed.
*/
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@Preview
@@ -1004,8 +1005,8 @@
Scaffold(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
bottomBar = {
- BottomAppBar(
- horizontalArrangement = BottomAppBarDefaults.HorizontalArrangement,
+ FlexibleBottomAppBar(
+ horizontalArrangement = BottomAppBarDefaults.FlexibleFixedHorizontalArrangement,
scrollBehavior = if (!isTouchExplorationEnabled) scrollBehavior else null,
content = {
IconButton(onClick = { /* doSomething() */ }) {
@@ -1054,8 +1055,8 @@
}
/**
- * A sample for a vibrant [BottomAppBar] that collapses when the content is scrolled up, and appears
- * when the content scrolled down. The content arrangement is fixed.
+ * A sample for a vibrant [FlexibleBottomAppBar] that collapses when the content is scrolled up, and
+ * appears when the content scrolled down. The content arrangement is fixed.
*/
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@Preview
@@ -1071,8 +1072,8 @@
Scaffold(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
bottomBar = {
- BottomAppBar(
- horizontalArrangement = BottomAppBarDefaults.HorizontalArrangement,
+ FlexibleBottomAppBar(
+ horizontalArrangement = BottomAppBarDefaults.FlexibleFixedHorizontalArrangement,
scrollBehavior = if (!isTouchExplorationEnabled) scrollBehavior else null,
containerColor =
MaterialTheme.colorScheme.primaryContainer, // TODO(b/356885344): tokens
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/CheckboxSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/CheckboxSamples.kt
index c15860d..61d6130 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/CheckboxSamples.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/CheckboxSamples.kt
@@ -27,6 +27,7 @@
import androidx.compose.foundation.selection.toggleable
import androidx.compose.foundation.selection.triStateToggleable
import androidx.compose.material3.Checkbox
+import androidx.compose.material3.CheckboxDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TriStateCheckbox
@@ -35,10 +36,15 @@
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.StrokeCap
+import androidx.compose.ui.graphics.StrokeJoin
+import androidx.compose.ui.graphics.drawscope.Stroke
+import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.state.ToggleableState
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
+import kotlin.math.floor
@Preview
@Sampled
@@ -79,6 +85,29 @@
@Preview
@Sampled
@Composable
+fun CheckboxRoundedStrokesSample() {
+ val strokeWidthPx = with(LocalDensity.current) { floor(CheckboxDefaults.StrokeWidth.toPx()) }
+ val checkmarkStroke =
+ remember(strokeWidthPx) {
+ Stroke(
+ width = strokeWidthPx,
+ cap = StrokeCap.Round,
+ join = StrokeJoin.Round,
+ )
+ }
+ val outlineStroke = remember(strokeWidthPx) { Stroke(width = strokeWidthPx) }
+ val checkedState = remember { mutableStateOf(true) }
+ Checkbox(
+ checked = checkedState.value,
+ onCheckedChange = { checkedState.value = it },
+ checkmarkStroke = checkmarkStroke,
+ outlineStroke = outlineStroke
+ )
+}
+
+@Preview
+@Sampled
+@Composable
fun TriStateCheckboxSample() {
Column {
// define dependent checkboxes states
@@ -146,3 +175,96 @@
}
}
}
+
+@Preview
+@Sampled
+@Composable
+fun TriStateCheckboxRoundedStrokesSample() {
+ val strokeWidthPx = with(LocalDensity.current) { floor(CheckboxDefaults.StrokeWidth.toPx()) }
+ val checkmarkStroke =
+ remember(strokeWidthPx) {
+ Stroke(
+ width = strokeWidthPx,
+ cap = StrokeCap.Round,
+ join = StrokeJoin.Round,
+ )
+ }
+ val outlineStroke = remember(strokeWidthPx) { Stroke(width = strokeWidthPx) }
+ Column {
+ // define dependent checkboxes states
+ val (state, onStateChange) = remember { mutableStateOf(true) }
+ val (state2, onStateChange2) = remember { mutableStateOf(true) }
+
+ // TriStateCheckbox state reflects state of dependent checkboxes
+ val parentState =
+ remember(state, state2) {
+ if (state && state2) ToggleableState.On
+ else if (!state && !state2) ToggleableState.Off else ToggleableState.Indeterminate
+ }
+ // click on TriStateCheckbox can set state for dependent checkboxes
+ val onParentClick = {
+ val s = parentState != ToggleableState.On
+ onStateChange(s)
+ onStateChange2(s)
+ }
+
+ // The sample below composes just basic checkboxes which are not fully accessible on their
+ // own. See the CheckboxWithTextSample as a way to ensure your checkboxes are fully
+ // accessible.
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ modifier =
+ Modifier.triStateToggleable(
+ state = parentState,
+ onClick = onParentClick,
+ role = Role.Checkbox
+ )
+ ) {
+ TriStateCheckbox(
+ state = parentState,
+ onClick = null,
+ checkmarkStroke = checkmarkStroke,
+ outlineStroke = outlineStroke
+ )
+ Text("Receive Emails")
+ }
+ Spacer(Modifier.size(25.dp))
+ Column(Modifier.padding(24.dp, 0.dp, 0.dp, 0.dp)) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ modifier =
+ Modifier.toggleable(
+ value = state,
+ onValueChange = onStateChange,
+ role = Role.Checkbox
+ )
+ ) {
+ Checkbox(
+ checked = state,
+ onCheckedChange = null,
+ checkmarkStroke = checkmarkStroke,
+ outlineStroke = outlineStroke
+ )
+ Text("Daily")
+ }
+ Spacer(Modifier.size(25.dp))
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ modifier =
+ Modifier.toggleable(
+ value = state2,
+ onValueChange = onStateChange2,
+ role = Role.Checkbox
+ )
+ ) {
+ Checkbox(
+ checked = state2,
+ onCheckedChange = null,
+ checkmarkStroke = checkmarkStroke,
+ outlineStroke = outlineStroke
+ )
+ Text("Weekly")
+ }
+ }
+ }
+}
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/AppBarScreenshotTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/AppBarScreenshotTest.kt
index 1a9d7b6..4b0b977 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/AppBarScreenshotTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/AppBarScreenshotTest.kt
@@ -657,7 +657,7 @@
fun bottomAppBarSpacedAround_lightTheme() {
composeTestRule.setMaterialContent(lightColorScheme()) {
Box(Modifier.testTag(BottomAppBarTestTag)) {
- BottomAppBar(
+ FlexibleBottomAppBar(
horizontalArrangement = Arrangement.SpaceAround,
contentPadding = PaddingValues(horizontal = 0.dp),
content = {
@@ -700,8 +700,9 @@
fun bottomAppBarSpacedBetween_lightTheme() {
composeTestRule.setMaterialContent(lightColorScheme()) {
Box(Modifier.testTag(BottomAppBarTestTag)) {
- BottomAppBar(
- horizontalArrangement = Arrangement.SpaceBetween,
+ FlexibleBottomAppBar(
+ // The default BottomAppBarDefaults.FlexibleHorizontalArrangement is an
+ // Arrangement.SpacedBetween.
content = {
IconButton(onClick = { /* doSomething() */ }) {
Icon(
@@ -742,7 +743,7 @@
fun bottomAppBarSpacedEvenly_lightTheme() {
composeTestRule.setMaterialContent(lightColorScheme()) {
Box(Modifier.testTag(BottomAppBarTestTag)) {
- BottomAppBar(
+ FlexibleBottomAppBar(
horizontalArrangement = Arrangement.SpaceEvenly,
contentPadding = PaddingValues(horizontal = 0.dp),
content = {
@@ -785,8 +786,8 @@
fun bottomAppBarFixed_lightTheme() {
composeTestRule.setMaterialContent(lightColorScheme()) {
Box(Modifier.testTag(BottomAppBarTestTag)) {
- BottomAppBar(
- horizontalArrangement = BottomAppBarDefaults.HorizontalArrangement,
+ FlexibleBottomAppBar(
+ horizontalArrangement = BottomAppBarDefaults.FlexibleFixedHorizontalArrangement,
content = {
IconButton(onClick = { /* doSomething() */ }) {
Icon(
@@ -827,8 +828,8 @@
fun bottomAppBarFixed_darkTheme() {
composeTestRule.setMaterialContent(darkColorScheme()) {
Box(Modifier.testTag(BottomAppBarTestTag)) {
- BottomAppBar(
- horizontalArrangement = BottomAppBarDefaults.HorizontalArrangement,
+ FlexibleBottomAppBar(
+ horizontalArrangement = BottomAppBarDefaults.FlexibleFixedHorizontalArrangement,
content = {
IconButton(onClick = { /* doSomething() */ }) {
Icon(
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/AppBarTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/AppBarTest.kt
index 71612d1..94eb5d0 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/AppBarTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/AppBarTest.kt
@@ -1852,12 +1852,27 @@
fun bottomAppBarWithCustomArrangement_heightIsFromSpec() {
rule
.setMaterialContentForSizeAssertions {
- BottomAppBar(
- horizontalArrangement = BottomAppBarDefaults.HorizontalArrangement,
+ FlexibleBottomAppBar(
+ horizontalArrangement = BottomAppBarDefaults.FlexibleFixedHorizontalArrangement,
content = {}
)
}
- .assertHeightIsEqualTo(64.dp) // TODO tokens
+ .assertHeightIsEqualTo(BottomAppBarDefaults.FlexibleBottomAppBarHeight)
+ .assertWidthIsEqualTo(rule.rootWidth())
+ }
+
+ @Test
+ fun bottomAppBarWithCustomHeight() {
+ val height = 128.dp
+ rule
+ .setMaterialContentForSizeAssertions {
+ FlexibleBottomAppBar(
+ horizontalArrangement = BottomAppBarDefaults.FlexibleFixedHorizontalArrangement,
+ expandedHeight = height,
+ content = {}
+ )
+ }
+ .assertHeightIsEqualTo(height)
.assertWidthIsEqualTo(rule.rootWidth())
}
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/CheckboxScreenshotTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/CheckboxScreenshotTest.kt
index 7a2851b..e54b4b2 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/CheckboxScreenshotTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/CheckboxScreenshotTest.kt
@@ -29,8 +29,12 @@
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.StrokeCap
+import androidx.compose.ui.graphics.StrokeJoin
+import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.input.InputMode
import androidx.compose.ui.input.InputModeManager
+import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalInputModeManager
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.state.ToggleableState
@@ -311,6 +315,73 @@
assertToggeableAgainstGolden("checkBox_${scheme.name}_customCheckboxColorsConstruct")
}
+ @Test
+ fun checkBox_customStroke_checked() {
+ rule.setMaterialContent(scheme.colorScheme) {
+ val stroke =
+ Stroke(
+ width = with(LocalDensity.current) { CheckboxDefaults.StrokeWidth.toPx() },
+ cap = StrokeCap.Round,
+ join = StrokeJoin.Round
+ )
+ Box(wrap.testTag(wrapperTestTag)) {
+ Checkbox(
+ modifier = wrap,
+ checked = true,
+ onCheckedChange = {},
+ checkmarkStroke = stroke,
+ outlineStroke = stroke
+ )
+ }
+ }
+ assertToggeableAgainstGolden("checkBox_${scheme.name}_customStroke_checked")
+ }
+
+ @Test
+ fun checkBox_customStroke_unchecked() {
+ rule.setMaterialContent(scheme.colorScheme) {
+ // Have the stroke thinner so we can verify it's being applied to the rounded box.
+ val stroke =
+ Stroke(
+ width = with(LocalDensity.current) { CheckboxDefaults.StrokeWidth.toPx() / 4 },
+ cap = StrokeCap.Round,
+ join = StrokeJoin.Round
+ )
+ Box(wrap.testTag(wrapperTestTag)) {
+ Checkbox(
+ modifier = wrap,
+ checked = false,
+ onCheckedChange = {},
+ checkmarkStroke = stroke,
+ outlineStroke = stroke
+ )
+ }
+ }
+ assertToggeableAgainstGolden("checkBox_${scheme.name}_customStroke_unchecked")
+ }
+
+ @Test
+ fun checkBox_customStroke_indeterminate() {
+ rule.setMaterialContent(scheme.colorScheme) {
+ val stroke =
+ Stroke(
+ width = with(LocalDensity.current) { CheckboxDefaults.StrokeWidth.toPx() },
+ cap = StrokeCap.Round,
+ join = StrokeJoin.Round
+ )
+ Box(wrap.testTag(wrapperTestTag)) {
+ TriStateCheckbox(
+ state = ToggleableState.Indeterminate,
+ checkmarkStroke = stroke,
+ outlineStroke = stroke,
+ modifier = wrap,
+ onClick = {}
+ )
+ }
+ }
+ assertToggeableAgainstGolden("checkBox_${scheme.name}_customStroke_indeterminate")
+ }
+
@Composable
private fun Checkboxes(colors: CheckboxColors) {
TriStateCheckbox(state = ToggleableState.Off, onClick = {}, colors = colors)
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/CheckboxTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/CheckboxTest.kt
index 3b1550a..b979580 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/CheckboxTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/CheckboxTest.kt
@@ -27,6 +27,8 @@
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.drawscope.Stroke
+import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.semantics.SemanticsProperties
@@ -54,6 +56,7 @@
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performTouchInput
+import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
@@ -230,11 +233,72 @@
)
}
- @OptIn(ExperimentalMaterial3Api::class)
+ @Test
+ fun checkBoxTest_MaterialSize_WhenChecked_withThickStroke_withoutMinimumTouchTarget() {
+ materialSizeTestForValue(
+ checkboxValue = On,
+ clickable = false, // Ensure a minimum size with clickable false and no min touch target
+ minimumTouchTarget = false,
+ strokeWidth = CheckboxDefaults.StrokeWidth * 3
+ )
+ }
+
+ @Test
+ fun checkBoxTest_MaterialSize_WhenChecked_withThinStroke_withoutMinimumTouchTarget() {
+ materialSizeTestForValue(
+ checkboxValue = On,
+ clickable = false, // Ensure a minimum size with clickable false and no min touch target
+ minimumTouchTarget = false,
+ strokeWidth = CheckboxDefaults.StrokeWidth / 3
+ )
+ }
+
+ @Test
+ fun checkBoxTest_MaterialSize_WhenUnchecked_withThickStroke_withoutMinimumTouchTarget() {
+ materialSizeTestForValue(
+ checkboxValue = Off,
+ clickable = false, // Ensure a minimum size with clickable false and no min touch target
+ minimumTouchTarget = false,
+ strokeWidth = CheckboxDefaults.StrokeWidth * 3
+ )
+ }
+
+ @Test
+ fun checkBoxTest_MaterialSize_WhenUnchecked_withThinStroke_withoutMinimumTouchTarget() {
+ materialSizeTestForValue(
+ checkboxValue = Off,
+ clickable = false, // Ensure a minimum size with clickable false and no min touch target
+ minimumTouchTarget = false,
+ strokeWidth = CheckboxDefaults.StrokeWidth / 3
+ )
+ }
+
+ @Test
+ fun checkBoxTest_MaterialSize_WhenIndeterminate_withThickStroke_withoutMinimumTouchTarget() {
+ materialSizeTestForValue(
+ checkboxValue = Indeterminate,
+ clickable = false, // Ensure a minimum size with clickable false and no min touch target
+ minimumTouchTarget = false,
+ strokeWidth = CheckboxDefaults.StrokeWidth * 3
+ )
+ }
+
+ @Test
+ fun checkBoxTest_MaterialSize_WhenIndeterminate_withThinStroke_withoutMinimumTouchTarget() {
+ materialSizeTestForValue(
+ checkboxValue = Indeterminate,
+ clickable = false, // Ensure a minimum size with clickable false and no min touch target
+ minimumTouchTarget = false,
+ strokeWidth = CheckboxDefaults.StrokeWidth / 3
+ )
+ }
+
+ @OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
private fun materialSizeTestForValue(
checkboxValue: ToggleableState,
clickable: Boolean,
- minimumTouchTarget: Boolean
+ minimumTouchTarget: Boolean,
+ strokeWidth: Dp? = null,
) {
rule
.setMaterialContentForSizeAssertions {
@@ -242,14 +306,29 @@
LocalMinimumInteractiveComponentSize provides
if (minimumTouchTarget) 48.dp else 0.dp
) {
- TriStateCheckbox(
- state = checkboxValue,
- onClick =
- if (clickable) {
- {}
- } else null,
- enabled = false
- )
+ if (strokeWidth == null) {
+ TriStateCheckbox(
+ state = checkboxValue,
+ onClick =
+ if (clickable) {
+ {}
+ } else null,
+ enabled = false
+ )
+ } else {
+ val strokeWidthPx =
+ Stroke(width = with(LocalDensity.current) { strokeWidth.toPx() })
+ TriStateCheckbox(
+ state = checkboxValue,
+ onClick =
+ if (clickable) {
+ {}
+ } else null,
+ enabled = false,
+ checkmarkStroke = strokeWidthPx,
+ outlineStroke = strokeWidthPx
+ )
+ }
}
}
.run {
diff --git a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/internal/AccessibilityServiceStateProvider.android.kt b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/internal/AccessibilityServiceStateProvider.android.kt
index 12c544b..4c9bf48 100644
--- a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/internal/AccessibilityServiceStateProvider.android.kt
+++ b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/internal/AccessibilityServiceStateProvider.android.kt
@@ -43,14 +43,23 @@
internal actual fun rememberAccessibilityServiceState(
listenToTouchExplorationState: Boolean,
listenToSwitchAccessState: Boolean,
+ listenToVoiceAccessState: Boolean,
): State<Boolean> {
val context = LocalContext.current
val accessibilityManager =
context.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
val listener =
- remember(listenToTouchExplorationState, listenToSwitchAccessState) {
- Listener(listenToTouchExplorationState, listenToSwitchAccessState)
+ remember(
+ listenToTouchExplorationState,
+ listenToSwitchAccessState,
+ listenToVoiceAccessState,
+ ) {
+ Listener(
+ listenToTouchExplorationState = listenToTouchExplorationState,
+ listenToSwitchAccessState = listenToSwitchAccessState,
+ listenToVoiceAccessState = listenToVoiceAccessState,
+ )
}
ObserveState(
@@ -85,7 +94,8 @@
@Stable
private class Listener(
listenToTouchExplorationState: Boolean,
- listenToSwitchAccessState: Boolean,
+ val listenToSwitchAccessState: Boolean,
+ val listenToVoiceAccessState: Boolean,
) : AccessibilityStateChangeListener, State<Boolean> {
private var accessibilityEnabled by mutableStateOf(false)
@@ -102,13 +112,18 @@
null
}
- private val switchAccessListener =
- if (listenToSwitchAccessState && Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ private val otherA11yServicesListener =
+ if (
+ (listenToSwitchAccessState || listenToVoiceAccessState) &&
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
+ ) {
object : AccessibilityServicesStateChangeListener {
- var enabled by mutableStateOf(false)
+ var switchAccessEnabled by mutableStateOf(false)
+ var voiceAccessEnabled by mutableStateOf(false)
override fun onAccessibilityServicesStateChanged(am: AccessibilityManager) {
- enabled = am.switchAccessEnabled
+ switchAccessEnabled = am.switchAccessEnabled
+ voiceAccessEnabled = am.voiceAccessEnabled
}
}
} else {
@@ -118,14 +133,25 @@
private val AccessibilityManager.switchAccessEnabled
get() =
getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_GENERIC).fastAny {
- it.settingsActivityName?.contains(SwitchAccessActivityName) == true
+ it.settingsActivityName?.contains(SwitchAccessActivityName, ignoreCase = true) ==
+ true
+ }
+
+ private val AccessibilityManager.voiceAccessEnabled
+ get() =
+ getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_GENERIC).fastAny {
+ it.settingsActivityName?.contains(VoiceAccessActivityName, ignoreCase = true) ==
+ true
}
override val value: Boolean
get() =
accessibilityEnabled &&
- ((touchExplorationListener?.enabled ?: false) ||
- (switchAccessListener?.enabled ?: false))
+ ((touchExplorationListener?.enabled == true) ||
+ (listenToSwitchAccessState &&
+ otherA11yServicesListener?.switchAccessEnabled == true) ||
+ (listenToVoiceAccessState &&
+ otherA11yServicesListener?.voiceAccessEnabled == true))
override fun onAccessibilityStateChanged(enabled: Boolean) {
accessibilityEnabled = enabled
@@ -139,8 +165,9 @@
am.addTouchExplorationStateChangeListener(it)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
- switchAccessListener?.let {
- it.enabled = am.switchAccessEnabled
+ otherA11yServicesListener?.let {
+ it.switchAccessEnabled = am.switchAccessEnabled
+ it.voiceAccessEnabled = am.voiceAccessEnabled
Api33Impl.addAccessibilityServicesStateChangeListener(am, it)
}
}
@@ -150,7 +177,7 @@
am.removeAccessibilityStateChangeListener(this)
touchExplorationListener?.let { am.removeTouchExplorationStateChangeListener(it) }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
- switchAccessListener?.let {
+ otherA11yServicesListener?.let {
Api33Impl.removeAccessibilityServicesStateChangeListener(am, it)
}
}
@@ -177,3 +204,4 @@
}
private const val SwitchAccessActivityName = "SwitchAccess"
+private const val VoiceAccessActivityName = "VoiceAccess"
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/AppBar.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/AppBar.kt
index 610a237..04de614 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/AppBar.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/AppBar.kt
@@ -1050,7 +1050,7 @@
* <a href="https://m3.material.io/components/bottom-app-bar/overview" class="external"
* target="_blank">Material Design bottom app bar</a>.
*
- * A bottom app bar displays navigation and key actions at the bottom of mobile screens.
+ * A bottom app bar displays navigation and key actions at the bottom of small screens.
*
* 
@@ -1106,7 +1106,7 @@
* <a href="https://m3.material.io/components/bottom-app-bar/overview" class="external"
* target="_blank">Material Design bottom app bar</a>.
*
- * A bottom app bar displays navigation and key actions at the bottom of mobile screens.
+ * A bottom app bar displays navigation and key actions at the bottom of small screens.
*
* 
@@ -1186,7 +1186,7 @@
* <a href="https://m3.material.io/components/bottom-app-bar/overview" class="external"
* target="_blank">Material Design bottom app bar</a>.
*
- * A bottom app bar displays navigation and key actions at the bottom of mobile screens.
+ * A bottom app bar displays navigation and key actions at the bottom of small screens.
*
* 
@@ -1235,7 +1235,7 @@
* <a href="https://m3.material.io/components/bottom-app-bar/overview" class="external"
* target="_blank">Material Design bottom app bar</a>.
*
- * A bottom app bar displays navigation and key actions at the bottom of mobile screens.
+ * A bottom app bar displays navigation and key actions at the bottom of small screens.
*
* 
@@ -1288,14 +1288,16 @@
)
}
+// TODO missing image of the flexible bottom app bar.
/**
* <a href="https://m3.material.io/components/bottom-app-bar/overview" class="external"
- * target="_blank">Material Design bottom app bar</a>.
+ * target="_blank">Material Design flexible bottom app bar</a>.
*
- * A bottom app bar displays navigation and key actions at the bottom of mobile screens.
+ * A flexible bottom app bar displays navigation and key actions at the bottom of small screens.
*
- * 
+ * This variation of the Bottom app bar has a [horizontalArrangement] parameter for controlling the
+ * way the content is arranged. Also, it allows more flexibility in controlling the bar's expanded
+ * height with an [expandedHeight] value.
*
* If you are interested in displaying a [FloatingActionButton], consider using another overload
* that takes a [FloatingActionButton] parameter.
@@ -1311,7 +1313,6 @@
* @sample androidx.compose.material3.samples.ExitAlwaysBottomAppBarSpacedEvenly
* @sample androidx.compose.material3.samples.ExitAlwaysBottomAppBarFixed
* @sample androidx.compose.material3.samples.ExitAlwaysBottomAppBarFixedVibrant
- * @param horizontalArrangement the horizontal arrangement of the content.
* @param modifier the [Modifier] to be applied to this BottomAppBar
* @param containerColor the color used for the background of this BottomAppBar. Use
* [Color.Transparent] to have no color.
@@ -1319,6 +1320,13 @@
* the matching content color for [containerColor], or to the current [LocalContentColor] if
* [containerColor] is not a color from the theme.
* @param contentPadding the padding applied to the content of this BottomAppBar
+ * @param horizontalArrangement the horizontal arrangement of the content inside this BottomAppBar
+ * @param expandedHeight the maximum height this bottom bar can reach when fully expanded. If a
+ * [scrollBehavior] is provided, the bar might collapse or expand based on scrolling. In that
+ * case, this value sets the upper limit for the bar's height during expansion. This [Dp] value
+ * must be specified, finite, and greater than zero; otherwise,
+ * [BottomAppBarDefaults.FlexibleBottomAppBarHeight] will be used as a default. In case the
+ * [scrollBehavior] is `null`, this value will simply be the fixed height of the bottom bar.
* @param windowInsets a window insets that app bar will respect.
* @param scrollBehavior a [BottomAppBarScrollBehavior] which holds various offset values that will
* be applied by this bottom app bar to set up its height. A scroll behavior is designed to work
@@ -1330,23 +1338,30 @@
@OptIn(ExperimentalMaterial3Api::class)
@ExperimentalMaterial3ExpressiveApi
@Composable
-fun BottomAppBar(
- horizontalArrangement: Arrangement.Horizontal,
+fun FlexibleBottomAppBar(
modifier: Modifier = Modifier,
containerColor: Color = BottomAppBarDefaults.containerColor,
contentColor: Color = contentColorFor(containerColor),
- contentPadding: PaddingValues = PaddingValues(horizontal = 16.dp), // TODO tokens
+ contentPadding: PaddingValues = BottomAppBarDefaults.FlexibleContentPadding,
+ horizontalArrangement: Arrangement.Horizontal =
+ BottomAppBarDefaults.FlexibleHorizontalArrangement,
+ expandedHeight: Dp = BottomAppBarDefaults.FlexibleBottomAppBarHeight,
windowInsets: WindowInsets = BottomAppBarDefaults.windowInsets,
scrollBehavior: BottomAppBarScrollBehavior? = null,
content: @Composable RowScope.() -> Unit
) {
BottomAppBarLayout(
- containerHeight = 64.dp, // TODO tokens
+ containerHeight =
+ if (expandedHeight.isFinite && expandedHeight.isSpecified && expandedHeight > 0.dp) {
+ expandedHeight
+ } else {
+ BottomAppBarDefaults.FlexibleBottomAppBarHeight
+ },
horizontalArrangement = horizontalArrangement,
modifier = modifier,
containerColor = containerColor,
contentColor = contentColor,
- tonalElevation = BottomAppBarDefaults.ContainerElevation,
+ tonalElevation = AppBarTokens.ContainerElevation,
contentPadding = contentPadding,
windowInsets = windowInsets,
scrollBehavior = scrollBehavior,
@@ -2192,7 +2207,34 @@
val bottomAppBarFabColor: Color
@Composable get() = FabSecondaryContainerTokens.ContainerColor.value
- val HorizontalArrangement =
+ @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
+ @get:ExperimentalMaterial3ExpressiveApi
+ @ExperimentalMaterial3ExpressiveApi
+ /** Default padding used for [FlexibleBottomAppBar]. */
+ val FlexibleContentPadding = PaddingValues(horizontal = 16.dp) // TODO tokens
+
+ /**
+ * Default height of a flexible [FlexibleBottomAppBar]. The height here represents the height of
+ * the bottom app bar in its expanded state.
+ */
+ @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
+ @get:ExperimentalMaterial3ExpressiveApi
+ @ExperimentalMaterial3ExpressiveApi
+ val FlexibleBottomAppBarHeight = AppBarSmallTokens.ContainerHeight
+
+ /** A default [Arrangement] that will be used to space a [FlexibleBottomAppBar]'s content. */
+ @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
+ @get:ExperimentalMaterial3ExpressiveApi
+ @ExperimentalMaterial3ExpressiveApi
+ val FlexibleHorizontalArrangement: Arrangement.Horizontal = Arrangement.SpaceBetween
+
+ /**
+ * An [Arrangement] that will be used to space [FlexibleBottomAppBar]'s with a fixed spacing.
+ */
+ @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
+ @get:ExperimentalMaterial3ExpressiveApi
+ @ExperimentalMaterial3ExpressiveApi
+ val FlexibleFixedHorizontalArrangement: Arrangement.Horizontal =
Arrangement.spacedBy(32.dp, Alignment.CenterHorizontally) // TODO tokens
// TODO: note that this scroll behavior may impact assistive technologies making the component
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Checkbox.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Checkbox.kt
index 0fd3f81..1888a07 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Checkbox.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Checkbox.kt
@@ -48,6 +48,7 @@
import androidx.compose.ui.graphics.drawscope.Fill
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.graphics.takeOrElse
+import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.state.ToggleableState
import androidx.compose.ui.unit.dp
@@ -96,6 +97,7 @@
colors: CheckboxColors = CheckboxDefaults.colors(),
interactionSource: MutableInteractionSource? = null
) {
+ val strokeWidthPx = with(LocalDensity.current) { floor(CheckboxDefaults.StrokeWidth.toPx()) }
TriStateCheckbox(
state = ToggleableState(checked),
onClick =
@@ -104,6 +106,72 @@
} else {
null
},
+ checkmarkStroke = Stroke(width = strokeWidthPx, cap = StrokeCap.Square),
+ outlineStroke = Stroke(width = strokeWidthPx),
+ modifier = modifier,
+ enabled = enabled,
+ colors = colors,
+ interactionSource = interactionSource
+ )
+}
+
+/**
+ * <a href="https://m3.material.io/components/checkbox/overview" class="external"
+ * target="_blank">Material Design checkbox</a>.
+ *
+ * Checkboxes allow users to select one or more items from a set. Checkboxes can turn an option on
+ * or off.
+ *
+ * 
+ *
+ * This Checkbox function offers greater flexibility in visual customization. Using the [Stroke]
+ * parameters, you can control the appearance of both the checkmark and the box that surrounds it.
+ *
+ * A sample of a `Checkbox` that uses a [Stroke] with rounded [StrokeCap] and
+ * [androidx.compose.ui.graphics.StrokeJoin]:
+ *
+ * @sample androidx.compose.material3.samples.CheckboxRoundedStrokesSample
+ * @param checked whether this checkbox is checked or unchecked
+ * @param onCheckedChange called when this checkbox is clicked. If `null`, then this checkbox will
+ * not be interactable, unless something else handles its input events and updates its state.
+ * @param checkmarkStroke stroke for the checkmark.
+ * @param outlineStroke stroke for the checkmark's box outline. Note that this stroke is applied
+ * when drawing the outline's rounded rectangle, so attributions such as
+ * [androidx.compose.ui.graphics.StrokeJoin] will be ignored.
+ * @param modifier the [Modifier] to be applied to this checkbox
+ * @param enabled controls the enabled state of this checkbox. When `false`, this component will not
+ * respond to user input, and it will appear visually disabled and disabled to accessibility
+ * services.
+ * @param colors [CheckboxColors] that will be used to resolve the colors used for this checkbox in
+ * different states. See [CheckboxDefaults.colors].
+ * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
+ * emitting [Interaction]s for this checkbox. You can use this to change the checkbox's appearance
+ * or preview the checkbox in different states. Note that if `null` is provided, interactions will
+ * still happen internally.
+ * @see [TriStateCheckbox] if you require support for an indeterminate state.
+ */
+@Composable
+fun Checkbox(
+ checked: Boolean,
+ onCheckedChange: ((Boolean) -> Unit)?,
+ checkmarkStroke: Stroke,
+ outlineStroke: Stroke,
+ modifier: Modifier = Modifier,
+ enabled: Boolean = true,
+ colors: CheckboxColors = CheckboxDefaults.colors(),
+ interactionSource: MutableInteractionSource? = null
+) {
+ TriStateCheckbox(
+ state = ToggleableState(checked),
+ onClick =
+ if (onCheckedChange != null) {
+ { onCheckedChange(!checked) }
+ } else {
+ null
+ },
+ checkmarkStroke = checkmarkStroke,
+ outlineStroke = outlineStroke,
modifier = modifier,
enabled = enabled,
colors = colors,
@@ -148,6 +216,68 @@
colors: CheckboxColors = CheckboxDefaults.colors(),
interactionSource: MutableInteractionSource? = null
) {
+ val strokeWidthPx = with(LocalDensity.current) { floor(CheckboxDefaults.StrokeWidth.toPx()) }
+ TriStateCheckbox(
+ state = state,
+ onClick = onClick,
+ checkmarkStroke = Stroke(width = strokeWidthPx, cap = StrokeCap.Square),
+ outlineStroke = Stroke(width = strokeWidthPx),
+ modifier = modifier,
+ enabled = enabled,
+ colors = colors,
+ interactionSource = interactionSource
+ )
+}
+
+/**
+ * <a href="https://m3.material.io/components/checkbox/guidelines" class="external"
+ * target="_blank">Material Design checkbox</a> parent.
+ *
+ * Checkboxes can have a parent-child relationship with other checkboxes. When the parent checkbox
+ * is checked, all child checkboxes are checked. If a parent checkbox is unchecked, all child
+ * checkboxes are unchecked. If some, but not all, child checkboxes are checked, the parent checkbox
+ * becomes an indeterminate checkbox.
+ *
+ * 
+ *
+ * This Checkbox function offers greater flexibility in visual customization. Using the [Stroke]
+ * parameters, you can control the appearance of both the checkmark and the box that surrounds it.
+ *
+ * A sample of a `TriStateCheckbox` that uses a [Stroke] with rounded [StrokeCap] and
+ * [androidx.compose.ui.graphics.StrokeJoin]:
+ *
+ * @sample androidx.compose.material3.samples.TriStateCheckboxRoundedStrokesSample
+ * @param state whether this checkbox is checked, unchecked, or in an indeterminate state
+ * @param onClick called when this checkbox is clicked. If `null`, then this checkbox will not be
+ * interactable, unless something else handles its input events and updates its [state].
+ * @param checkmarkStroke stroke for the checkmark.
+ * @param outlineStroke stroke for the checkmark's box outline. Note that this stroke is applied
+ * when drawing the outline's rounded rectangle, so attributions such as
+ * [androidx.compose.ui.graphics.StrokeJoin] will be ignored.
+ * @param modifier the [Modifier] to be applied to this checkbox
+ * @param enabled controls the enabled state of this checkbox. When `false`, this component will not
+ * respond to user input, and it will appear visually disabled and disabled to accessibility
+ * services.
+ * @param colors [CheckboxColors] that will be used to resolve the colors used for this checkbox in
+ * different states. See [CheckboxDefaults.colors].
+ * @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
+ * emitting [Interaction]s for this checkbox. You can use this to change the checkbox's appearance
+ * or preview the checkbox in different states. Note that if `null` is provided, interactions will
+ * still happen internally.
+ * @see [Checkbox] if you want a simple component that represents Boolean state
+ */
+@Composable
+fun TriStateCheckbox(
+ state: ToggleableState,
+ onClick: (() -> Unit)?,
+ checkmarkStroke: Stroke,
+ outlineStroke: Stroke,
+ modifier: Modifier = Modifier,
+ enabled: Boolean = true,
+ colors: CheckboxColors = CheckboxDefaults.colors(),
+ interactionSource: MutableInteractionSource? = null
+) {
val toggleableModifier =
if (onClick != null) {
Modifier.triStateToggleable(
@@ -175,7 +305,9 @@
)
.then(toggleableModifier)
.padding(CheckboxDefaultPadding),
- colors = colors
+ colors = colors,
+ checkmarkStroke = checkmarkStroke,
+ outlineStroke = outlineStroke
)
}
@@ -255,6 +387,12 @@
)
.also { defaultCheckboxColorsCached = it }
}
+
+ /**
+ * The default stroke width for a [Checkbox]. This width will be used for the checkmark when the
+ * `Checkbox` is in a checked or indeterminate states, or for the outline when it's unchecked.
+ */
+ val StrokeWidth = 2.dp
}
@Composable
@@ -262,7 +400,9 @@
enabled: Boolean,
value: ToggleableState,
modifier: Modifier,
- colors: CheckboxColors
+ colors: CheckboxColors,
+ checkmarkStroke: Stroke,
+ outlineStroke: Stroke,
) {
val transition = updateTransition(value)
val defaultAnimationSpec = MotionSchemeKeyTokens.DefaultSpatial.value<Float>()
@@ -306,31 +446,24 @@
val boxColor = colors.boxColor(enabled, value)
val borderColor = colors.borderColor(enabled, value)
Canvas(modifier.wrapContentSize(Alignment.Center).requiredSize(CheckboxSize)) {
- val strokeWidthPx = floor(StrokeWidth.toPx())
drawBox(
boxColor = boxColor.value,
borderColor = borderColor.value,
radius = RadiusSize.toPx(),
- strokeWidth = strokeWidthPx
+ stroke = outlineStroke
)
drawCheck(
checkColor = checkColor.value,
checkFraction = checkDrawFraction.value,
crossCenterGravitation = checkCenterGravitationShiftFraction.value,
- strokeWidthPx = strokeWidthPx,
+ stroke = checkmarkStroke,
drawingCache = checkCache
)
}
}
-private fun DrawScope.drawBox(
- boxColor: Color,
- borderColor: Color,
- radius: Float,
- strokeWidth: Float
-) {
- val halfStrokeWidth = strokeWidth / 2.0f
- val stroke = Stroke(strokeWidth)
+private fun DrawScope.drawBox(boxColor: Color, borderColor: Color, radius: Float, stroke: Stroke) {
+ val halfStrokeWidth = stroke.width / 2.0f
val checkboxSize = size.width
if (boxColor == borderColor) {
drawRoundRect(
@@ -342,15 +475,15 @@
} else {
drawRoundRect(
boxColor,
- topLeft = Offset(strokeWidth, strokeWidth),
- size = Size(checkboxSize - strokeWidth * 2, checkboxSize - strokeWidth * 2),
- cornerRadius = CornerRadius(max(0f, radius - strokeWidth)),
+ topLeft = Offset(stroke.width, stroke.width),
+ size = Size(checkboxSize - stroke.width * 2, checkboxSize - stroke.width * 2),
+ cornerRadius = CornerRadius(max(0f, radius - stroke.width)),
style = Fill
)
drawRoundRect(
borderColor,
topLeft = Offset(halfStrokeWidth, halfStrokeWidth),
- size = Size(checkboxSize - strokeWidth, checkboxSize - strokeWidth),
+ size = Size(checkboxSize - stroke.width, checkboxSize - stroke.width),
cornerRadius = CornerRadius(radius - halfStrokeWidth),
style = stroke
)
@@ -361,10 +494,9 @@
checkColor: Color,
checkFraction: Float,
crossCenterGravitation: Float,
- strokeWidthPx: Float,
+ stroke: Stroke,
drawingCache: CheckDrawingCache
) {
- val stroke = Stroke(width = strokeWidthPx, cap = StrokeCap.Square)
val width = size.width
val checkCrossX = 0.4f
val checkCrossY = 0.7f
@@ -380,13 +512,13 @@
val gravitatedRightY = lerp(rightY, 0.5f, crossCenterGravitation)
with(drawingCache) {
- checkPath.reset()
+ checkPath.rewind()
checkPath.moveTo(width * leftX, width * gravitatedLeftY)
checkPath.lineTo(width * gravitatedCrossX, width * gravitatedCrossY)
checkPath.lineTo(width * rightX, width * gravitatedRightY)
// TODO: replace with proper declarative non-android alternative when ready (b/158188351)
pathMeasure.setPath(checkPath, false)
- pathToDraw.reset()
+ pathToDraw.rewind()
pathMeasure.getSegment(0f, pathMeasure.length * checkFraction, pathToDraw, true)
}
drawPath(drawingCache.pathToDraw, checkColor, style = stroke)
@@ -607,5 +739,4 @@
// TODO(b/188529841): Update the padding and size when the Checkbox spec is finalized.
private val CheckboxDefaultPadding = 2.dp
private val CheckboxSize = 20.dp
-private val StrokeWidth = 2.dp
private val RadiusSize = 2.dp
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/internal/AccessibilityServiceStateProvider.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/internal/AccessibilityServiceStateProvider.kt
index e03821f..9986bf1 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/internal/AccessibilityServiceStateProvider.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/internal/AccessibilityServiceStateProvider.kt
@@ -25,9 +25,11 @@
* @param listenToTouchExplorationState whether to track the enabled/disabled state of touch
* exploration (i.e. TalkBack)
* @param listenToSwitchAccessState whether to track the enabled/disabled state of Switch Access
+ * @param listenToVoiceAccessState whether to track the enabled/disabled state of Voice Access
*/
@Composable
internal expect fun rememberAccessibilityServiceState(
listenToTouchExplorationState: Boolean = true,
listenToSwitchAccessState: Boolean = true,
+ listenToVoiceAccessState: Boolean = true,
): State<Boolean>
diff --git a/compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/internal/AccessibilityServiceStateProvider.commonStubs.kt b/compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/internal/AccessibilityServiceStateProvider.commonStubs.kt
index e68e24c..6654fd6 100644
--- a/compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/internal/AccessibilityServiceStateProvider.commonStubs.kt
+++ b/compose/material3/material3/src/commonStubsMain/kotlin/androidx/compose/material3/internal/AccessibilityServiceStateProvider.commonStubs.kt
@@ -24,4 +24,5 @@
internal actual fun rememberAccessibilityServiceState(
listenToTouchExplorationState: Boolean,
listenToSwitchAccessState: Boolean,
+ listenToVoiceAccessState: Boolean,
): State<Boolean> = implementedInJetBrainsFork()
diff --git a/compose/runtime/runtime/proguard-rules.pro b/compose/runtime/runtime/proguard-rules.pro
index 6e78193..e69e0a1 100644
--- a/compose/runtime/runtime/proguard-rules.pro
+++ b/compose/runtime/runtime/proguard-rules.pro
@@ -17,7 +17,7 @@
# Keep all the functions created to throw an exception. We don't want these functions to be
# inlined in any way, which R8 will do by default. The whole point of these functions is to
# reduce the amount of code generated at the call site.
--keep,allowshrinking,allowobfuscation class androidx.compose.runtime.** {
+-keepclassmembers,allowshrinking,allowobfuscation class androidx.compose.runtime.** {
# java.lang.Void == methods that return Nothing
static void throw*Exception(...);
static void throw*ExceptionForNullCheck(...);
@@ -31,3 +31,4 @@
static void compose*RuntimeError(...);
static java.lang.Void compose*RuntimeError(...);
}
+
diff --git a/compose/runtime/runtime/src/androidMain/kotlin/androidx/compose/runtime/collection/ArrayUtils.android.kt b/compose/runtime/runtime/src/androidMain/kotlin/androidx/compose/runtime/collection/ArrayUtils.android.kt
new file mode 100644
index 0000000..c487bdc
--- /dev/null
+++ b/compose/runtime/runtime/src/androidMain/kotlin/androidx/compose/runtime/collection/ArrayUtils.android.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2024 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.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE", "KotlinRedundantDiagnosticSuppress")
+
+package androidx.compose.runtime.collection
+
+internal actual inline fun <T> Array<out T>.fastCopyInto(
+ destination: Array<T>,
+ destinationOffset: Int,
+ startIndex: Int,
+ endIndex: Int
+): Array<T> {
+ System.arraycopy(this, startIndex, destination, destinationOffset, endIndex - startIndex)
+ return destination
+}
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SlotTable.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SlotTable.kt
index a295937..afd4db85 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SlotTable.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/SlotTable.kt
@@ -23,6 +23,7 @@
import androidx.collection.MutableIntSet
import androidx.collection.MutableObjectList
import androidx.collection.mutableIntListOf
+import androidx.compose.runtime.collection.fastCopyInto
import androidx.compose.runtime.platform.makeSynchronizedObject
import androidx.compose.runtime.platform.synchronized
import androidx.compose.runtime.snapshots.fastAny
@@ -2122,7 +2123,7 @@
// 4) copy the slots to their new location
if (moveDataLen > 0) {
val slots = slots
- slots.copyInto(
+ slots.fastCopyInto(
destination = slots,
destinationOffset = destinationSlot,
startIndex = dataIndexToDataAddress(dataStart + moveDataLen),
@@ -2206,7 +2207,7 @@
)
val slots = toWriter.slots
val currentSlot = toWriter.currentSlot
- fromWriter.slots.copyInto(
+ fromWriter.slots.fastCopyInto(
destination = slots,
destinationOffset = currentSlot,
startIndex = sourceSlotsStart,
@@ -2676,7 +2677,7 @@
val slots = slots
if (index < gapStart) {
// move the gap down to index by shifting the data up.
- slots.copyInto(
+ slots.fastCopyInto(
destination = slots,
destinationOffset = index + gapLen,
startIndex = index,
@@ -2684,7 +2685,7 @@
)
} else {
// Shift the data down, leaving the gap at index
- slots.copyInto(
+ slots.fastCopyInto(
destination = slots,
destinationOffset = gapStart,
startIndex = gapStart + gapLen,
@@ -2830,13 +2831,13 @@
val newGapEndAddress = gapStart + newGapLen
// Copy the old arrays into the new arrays
- slots.copyInto(
+ slots.fastCopyInto(
destination = newData,
destinationOffset = 0,
startIndex = 0,
endIndex = gapStart
)
- slots.copyInto(
+ slots.fastCopyInto(
destination = newData,
destinationOffset = newGapEndAddress,
startIndex = oldGapEndAddress,
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Stack.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Stack.kt
index 03989dd..d11eacb 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Stack.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Stack.kt
@@ -14,9 +14,13 @@
* limitations under the License.
*/
+@file:Suppress("NOTHING_TO_INLINE", "KotlinRedundantDiagnosticSuppress")
+
package androidx.compose.runtime
+import kotlin.jvm.JvmField
import kotlin.jvm.JvmInline
+import kotlin.math.min
@JvmInline
internal value class Stack<T>(private val backing: ArrayList<T> = ArrayList()) {
@@ -42,22 +46,32 @@
}
internal class IntStack {
- private var slots = IntArray(10)
- private var tos = 0
+ @JvmField internal var slots = IntArray(10)
+ @JvmField internal var tos = 0
- val size: Int
+ inline val size: Int
get() = tos
+ private fun resize(): IntArray {
+ val copy = slots.copyOf(slots.size * 2)
+ slots = copy
+ return copy
+ }
+
fun push(value: Int) {
+ var slots = slots
if (tos >= slots.size) {
- slots = slots.copyOf(slots.size * 2)
+ slots = resize()
}
slots[tos++] = value
}
fun pop(): Int = slots[--tos]
- fun peekOr(default: Int): Int = if (tos > 0) peek() else default
+ fun peekOr(default: Int): Int {
+ val index = tos - 1
+ return if (index >= 0) slots[index] else default
+ }
fun peek() = slots[tos - 1]
@@ -65,16 +79,20 @@
fun peek(index: Int) = slots[index]
- fun isEmpty() = tos == 0
+ inline fun isEmpty() = tos == 0
- fun isNotEmpty() = tos != 0
+ inline fun isNotEmpty() = tos != 0
fun clear() {
tos = 0
}
fun indexOf(value: Int): Int {
- for (i in 0 until tos) if (slots[i] == value) return i
+ val slots = slots
+ val end = min(slots.size, tos)
+ for (i in 0 until end) {
+ if (slots[i] == value) return i
+ }
return -1
}
}
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/changelist/Operations.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/changelist/Operations.kt
index f250bc8..efa946a 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/changelist/Operations.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/changelist/Operations.kt
@@ -24,6 +24,7 @@
import androidx.compose.runtime.RememberManager
import androidx.compose.runtime.SlotWriter
import androidx.compose.runtime.changelist.Operation.ObjectParameter
+import androidx.compose.runtime.collection.fastCopyInto
import androidx.compose.runtime.debugRuntimeCheck
import androidx.compose.runtime.requirePrecondition
import kotlin.contracts.ExperimentalContracts
@@ -137,7 +138,7 @@
val resizeAmount = opCodesSize.coerceAtMost(OperationsMaxResizeAmount)
@Suppress("UNCHECKED_CAST")
val newOpCodes = arrayOfNulls<Operation>(opCodesSize + resizeAmount) as Array<Operation>
- opCodes = opCodes.copyInto(newOpCodes, 0, 0, opCodesSize)
+ opCodes = opCodes.fastCopyInto(newOpCodes, 0, 0, opCodesSize)
}
private inline fun ensureIntArgsSizeAtLeast(requiredSize: Int) {
@@ -162,7 +163,7 @@
private fun resizeObjectArgs(currentSize: Int, requiredSize: Int) {
val newObjectArgs = arrayOfNulls<Any>(determineNewSize(currentSize, requiredSize))
- objectArgs.copyInto(newObjectArgs, 0, 0, currentSize)
+ objectArgs.fastCopyInto(newObjectArgs, 0, 0, currentSize)
objectArgs = newObjectArgs
}
@@ -291,7 +292,7 @@
other.pushOp(op)
// Move the objects then null out our contents
- objectArgs.copyInto(
+ objectArgs.fastCopyInto(
destination = other.objectArgs,
destinationOffset = other.objectArgsSize - op.objects,
startIndex = objectArgsSize - op.objects,
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/ArrayUtils.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/ArrayUtils.kt
new file mode 100644
index 0000000..03c2546
--- /dev/null
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/ArrayUtils.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2024 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.runtime.collection
+
+/**
+ * Equivalent of Array.copyInto() with an implementation designed to avoid unnecessary null checks
+ * and exception throws on Android after inlining.
+ */
+internal expect fun <T> Array<out T>.fastCopyInto(
+ destination: Array<T>,
+ destinationOffset: Int,
+ startIndex: Int,
+ endIndex: Int
+): Array<T>
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/MutableVector.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/MutableVector.kt
index 1315b32..c16d977 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/MutableVector.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/collection/MutableVector.kt
@@ -66,7 +66,7 @@
ensureCapacity(size + 1)
val content = content
if (index != size) {
- content.copyInto(
+ content.fastCopyInto(
destination = content,
destinationOffset = index + 1,
startIndex = index,
@@ -83,12 +83,13 @@
*/
fun addAll(index: Int, elements: List<T>): Boolean {
if (elements.isEmpty()) return false
- ensureCapacity(size + elements.size)
+ val elementsSize = elements.size
+ ensureCapacity(size + elementsSize)
val content = content
if (index != size) {
- content.copyInto(
+ content.fastCopyInto(
destination = content,
- destinationOffset = index + elements.size,
+ destinationOffset = index + elementsSize,
startIndex = index,
endIndex = size
)
@@ -96,7 +97,7 @@
for (i in elements.indices) {
content[index + i] = elements[i]
}
- size += elements.size
+ size += elementsSize
return true
}
@@ -105,24 +106,25 @@
* that are in the way.
*/
fun addAll(index: Int, elements: MutableVector<T>): Boolean {
- if (elements.isEmpty()) return false
- ensureCapacity(size + elements.size)
+ val elementsSize = elements.size
+ if (elementsSize == 0) return false
+ ensureCapacity(size + elementsSize)
val content = content
if (index != size) {
- content.copyInto(
+ content.fastCopyInto(
destination = content,
- destinationOffset = index + elements.size,
+ destinationOffset = index + elementsSize,
startIndex = index,
endIndex = size
)
}
- elements.content.copyInto(
+ elements.content.fastCopyInto(
destination = content,
destinationOffset = index,
startIndex = 0,
- endIndex = elements.size
+ endIndex = elementsSize
)
- size += elements.size
+ size += elementsSize
return true
}
@@ -147,12 +149,13 @@
* [MutableVector] was changed.
*/
fun addAll(@Suppress("ArrayReturn") elements: Array<T>): Boolean {
- if (elements.isEmpty()) {
+ val elementsSize = elements.size
+ if (elementsSize == 0) {
return false
}
- ensureCapacity(size + elements.size)
- elements.copyInto(destination = content, destinationOffset = size)
- size += elements.size
+ ensureCapacity(size + elementsSize)
+ elements.fastCopyInto(destination = content, destinationOffset = size, 0, elementsSize)
+ size += elementsSize
return true
}
@@ -162,18 +165,19 @@
*/
fun addAll(index: Int, elements: Collection<T>): Boolean {
if (elements.isEmpty()) return false
- ensureCapacity(size + elements.size)
+ val elementsSize = elements.size
+ ensureCapacity(size + elementsSize)
val content = content
if (index != size) {
- content.copyInto(
+ content.fastCopyInto(
destination = content,
- destinationOffset = index + elements.size,
+ destinationOffset = index + elementsSize,
startIndex = index,
endIndex = size
)
}
elements.forEachIndexed { i, item -> content[index + i] = item }
- size += elements.size
+ size += elementsSize
return true
}
@@ -293,7 +297,7 @@
val oldSize = oldContent.size
val newSize = max(capacity, oldSize * 2)
val newContent = arrayOfNulls<Any?>(newSize) as Array<T?>
- oldContent.copyInto(newContent, 0, 0, oldSize)
+ oldContent.fastCopyInto(newContent, 0, 0, oldSize)
content = newContent
}
@@ -694,7 +698,7 @@
val content = content
val item = content[index] as T
if (index != lastIndex) {
- content.copyInto(
+ content.fastCopyInto(
destination = content,
destinationOffset = index,
startIndex = index + 1,
@@ -710,7 +714,7 @@
fun removeRange(start: Int, end: Int) {
if (end > start) {
if (end < size) {
- content.copyInto(
+ content.fastCopyInto(
destination = content,
destinationOffset = start,
startIndex = end,
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotWeakSet.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotWeakSet.kt
index 66075f0a..e70d6fd 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotWeakSet.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/SnapshotWeakSet.kt
@@ -17,6 +17,7 @@
package androidx.compose.runtime.snapshots
import androidx.compose.runtime.TestOnly
+import androidx.compose.runtime.collection.fastCopyInto
import androidx.compose.runtime.internal.WeakReference
import androidx.compose.runtime.internal.identityHashCode
@@ -70,13 +71,18 @@
val newCapacity = capacity * 2
val newValues = arrayOfNulls<WeakReference<T>?>(newCapacity)
val newHashes = IntArray(newCapacity)
- values.copyInto(
+ values.fastCopyInto(
destination = newValues,
destinationOffset = insertIndex + 1,
startIndex = insertIndex,
endIndex = size
)
- values.copyInto(destination = newValues, endIndex = insertIndex)
+ values.fastCopyInto(
+ destination = newValues,
+ destinationOffset = 0,
+ startIndex = 0,
+ endIndex = insertIndex
+ )
hashes.copyInto(
destination = newHashes,
destinationOffset = insertIndex + 1,
@@ -87,7 +93,7 @@
values = newValues
hashes = newHashes
} else {
- values.copyInto(
+ values.fastCopyInto(
destination = values,
destinationOffset = insertIndex + 1,
startIndex = insertIndex,
diff --git a/compose/runtime/runtime/src/nonAndroidMain/kotlin/androidx/compose/runtime/collection/ArrayUtils.nonAndroid.kt b/compose/runtime/runtime/src/nonAndroidMain/kotlin/androidx/compose/runtime/collection/ArrayUtils.nonAndroid.kt
new file mode 100644
index 0000000..2e2c834
--- /dev/null
+++ b/compose/runtime/runtime/src/nonAndroidMain/kotlin/androidx/compose/runtime/collection/ArrayUtils.nonAndroid.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2024 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.
+ */
+
+@file:Suppress("NOTHING_TO_INLINE", "KotlinRedundantDiagnosticSuppress")
+
+package androidx.compose.runtime.collection
+
+internal actual inline fun <T> Array<out T>.fastCopyInto(
+ destination: Array<T>,
+ destinationOffset: Int,
+ startIndex: Int,
+ endIndex: Int
+): Array<T> = this.copyInto(destination, destinationOffset, startIndex, endIndex)
diff --git a/compose/ui/ui-graphics/proguard-rules.pro b/compose/ui/ui-graphics/proguard-rules.pro
index 67d118b..72f4a6c 100644
--- a/compose/ui/ui-graphics/proguard-rules.pro
+++ b/compose/ui/ui-graphics/proguard-rules.pro
@@ -15,7 +15,7 @@
# Keep all the functions created to throw an exception. We don't want these functions to be
# inlined in any way, which R8 will do by default. The whole point of these functions is to
# reduce the amount of code generated at the call site.
--keep,allowshrinking,allowobfuscation class androidx.compose.**.* {
+-keepclassmembers,allowshrinking,allowobfuscation class androidx.compose.**.* {
static void throw*Exception(...);
static void throw*ExceptionForNullCheck(...);
# For methods returning Nothing
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/IntervalTree.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/IntervalTree.kt
index 1d3b503..8a2ff33 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/IntervalTree.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/IntervalTree.kt
@@ -82,8 +82,7 @@
// structure beyond what can be found in various descriptions of binary search
// trees and red/black trees
- @JvmField
- internal val terminator = Node(Float.MAX_VALUE, Float.MIN_VALUE, null, TreeColor.Black)
+ @JvmField internal val terminator = Node(Float.MAX_VALUE, Float.MIN_VALUE, null, TreeColorBlack)
@JvmField internal var root = terminator
@JvmField internal val stack = ArrayList<Node>()
@@ -203,7 +202,7 @@
* @param data Data to associate with the interval
*/
fun addInterval(start: Float, end: Float, data: T?) {
- val node = Node(start, end, data, TreeColor.Red)
+ val node = Node(start, end, data, TreeColorRed)
// Update the tree without doing any balancing
var current = root
@@ -239,44 +238,44 @@
private fun rebalance(target: Node) {
var node = target
- while (node !== root && node.parent.color == TreeColor.Red) {
+ while (node !== root && node.parent.color == TreeColorRed) {
val ancestor = node.parent.parent
if (node.parent === ancestor.left) {
val right = ancestor.right
- if (right.color == TreeColor.Red) {
- right.color = TreeColor.Black
- node.parent.color = TreeColor.Black
- ancestor.color = TreeColor.Red
+ if (right.color == TreeColorRed) {
+ right.color = TreeColorBlack
+ node.parent.color = TreeColorBlack
+ ancestor.color = TreeColorRed
node = ancestor
} else {
if (node === node.parent.right) {
node = node.parent
rotateLeft(node)
}
- node.parent.color = TreeColor.Black
- ancestor.color = TreeColor.Red
+ node.parent.color = TreeColorBlack
+ ancestor.color = TreeColorRed
rotateRight(ancestor)
}
} else {
val left = ancestor.left
- if (left.color == TreeColor.Red) {
- left.color = TreeColor.Black
- node.parent.color = TreeColor.Black
- ancestor.color = TreeColor.Red
+ if (left.color == TreeColorRed) {
+ left.color = TreeColorBlack
+ node.parent.color = TreeColorBlack
+ ancestor.color = TreeColorRed
node = ancestor
} else {
if (node === node.parent.left) {
node = node.parent
rotateRight(node)
}
- node.parent.color = TreeColor.Black
- ancestor.color = TreeColor.Red
+ node.parent.color = TreeColorBlack
+ ancestor.color = TreeColorRed
rotateLeft(ancestor)
}
}
}
- root.color = TreeColor.Black
+ root.color = TreeColorBlack
}
private fun rotateLeft(node: Node) {
@@ -340,11 +339,6 @@
}
}
- internal enum class TreeColor {
- Red,
- Black
- }
-
internal inner class Node(start: Float, end: Float, data: T?, var color: TreeColor) :
Interval<T>(start, end, data) {
var min: Float = start
@@ -378,3 +372,8 @@
}
}
}
+
+private typealias TreeColor = Int
+
+private const val TreeColorRed = 0
+private const val TreeColorBlack = 1
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/PathGeometry.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/PathGeometry.kt
index 53b0d54..4c96e1e 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/PathGeometry.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/PathGeometry.kt
@@ -58,7 +58,7 @@
// segments.
var type = iterator.next(points)
while (type != PathSegment.Type.Done) {
- @Suppress("KotlinConstantConditions")
+ @Suppress("KotlinConstantConditions", "RedundantSuppression")
when (type) {
PathSegment.Type.Move -> {
if (!first) {
@@ -175,7 +175,7 @@
var type = iterator.next(points)
while (type != PathSegment.Type.Done) {
- @Suppress("KotlinConstantConditions")
+ @Suppress("KotlinConstantConditions", "RedundantSuppression")
when (type) {
PathSegment.Type.Move -> {
if (!first && !isEmpty) {
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/colorspace/ColorSpace.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/colorspace/ColorSpace.kt
index 3905cff..ec0cc69 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/colorspace/ColorSpace.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/colorspace/ColorSpace.kt
@@ -633,7 +633,7 @@
* @param r2: The third element of the vector
* @return The first element of the resulting multiplication.
*/
-@Suppress("NOTHING_TO_INLINE")
+@Suppress("NOTHING_TO_INLINE", "KotlinRedundantDiagnosticSuppress")
internal inline fun mul3x3Float3_0(lhs: FloatArray, r0: Float, r1: Float, r2: Float): Float {
return lhs[0] * r0 + lhs[3] * r1 + lhs[6] * r2
}
@@ -648,7 +648,7 @@
* @param r2: The third element of the vector
* @return The second element of the resulting multiplication.
*/
-@Suppress("NOTHING_TO_INLINE")
+@Suppress("NOTHING_TO_INLINE", "KotlinRedundantDiagnosticSuppress")
internal inline fun mul3x3Float3_1(lhs: FloatArray, r0: Float, r1: Float, r2: Float): Float {
return lhs[1] * r0 + lhs[4] * r1 + lhs[7] * r2
}
@@ -663,7 +663,7 @@
* @param r2: The third element of the vector
* @return The third element of the resulting multiplication.
*/
-@Suppress("NOTHING_TO_INLINE")
+@Suppress("NOTHING_TO_INLINE", "KotlinRedundantDiagnosticSuppress")
internal inline fun mul3x3Float3_2(lhs: FloatArray, r0: Float, r1: Float, r2: Float): Float {
return lhs[2] * r0 + lhs[5] * r1 + lhs[8] * r2
}
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/colorspace/Connector.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/colorspace/Connector.kt
index 7a47484..2a81bac 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/colorspace/Connector.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/colorspace/Connector.kt
@@ -230,7 +230,7 @@
chromaticAdaptation(
Adaptation.Bradford.transform,
srcXYZ,
- Illuminant.D50Xyz.copyOf()
+ Illuminant.newD50Xyz()
)
transform = mul3x3(srcAdaptation, source.transform)
}
@@ -240,7 +240,7 @@
chromaticAdaptation(
Adaptation.Bradford.transform,
dstXYZ,
- Illuminant.D50Xyz.copyOf()
+ Illuminant.newD50Xyz()
)
inverseTransform = inverse3x3(mul3x3(dstAdaptation, destination.transform))
}
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/colorspace/Illuminant.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/colorspace/Illuminant.kt
index b3598c3..0bf3461 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/colorspace/Illuminant.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/colorspace/Illuminant.kt
@@ -73,4 +73,6 @@
val E = WhitePoint(0.33333f, 0.33333f)
internal val D50Xyz = floatArrayOf(0.964212f, 1.0f, 0.825188f)
+
+ internal fun newD50Xyz() = floatArrayOf(0.964212f, 1.0f, 0.825188f)
}
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/colorspace/Rgb.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/colorspace/Rgb.kt
index 06f2891..e9d3032 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/colorspace/Rgb.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/colorspace/Rgb.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-@file:Suppress("NOTHING_TO_INLINE")
+@file:Suppress("NOTHING_TO_INLINE", "KotlinRedundantDiagnosticSuppress")
package androidx.compose.ui.graphics.colorspace
@@ -853,10 +853,9 @@
var result = super.hashCode()
result = 31 * result + whitePoint.hashCode()
result = 31 * result + primaries.contentHashCode()
- result = 31 * result + (if (min != +0.0f) min.toBits() else 0)
- result = 31 * result + (if (max != +0.0f) max.toBits() else 0)
- result =
- (31 * result + if (transferParameters != null) transferParameters.hashCode() else 0)
+ result = 31 * result + (if (min != 0.0f) min.toBits() else 0)
+ result = 31 * result + (if (max != 0.0f) max.toBits() else 0)
+ result = (31 * result + (transferParameters?.hashCode() ?: 0))
if (transferParameters == null) {
result = 31 * result + oetfOrig.hashCode()
result = 31 * result + eotfOrig.hashCode()
diff --git a/compose/ui/ui-test/api/current.txt b/compose/ui/ui-test/api/current.txt
index 1a28106..9c3549c 100644
--- a/compose/ui/ui-test/api/current.txt
+++ b/compose/ui/ui-test/api/current.txt
@@ -495,7 +495,6 @@
method public androidx.compose.ui.test.SemanticsNodeInteraction assertExists(optional String? errorMessageOnFail);
method public void assertIsDeactivated(optional String? errorMessageOnFail);
method public androidx.compose.ui.semantics.SemanticsNode fetchSemanticsNode(optional String? errorMessageOnFail);
- method public int semanticsId();
}
public final class SemanticsNodeInteractionCollection {
diff --git a/compose/ui/ui-test/api/restricted_current.txt b/compose/ui/ui-test/api/restricted_current.txt
index 0e101d0..5ded425 100644
--- a/compose/ui/ui-test/api/restricted_current.txt
+++ b/compose/ui/ui-test/api/restricted_current.txt
@@ -497,7 +497,6 @@
method public androidx.compose.ui.test.SemanticsNodeInteraction assertExists(optional String? errorMessageOnFail);
method public void assertIsDeactivated(optional String? errorMessageOnFail);
method public androidx.compose.ui.semantics.SemanticsNode fetchSemanticsNode(optional String? errorMessageOnFail);
- method public int semanticsId();
}
public final class SemanticsNodeInteractionCollection {
diff --git a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/SemanticsNodeInteraction.kt b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/SemanticsNodeInteraction.kt
index 63c3346..81f94f3 100644
--- a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/SemanticsNodeInteraction.kt
+++ b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/SemanticsNodeInteraction.kt
@@ -147,11 +147,6 @@
}
}
- /** Fetch the semantics ID. */
- fun semanticsId(): Int {
- return fetchSemanticsNode().id
- }
-
private fun fetchOneOrThrow(
errorMessageOnFail: String? = null,
skipDeactivatedNodes: Boolean = true
diff --git a/compose/ui/ui-text/proguard-rules.pro b/compose/ui/ui-text/proguard-rules.pro
index 67d118b..72f4a6c 100644
--- a/compose/ui/ui-text/proguard-rules.pro
+++ b/compose/ui/ui-text/proguard-rules.pro
@@ -15,7 +15,7 @@
# Keep all the functions created to throw an exception. We don't want these functions to be
# inlined in any way, which R8 will do by default. The whole point of these functions is to
# reduce the amount of code generated at the call site.
--keep,allowshrinking,allowobfuscation class androidx.compose.**.* {
+-keepclassmembers,allowshrinking,allowobfuscation class androidx.compose.**.* {
static void throw*Exception(...);
static void throw*ExceptionForNullCheck(...);
# For methods returning Nothing
diff --git a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/URLSpanCache.android.kt b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/URLSpanCache.android.kt
index 6418529..209fdb9 100644
--- a/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/URLSpanCache.android.kt
+++ b/compose/ui/ui-text/src/androidMain/kotlin/androidx/compose/ui/text/platform/URLSpanCache.android.kt
@@ -42,8 +42,6 @@
*
* See b/253292081.
*/
-// "URL" violates naming guidelines, but that is intentional to match the platform API.
-@Suppress("AcronymName")
@OptIn(ExperimentalTextApi::class)
@InternalTextApi
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@@ -54,11 +52,9 @@
private val linkSpansWithListenerByAnnotation =
WeakHashMap<AnnotatedString.Range<LinkAnnotation>, ComposeClickableSpan>()
- @Suppress("AcronymName")
fun toURLSpan(urlAnnotation: UrlAnnotation): URLSpan =
spansByAnnotation.getOrPut(urlAnnotation) { URLSpan(urlAnnotation.url) }
- @Suppress("AcronymName")
fun toURLSpan(urlRange: AnnotatedString.Range<LinkAnnotation.Url>): URLSpan =
urlSpansByAnnotation.getOrPut(urlRange) { URLSpan(urlRange.item.url) }
diff --git a/compose/ui/ui-unit/proguard-rules.pro b/compose/ui/ui-unit/proguard-rules.pro
index 67d118b..72f4a6c 100644
--- a/compose/ui/ui-unit/proguard-rules.pro
+++ b/compose/ui/ui-unit/proguard-rules.pro
@@ -15,7 +15,7 @@
# Keep all the functions created to throw an exception. We don't want these functions to be
# inlined in any way, which R8 will do by default. The whole point of these functions is to
# reduce the amount of code generated at the call site.
--keep,allowshrinking,allowobfuscation class androidx.compose.**.* {
+-keepclassmembers,allowshrinking,allowobfuscation class androidx.compose.**.* {
static void throw*Exception(...);
static void throw*ExceptionForNullCheck(...);
# For methods returning Nothing
diff --git a/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Constraints.kt b/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Constraints.kt
index 79ba2da..80e1938 100644
--- a/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Constraints.kt
+++ b/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Constraints.kt
@@ -368,10 +368,16 @@
private const val MinFocusBits = 16
private const val MaxAllowedForMinFocusBits = (1 shl (31 - MinFocusBits)) - 2
+/** The mask to use for the focused dimension when there is minimal focus. */
+private const val MinFocusMask = 0xFFFF // 64K (16 bits)
+
/** The number of bits used for the non-focused dimension when there is minimal focus. */
private const val MinNonFocusBits = 15
private const val MaxAllowedForMinNonFocusBits = (1 shl (31 - MinNonFocusBits)) - 2
+/** The mask to use for the non-focused dimension when there is minimal focus. */
+private const val MinNonFocusMask = 0x7FFF // 32K (15 bits)
+
/** The number of bits to use for the focused dimension when there is maximal focus. */
private const val MaxFocusBits = 18
private const val MaxAllowedForMaxFocusBits = (1 shl (31 - MaxFocusBits)) - 2
@@ -383,6 +389,9 @@
private const val MaxNonFocusBits = 13
private const val MaxAllowedForMaxNonFocusBits = (1 shl (31 - MaxNonFocusBits)) - 2
+/** The mask to use for the non-focused dimension when there is maximal focus. */
+private const val MaxNonFocusMask = 0x1FFF // 8K (13 bits)
+
// 0xFFFFFFFE_00000003UL.toLong(), written as a signed value to declare it const
@PublishedApi internal const val MaxDimensionsAndFocusMask = -0x00000001_FFFFFFFDL
@@ -443,35 +452,22 @@
}
internal fun bitsNeedForSizeUnchecked(size: Int): Int {
- // We could look at the value of size itself, for instance by doing:
- // when {
- // size < MaxNonFocusMask -> MaxNonFocusBits
- // ...
- // }
- // but the following solution saves a few instructions by avoiding
- // multiple moves to load large constants
- val bits = (size + 1).countLeadingZeroBits()
return when {
- bits >= 32 - MaxNonFocusBits -> MaxNonFocusBits
- bits >= 32 - MinNonFocusBits -> MinNonFocusBits
- bits >= 32 - MinFocusBits -> MinFocusBits
- bits >= 32 - MaxFocusBits -> MaxFocusBits
+ size < MaxNonFocusMask -> MaxNonFocusBits
+ size < MinNonFocusMask -> MinNonFocusBits
+ size < MinFocusMask -> MinFocusBits
+ size < MaxFocusMask -> MaxFocusBits
else -> 255
}
}
private inline fun maxAllowedForSize(size: Int): Int {
- // See comment in bitsNeedForSizeUnchecked()
- // Note: the return value in every case is `1 shl (31 - bits) - 2`
- // However, computing the value instead of using constants uses more
- // instructions, so not worth it
- val bits = (size + 1).countLeadingZeroBits()
- if (bits <= 13) throwInvalidConstraintsSizeException(size)
return when {
- bits >= 32 - MaxNonFocusBits -> MaxAllowedForMaxNonFocusBits
- bits >= 32 - MinNonFocusBits -> MaxAllowedForMinNonFocusBits
- bits >= 32 - MinFocusBits -> MaxAllowedForMinFocusBits
- else -> MaxAllowedForMaxFocusBits
+ size < MaxNonFocusMask -> MaxAllowedForMaxNonFocusBits
+ size < MinNonFocusMask -> MaxAllowedForMinNonFocusBits
+ size < MinFocusMask -> MaxAllowedForMinFocusBits
+ size < MaxFocusMask -> MaxAllowedForMaxFocusBits
+ else -> throwInvalidConstraintsSizeException(size)
}
}
diff --git a/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Density.kt b/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Density.kt
index 5b3e3c5..cbefe8a 100644
--- a/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Density.kt
+++ b/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Density.kt
@@ -65,7 +65,7 @@
*/
@Stable
fun TextUnit.toPx(): Float {
- check(type == TextUnitType.Sp) { "Only Sp can convert to Px" }
+ checkPrecondition(type == TextUnitType.Sp) { "Only Sp can convert to Px" }
return toDp().toPx()
}
diff --git a/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Dp.kt b/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Dp.kt
index a88b3c0..e5cac56 100644
--- a/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Dp.kt
+++ b/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Dp.kt
@@ -83,7 +83,10 @@
/** Infinite dp dimension. */
@Stable val Infinity = Dp(Float.POSITIVE_INFINITY)
- /** Constant that means unspecified Dp */
+ /**
+ * Constant that means unspecified Dp. Instead of comparing a [Dp] value to this constant,
+ * consider using [isSpecified] and [isUnspecified] instead.
+ */
@Stable val Unspecified = Dp(Float.NaN)
}
}
diff --git a/compose/ui/ui-util/proguard-rules.pro b/compose/ui/ui-util/proguard-rules.pro
index 67d118b..72f4a6c 100644
--- a/compose/ui/ui-util/proguard-rules.pro
+++ b/compose/ui/ui-util/proguard-rules.pro
@@ -15,7 +15,7 @@
# Keep all the functions created to throw an exception. We don't want these functions to be
# inlined in any way, which R8 will do by default. The whole point of these functions is to
# reduce the amount of code generated at the call site.
--keep,allowshrinking,allowobfuscation class androidx.compose.**.* {
+-keepclassmembers,allowshrinking,allowobfuscation class androidx.compose.**.* {
static void throw*Exception(...);
static void throw*ExceptionForNullCheck(...);
# For methods returning Nothing
diff --git a/compose/ui/ui/api/current.txt b/compose/ui/ui/api/current.txt
index 2b88ca8..b2b5534 100644
--- a/compose/ui/ui/api/current.txt
+++ b/compose/ui/ui/api/current.txt
@@ -220,9 +220,9 @@
package androidx.compose.ui.autofill {
- public interface Autofill {
- method public void cancelAutofillForNode(androidx.compose.ui.autofill.AutofillNode autofillNode);
- method public void requestAutofillForNode(androidx.compose.ui.autofill.AutofillNode autofillNode);
+ @Deprecated public interface Autofill {
+ method @Deprecated public void cancelAutofillForNode(androidx.compose.ui.autofill.AutofillNode autofillNode);
+ method @Deprecated public void requestAutofillForNode(androidx.compose.ui.autofill.AutofillNode autofillNode);
}
public abstract class AutofillManager {
@@ -230,64 +230,64 @@
method public abstract void commit();
}
- public final class AutofillNode {
- ctor public AutofillNode(optional java.util.List<? extends androidx.compose.ui.autofill.AutofillType> autofillTypes, optional androidx.compose.ui.geometry.Rect? boundingBox, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit>? onFill);
- method public java.util.List<androidx.compose.ui.autofill.AutofillType> getAutofillTypes();
- method public androidx.compose.ui.geometry.Rect? getBoundingBox();
- method public int getId();
- method public kotlin.jvm.functions.Function1<java.lang.String,kotlin.Unit>? getOnFill();
- method public void setBoundingBox(androidx.compose.ui.geometry.Rect?);
- property public final java.util.List<androidx.compose.ui.autofill.AutofillType> autofillTypes;
- property public final androidx.compose.ui.geometry.Rect? boundingBox;
- property public final int id;
- property public final kotlin.jvm.functions.Function1<java.lang.String,kotlin.Unit>? onFill;
+ @Deprecated public final class AutofillNode {
+ ctor @Deprecated public AutofillNode(optional java.util.List<? extends androidx.compose.ui.autofill.AutofillType> autofillTypes, optional androidx.compose.ui.geometry.Rect? boundingBox, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit>? onFill);
+ method @Deprecated public java.util.List<androidx.compose.ui.autofill.AutofillType> getAutofillTypes();
+ method @Deprecated public androidx.compose.ui.geometry.Rect? getBoundingBox();
+ method @Deprecated public int getId();
+ method @Deprecated public kotlin.jvm.functions.Function1<java.lang.String,kotlin.Unit>? getOnFill();
+ method @Deprecated public void setBoundingBox(androidx.compose.ui.geometry.Rect?);
+ property @Deprecated public final java.util.List<androidx.compose.ui.autofill.AutofillType> autofillTypes;
+ property @Deprecated public final androidx.compose.ui.geometry.Rect? boundingBox;
+ property @Deprecated public final int id;
+ property @Deprecated public final kotlin.jvm.functions.Function1<java.lang.String,kotlin.Unit>? onFill;
}
- public final class AutofillTree {
- ctor public AutofillTree();
- method public java.util.Map<java.lang.Integer,androidx.compose.ui.autofill.AutofillNode> getChildren();
- method public kotlin.Unit? performAutofill(int id, String value);
- method public operator void plusAssign(androidx.compose.ui.autofill.AutofillNode autofillNode);
- property public final java.util.Map<java.lang.Integer,androidx.compose.ui.autofill.AutofillNode> children;
+ @Deprecated public final class AutofillTree {
+ ctor @Deprecated public AutofillTree();
+ method @Deprecated public java.util.Map<java.lang.Integer,androidx.compose.ui.autofill.AutofillNode> getChildren();
+ method @Deprecated public kotlin.Unit? performAutofill(int id, String value);
+ method @Deprecated public operator void plusAssign(androidx.compose.ui.autofill.AutofillNode autofillNode);
+ property @Deprecated public final java.util.Map<java.lang.Integer,androidx.compose.ui.autofill.AutofillNode> children;
}
- public enum AutofillType {
- enum_constant public static final androidx.compose.ui.autofill.AutofillType AddressAuxiliaryDetails;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType AddressCountry;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType AddressLocality;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType AddressRegion;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType AddressStreet;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType BirthDateDay;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType BirthDateFull;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType BirthDateMonth;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType BirthDateYear;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType CreditCardExpirationDate;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType CreditCardExpirationDay;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType CreditCardExpirationMonth;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType CreditCardExpirationYear;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType CreditCardNumber;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType CreditCardSecurityCode;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType EmailAddress;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType Gender;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType NewPassword;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType NewUsername;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType Password;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PersonFirstName;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PersonFullName;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PersonLastName;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PersonMiddleInitial;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PersonMiddleName;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PersonNamePrefix;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PersonNameSuffix;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PhoneCountryCode;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PhoneNumber;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PhoneNumberDevice;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PhoneNumberNational;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PostalAddress;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PostalCode;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PostalCodeExtended;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType SmsOtpCode;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType Username;
+ @Deprecated public enum AutofillType {
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType AddressAuxiliaryDetails;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType AddressCountry;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType AddressLocality;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType AddressRegion;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType AddressStreet;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType BirthDateDay;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType BirthDateFull;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType BirthDateMonth;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType BirthDateYear;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType CreditCardExpirationDate;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType CreditCardExpirationDay;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType CreditCardExpirationMonth;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType CreditCardExpirationYear;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType CreditCardNumber;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType CreditCardSecurityCode;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType EmailAddress;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType Gender;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType NewPassword;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType NewUsername;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType Password;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType PersonFirstName;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType PersonFullName;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType PersonLastName;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType PersonMiddleInitial;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType PersonMiddleName;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType PersonNamePrefix;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType PersonNameSuffix;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType PhoneCountryCode;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType PhoneNumber;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType PhoneNumberDevice;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType PhoneNumberNational;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType PostalAddress;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType PostalCode;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType PostalCodeExtended;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType SmsOtpCode;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType Username;
}
@kotlin.jvm.JvmInline public final value class ContentDataType {
@@ -3202,9 +3202,9 @@
public final class CompositionLocalsKt {
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.AccessibilityManager?> getLocalAccessibilityManager();
- method @SuppressCompatibility @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.autofill.Autofill?> getLocalAutofill();
+ method @Deprecated public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.autofill.Autofill?> getLocalAutofill();
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.autofill.AutofillManager?> getLocalAutofillManager();
- method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.autofill.AutofillTree> getLocalAutofillTree();
+ method @Deprecated public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.autofill.AutofillTree> getLocalAutofillTree();
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.Clipboard> getLocalClipboard();
method @Deprecated public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.ClipboardManager> getLocalClipboardManager();
method public static androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Boolean> getLocalCursorBlinkEnabled();
@@ -3223,9 +3223,9 @@
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.ViewConfiguration> getLocalViewConfiguration();
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.WindowInfo> getLocalWindowInfo();
property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.AccessibilityManager?> LocalAccessibilityManager;
- property @SuppressCompatibility @androidx.compose.ui.ExperimentalComposeUiApi public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.autofill.Autofill?> LocalAutofill;
+ property @Deprecated public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.autofill.Autofill?> LocalAutofill;
property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.autofill.AutofillManager?> LocalAutofillManager;
- property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.autofill.AutofillTree> LocalAutofillTree;
+ property @Deprecated public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.autofill.AutofillTree> LocalAutofillTree;
property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.Clipboard> LocalClipboard;
property @Deprecated public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.ClipboardManager> LocalClipboardManager;
property public static final androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Boolean> LocalCursorBlinkEnabled;
diff --git a/compose/ui/ui/api/restricted_current.txt b/compose/ui/ui/api/restricted_current.txt
index 8b114f0..f381fac 100644
--- a/compose/ui/ui/api/restricted_current.txt
+++ b/compose/ui/ui/api/restricted_current.txt
@@ -220,9 +220,9 @@
package androidx.compose.ui.autofill {
- public interface Autofill {
- method public void cancelAutofillForNode(androidx.compose.ui.autofill.AutofillNode autofillNode);
- method public void requestAutofillForNode(androidx.compose.ui.autofill.AutofillNode autofillNode);
+ @Deprecated public interface Autofill {
+ method @Deprecated public void cancelAutofillForNode(androidx.compose.ui.autofill.AutofillNode autofillNode);
+ method @Deprecated public void requestAutofillForNode(androidx.compose.ui.autofill.AutofillNode autofillNode);
}
public abstract class AutofillManager {
@@ -230,64 +230,64 @@
method public abstract void commit();
}
- public final class AutofillNode {
- ctor public AutofillNode(optional java.util.List<? extends androidx.compose.ui.autofill.AutofillType> autofillTypes, optional androidx.compose.ui.geometry.Rect? boundingBox, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit>? onFill);
- method public java.util.List<androidx.compose.ui.autofill.AutofillType> getAutofillTypes();
- method public androidx.compose.ui.geometry.Rect? getBoundingBox();
- method public int getId();
- method public kotlin.jvm.functions.Function1<java.lang.String,kotlin.Unit>? getOnFill();
- method public void setBoundingBox(androidx.compose.ui.geometry.Rect?);
- property public final java.util.List<androidx.compose.ui.autofill.AutofillType> autofillTypes;
- property public final androidx.compose.ui.geometry.Rect? boundingBox;
- property public final int id;
- property public final kotlin.jvm.functions.Function1<java.lang.String,kotlin.Unit>? onFill;
+ @Deprecated public final class AutofillNode {
+ ctor @Deprecated public AutofillNode(optional java.util.List<? extends androidx.compose.ui.autofill.AutofillType> autofillTypes, optional androidx.compose.ui.geometry.Rect? boundingBox, kotlin.jvm.functions.Function1<? super java.lang.String,kotlin.Unit>? onFill);
+ method @Deprecated public java.util.List<androidx.compose.ui.autofill.AutofillType> getAutofillTypes();
+ method @Deprecated public androidx.compose.ui.geometry.Rect? getBoundingBox();
+ method @Deprecated public int getId();
+ method @Deprecated public kotlin.jvm.functions.Function1<java.lang.String,kotlin.Unit>? getOnFill();
+ method @Deprecated public void setBoundingBox(androidx.compose.ui.geometry.Rect?);
+ property @Deprecated public final java.util.List<androidx.compose.ui.autofill.AutofillType> autofillTypes;
+ property @Deprecated public final androidx.compose.ui.geometry.Rect? boundingBox;
+ property @Deprecated public final int id;
+ property @Deprecated public final kotlin.jvm.functions.Function1<java.lang.String,kotlin.Unit>? onFill;
}
- public final class AutofillTree {
- ctor public AutofillTree();
- method public java.util.Map<java.lang.Integer,androidx.compose.ui.autofill.AutofillNode> getChildren();
- method public kotlin.Unit? performAutofill(int id, String value);
- method public operator void plusAssign(androidx.compose.ui.autofill.AutofillNode autofillNode);
- property public final java.util.Map<java.lang.Integer,androidx.compose.ui.autofill.AutofillNode> children;
+ @Deprecated public final class AutofillTree {
+ ctor @Deprecated public AutofillTree();
+ method @Deprecated public java.util.Map<java.lang.Integer,androidx.compose.ui.autofill.AutofillNode> getChildren();
+ method @Deprecated public kotlin.Unit? performAutofill(int id, String value);
+ method @Deprecated public operator void plusAssign(androidx.compose.ui.autofill.AutofillNode autofillNode);
+ property @Deprecated public final java.util.Map<java.lang.Integer,androidx.compose.ui.autofill.AutofillNode> children;
}
- public enum AutofillType {
- enum_constant public static final androidx.compose.ui.autofill.AutofillType AddressAuxiliaryDetails;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType AddressCountry;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType AddressLocality;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType AddressRegion;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType AddressStreet;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType BirthDateDay;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType BirthDateFull;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType BirthDateMonth;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType BirthDateYear;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType CreditCardExpirationDate;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType CreditCardExpirationDay;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType CreditCardExpirationMonth;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType CreditCardExpirationYear;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType CreditCardNumber;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType CreditCardSecurityCode;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType EmailAddress;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType Gender;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType NewPassword;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType NewUsername;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType Password;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PersonFirstName;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PersonFullName;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PersonLastName;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PersonMiddleInitial;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PersonMiddleName;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PersonNamePrefix;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PersonNameSuffix;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PhoneCountryCode;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PhoneNumber;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PhoneNumberDevice;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PhoneNumberNational;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PostalAddress;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PostalCode;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType PostalCodeExtended;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType SmsOtpCode;
- enum_constant public static final androidx.compose.ui.autofill.AutofillType Username;
+ @Deprecated public enum AutofillType {
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType AddressAuxiliaryDetails;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType AddressCountry;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType AddressLocality;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType AddressRegion;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType AddressStreet;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType BirthDateDay;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType BirthDateFull;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType BirthDateMonth;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType BirthDateYear;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType CreditCardExpirationDate;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType CreditCardExpirationDay;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType CreditCardExpirationMonth;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType CreditCardExpirationYear;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType CreditCardNumber;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType CreditCardSecurityCode;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType EmailAddress;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType Gender;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType NewPassword;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType NewUsername;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType Password;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType PersonFirstName;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType PersonFullName;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType PersonLastName;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType PersonMiddleInitial;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType PersonMiddleName;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType PersonNamePrefix;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType PersonNameSuffix;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType PhoneCountryCode;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType PhoneNumber;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType PhoneNumberDevice;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType PhoneNumberNational;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType PostalAddress;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType PostalCode;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType PostalCodeExtended;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType SmsOtpCode;
+ enum_constant @Deprecated public static final androidx.compose.ui.autofill.AutofillType Username;
}
@kotlin.jvm.JvmInline public final value class ContentDataType {
@@ -3256,9 +3256,9 @@
public final class CompositionLocalsKt {
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.AccessibilityManager?> getLocalAccessibilityManager();
- method @SuppressCompatibility @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.autofill.Autofill?> getLocalAutofill();
+ method @Deprecated public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.autofill.Autofill?> getLocalAutofill();
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.autofill.AutofillManager?> getLocalAutofillManager();
- method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.autofill.AutofillTree> getLocalAutofillTree();
+ method @Deprecated public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.autofill.AutofillTree> getLocalAutofillTree();
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.Clipboard> getLocalClipboard();
method @Deprecated public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.ClipboardManager> getLocalClipboardManager();
method public static androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Boolean> getLocalCursorBlinkEnabled();
@@ -3277,9 +3277,9 @@
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.ViewConfiguration> getLocalViewConfiguration();
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.WindowInfo> getLocalWindowInfo();
property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.AccessibilityManager?> LocalAccessibilityManager;
- property @SuppressCompatibility @androidx.compose.ui.ExperimentalComposeUiApi public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.autofill.Autofill?> LocalAutofill;
+ property @Deprecated public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.autofill.Autofill?> LocalAutofill;
property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.autofill.AutofillManager?> LocalAutofillManager;
- property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.autofill.AutofillTree> LocalAutofillTree;
+ property @Deprecated public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.autofill.AutofillTree> LocalAutofillTree;
property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.Clipboard> LocalClipboard;
property @Deprecated public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.ClipboardManager> LocalClipboardManager;
property public static final androidx.compose.runtime.ProvidableCompositionLocal<java.lang.Boolean> LocalCursorBlinkEnabled;
diff --git a/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/autofill/AndroidAutofillBenchmark.kt b/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/autofill/AndroidAutofillBenchmark.kt
index d4ba384..6677cd4 100644
--- a/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/autofill/AndroidAutofillBenchmark.kt
+++ b/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/autofill/AndroidAutofillBenchmark.kt
@@ -41,13 +41,15 @@
@get:Rule val benchmarkRule = BenchmarkRule()
- private lateinit var autofillTree: androidx.compose.ui.autofill.AutofillTree
+ private lateinit var autofillTree:
+ @Suppress("Deprecation")
+ androidx.compose.ui.autofill.AutofillTree
private lateinit var composeView: View
@Before
fun setup() {
composeTestRule.setContent {
- autofillTree = LocalAutofillTree.current
+ autofillTree = @Suppress("Deprecation") LocalAutofillTree.current
composeView = LocalView.current
}
}
@@ -59,6 +61,7 @@
composeTestRule.runOnUiThread {
// Arrange.
val autofillNode =
+ @Suppress("Deprecation")
androidx.compose.ui.autofill.AutofillNode(
onFill = {},
autofillTypes =
diff --git a/compose/ui/ui/build.gradle b/compose/ui/ui/build.gradle
index 73d3551..ed7c760 100644
--- a/compose/ui/ui/build.gradle
+++ b/compose/ui/ui/build.gradle
@@ -24,7 +24,6 @@
import androidx.build.KotlinTarget
import androidx.build.LibraryType
-import androidx.build.KmpPlatformsKt
import androidx.build.PlatformIdentifier
import static androidx.inspection.gradle.InspectionPluginKt.packageInspector
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/autofill/ExplicitAutofillTypesDemo.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/autofill/ExplicitAutofillTypesDemo.kt
index f9e568a..fdb8704c 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/autofill/ExplicitAutofillTypesDemo.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/autofill/ExplicitAutofillTypesDemo.kt
@@ -30,7 +30,6 @@
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
-import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.layout.boundsInWindow
@@ -49,7 +48,11 @@
Column {
Autofill(
- autofillTypes = listOf(androidx.compose.ui.autofill.AutofillType.PersonFullName),
+ autofillTypes =
+ listOf(
+ @Suppress("Deprecation")
+ androidx.compose.ui.autofill.AutofillType.PersonFullName
+ ),
onFill = { name = TextFieldValue(it) }
) {
OutlinedTextField(
@@ -62,7 +65,10 @@
Spacer(Modifier.height(10.dp))
Autofill(
- autofillTypes = listOf(androidx.compose.ui.autofill.AutofillType.EmailAddress),
+ autofillTypes =
+ listOf(
+ @Suppress("Deprecation") androidx.compose.ui.autofill.AutofillType.EmailAddress
+ ),
onFill = { email = TextFieldValue(it) }
) {
OutlinedTextField(
@@ -76,14 +82,15 @@
@Composable
private fun Autofill(
- autofillTypes: List<androidx.compose.ui.autofill.AutofillType>,
+ autofillTypes: List<@Suppress("Deprecation") androidx.compose.ui.autofill.AutofillType>,
onFill: ((String) -> Unit),
content: @Composable BoxScope.() -> Unit
) {
- val autofill = @OptIn(ExperimentalComposeUiApi::class) LocalAutofill.current
- val autofillTree = LocalAutofillTree.current
+ val autofill = @Suppress("Deprecation") LocalAutofill.current
+ val autofillTree = @Suppress("Deprecation") LocalAutofillTree.current
val autofillNode =
remember(autofillTypes, onFill) {
+ @Suppress("Deprecation")
androidx.compose.ui.autofill.AutofillNode(
onFill = onFill,
autofillTypes = autofillTypes
diff --git a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/autofill/MixedOldNewAutofillDemo.kt b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/autofill/MixedOldNewAutofillDemo.kt
index fd19751..5c8044d 100644
--- a/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/autofill/MixedOldNewAutofillDemo.kt
+++ b/compose/ui/ui/integration-tests/ui-demos/src/main/java/androidx/compose/ui/demos/autofill/MixedOldNewAutofillDemo.kt
@@ -32,7 +32,6 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.remember
-import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.autofill.ContentType
import androidx.compose.ui.focus.onFocusChanged
@@ -69,10 +68,11 @@
)
// Text field using old autofill API.
- val autofill = @OptIn(ExperimentalComposeUiApi::class) LocalAutofill.current
- val autofillTree = LocalAutofillTree.current
+ val autofill = @Suppress("DEPRECATION") LocalAutofill.current
+ val autofillTree = @Suppress("DEPRECATION") LocalAutofillTree.current
val textState = rememberTextFieldState()
val autofillNode = remember {
+ @Suppress("DEPRECATION")
androidx.compose.ui.autofill.AutofillNode(
onFill = { textState.edit { replace(0, length, it) } },
autofillTypes = listOf(androidx.compose.ui.autofill.AutofillType.Password),
diff --git a/compose/ui/ui/proguard-rules.pro b/compose/ui/ui/proguard-rules.pro
index fa5b4c3..36af3b2 100644
--- a/compose/ui/ui/proguard-rules.pro
+++ b/compose/ui/ui/proguard-rules.pro
@@ -35,7 +35,7 @@
# Keep all the functions created to throw an exception. We don't want these functions to be
# inlined in any way, which R8 will do by default. The whole point of these functions is to
# reduce the amount of code generated at the call site.
--keep,allowshrinking,allowobfuscation class androidx.compose.**.* {
+-keepclassmembers,allowshrinking,allowobfuscation class androidx.compose.**.* {
static void throw*Exception(...);
static void throw*ExceptionForNullCheck(...);
# For methods returning Nothing
@@ -52,3 +52,4 @@
-keepnames class androidx.compose.ui.input.pointer.PointerInputEventHandler {
*;
}
+
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt
index 164d5628..5758c8a 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt
@@ -149,13 +149,13 @@
import androidx.compose.ui.semantics.paneTitle
import androidx.compose.ui.semantics.role
import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.semantics.semanticsId
import androidx.compose.ui.semantics.stateDescription
import androidx.compose.ui.semantics.testTag
import androidx.compose.ui.semantics.testTagsAsResourceId
import androidx.compose.ui.semantics.textSelectionRange
import androidx.compose.ui.semantics.traversalIndex
import androidx.compose.ui.test.SemanticsMatcher.Companion.expectValue
-import androidx.compose.ui.test.SemanticsNodeInteraction
import androidx.compose.ui.test.TestActivity
import androidx.compose.ui.test.assert
import androidx.compose.ui.test.assertContentDescriptionEquals
@@ -210,6 +210,7 @@
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.ACTION_SET_TEXT
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_IME_ENTER
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.FOCUS_INPUT
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_CHARACTER
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.RangeInfoCompat.RANGE_TYPE_FLOAT
import androidx.core.view.accessibility.AccessibilityNodeProviderCompat
@@ -306,7 +307,7 @@
BasicText("ToggleableText")
}
}
- val virtualId = rule.onNodeWithTag(tag).semanticsId
+ val virtualId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { createAccessibilityNodeInfo(virtualId) }
@@ -386,7 +387,7 @@
}
}
- val passwordFieldId = rule.onNodeWithTag(tag, true).semanticsId
+ val passwordFieldId = rule.onNodeWithTag(tag, true).semanticsId()
// Act.
val info = rule.runOnIdle { createAccessibilityNodeInfo(passwordFieldId) }
@@ -423,7 +424,7 @@
repeat(5) { DropdownMenuItem(onClick = {}) { Text("Menu Item $it") } }
}
}
- val virtualId = rule.onNodeWithTag(tag, true).semanticsId
+ val virtualId = rule.onNodeWithTag(tag, true).semanticsId()
// Act.
val info = rule.runOnIdle { createAccessibilityNodeInfo(virtualId) }
@@ -452,7 +453,7 @@
BasicText("Text")
}
}
- val virtualId = rule.onNodeWithTag(tag).semanticsId
+ val virtualId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { createAccessibilityNodeInfo(virtualId) }
@@ -483,7 +484,7 @@
BasicText("Text")
}
}
- val virtualId = rule.onNodeWithTag(tag).semanticsId
+ val virtualId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { createAccessibilityNodeInfo(virtualId) }
@@ -516,7 +517,7 @@
BasicText("Text")
}
}
- val virtualId = rule.onNodeWithTag(tag).semanticsId
+ val virtualId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { createAccessibilityNodeInfo(virtualId) }
@@ -545,7 +546,7 @@
Text("Filter chip")
}
}
- val virtualId = rule.onNodeWithTag(tag).semanticsId
+ val virtualId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { createAccessibilityNodeInfo(virtualId) }
@@ -574,7 +575,7 @@
setContent {
Box(Modifier.semantics { pageUp { true } }.testTag(tag)) { BasicText("Text") }
}
- val virtualId = rule.onNodeWithTag(tag).semanticsId
+ val virtualId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { createAccessibilityNodeInfo(virtualId) }
@@ -603,7 +604,7 @@
BasicText("Text")
}
}
- val virtualId = rule.onNodeWithTag(tag).semanticsId
+ val virtualId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { createAccessibilityNodeInfo(virtualId) }
@@ -623,7 +624,7 @@
fun testCreateAccessibilityNodeInfo_numberPicker_expectedClassName() {
// Arrange.
setContent { Box(Modifier.semantics { role = Role.ValuePicker }.testTag(tag)) }
- val virtualId = rule.onNodeWithTag(tag).semanticsId
+ val virtualId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { createAccessibilityNodeInfo(virtualId) }
@@ -638,7 +639,7 @@
fun testCreateAccessibilityNodeInfo_progressIndicator_determinate() {
// Arrange.
setContent { Box(Modifier.progressSemantics(0.5f).testTag(tag)) { BasicText("Text") } }
- val virtualId = rule.onNodeWithTag(tag).semanticsId
+ val virtualId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { createAccessibilityNodeInfo(virtualId) }
@@ -663,7 +664,7 @@
fun testCreateAccessibilityNodeInfo_progressIndicator_determinate_indeterminate() {
// Arrange.
setContent { Box(Modifier.progressSemantics().testTag(tag)) { BasicText("Text") } }
- val virtualId = rule.onNodeWithTag(tag).semanticsId
+ val virtualId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { createAccessibilityNodeInfo(virtualId) }
@@ -691,7 +692,7 @@
onValueChange = { value = it }
)
}
- val virtualId = rule.onNodeWithTag(tag).semanticsId
+ val virtualId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { createAccessibilityNodeInfo(virtualId) }
@@ -735,7 +736,7 @@
fun emptyTextField_hasStateDescription() {
setContent { BasicTextField(rememberTextFieldState(), modifier = Modifier.testTag(tag)) }
- val virtualId = rule.onNodeWithTag(tag).semanticsId
+ val virtualId = rule.onNodeWithTag(tag).semanticsId()
val info = rule.runOnIdle { createAccessibilityNodeInfo(virtualId) }
rule.runOnIdle {
@@ -757,7 +758,7 @@
}
}
- val virtualId = rule.onNodeWithTag(tag).semanticsId
+ val virtualId = rule.onNodeWithTag(tag).semanticsId()
val info = rule.runOnIdle { createAccessibilityNodeInfo(virtualId) }
rule.runOnIdle {
@@ -782,7 +783,7 @@
)
}
- val virtualId = rule.onNodeWithTag(tag).semanticsId
+ val virtualId = rule.onNodeWithTag(tag).semanticsId()
val info = rule.runOnIdle { createAccessibilityNodeInfo(virtualId) }
rule.runOnIdle { with(info) { assertThat(stateDescription).isNull() } }
@@ -808,7 +809,7 @@
)
}
- val virtualId = rule.onNodeWithTag(tag).semanticsId
+ val virtualId = rule.onNodeWithTag(tag).semanticsId()
val info = rule.runOnIdle { createAccessibilityNodeInfo(virtualId) }
rule.runOnIdle { with(info) { assertThat(stateDescription).isNull() } }
@@ -819,7 +820,7 @@
// Arrange.
val text = "Test"
setContent { BasicText(text = text) }
- val virtualId = rule.onNodeWithText(text).semanticsId
+ val virtualId = rule.onNodeWithText(text).semanticsId()
// Act.
val info = rule.runOnIdle { createAccessibilityNodeInfo(virtualId) }
@@ -832,7 +833,7 @@
fun testCreateAccessibilityNodeInfo_forFocusable_notFocused() {
// Arrange.
setContent { Box(Modifier.testTag(tag).focusable()) { BasicText("focusable") } }
- val virtualId = rule.onNodeWithTag(tag).assert(expectValue(Focused, false)).semanticsId
+ val virtualId = rule.onNodeWithTag(tag).assert(expectValue(Focused, false)).semanticsId()
// Act.
val info = rule.runOnIdle { createAccessibilityNodeInfo(virtualId) }
@@ -860,7 +861,7 @@
}
}
rule.runOnIdle { focusRequester.requestFocus() }
- val virtualId = rule.onNodeWithTag(tag).assert(expectValue(Focused, true)).semanticsId
+ val virtualId = rule.onNodeWithTag(tag).assert(expectValue(Focused, true)).semanticsId()
// Act.
val info = rule.runOnIdle { createAccessibilityNodeInfo(virtualId) }
@@ -921,8 +922,8 @@
Row { Text(overlaidText) }
}
}
- val node3VirtualId = rule.onNodeWithText(text3).semanticsId
- val overlaidNodeVirtualId = rule.onNodeWithText(overlaidText).semanticsId
+ val node3VirtualId = rule.onNodeWithText(text3).semanticsId()
+ val overlaidNodeVirtualId = rule.onNodeWithText(overlaidText).semanticsId()
// Act.
val ani3 = rule.runOnIdle { createAccessibilityNodeInfo(node3VirtualId) }
@@ -957,8 +958,8 @@
Row { Text(overlaidText) }
}
}
- val node3VirtualId = rule.onNodeWithText(text3).semanticsId
- val overlaidNodeVirtualId = rule.onNodeWithText(overlaidText).semanticsId
+ val node3VirtualId = rule.onNodeWithText(text3).semanticsId()
+ val overlaidNodeVirtualId = rule.onNodeWithText(overlaidText).semanticsId()
// Act.
val ani3 = rule.runOnIdle { createAccessibilityNodeInfo(node3VirtualId) }
@@ -999,8 +1000,8 @@
}
}
}
- val rowVirtualId = rule.onNodeWithTag(clickableRowTag).semanticsId
- val buttonId = rule.onNodeWithTag(clickableButtonTag).semanticsId
+ val rowVirtualId = rule.onNodeWithTag(clickableRowTag).semanticsId()
+ val buttonId = rule.onNodeWithTag(clickableButtonTag).semanticsId()
// Act.
val rowANI = rule.runOnIdle { createAccessibilityNodeInfo(rowVirtualId) }
@@ -1047,8 +1048,8 @@
}
}
- val titleId = rule.onNodeWithTag(clickableTitle).semanticsId
- val firstElementId = rule.onNodeWithTag(clickableFirstListElement).semanticsId
+ val titleId = rule.onNodeWithTag(clickableTitle).semanticsId()
+ val firstElementId = rule.onNodeWithTag(clickableFirstListElement).semanticsId()
// Act.
val titleANI = rule.runOnIdle { createAccessibilityNodeInfo(titleId) }
@@ -1104,10 +1105,10 @@
}
}
}
- val topText1 = rule.onNodeWithText(topSampleText + 1).semanticsId
- val topText2 = rule.onNodeWithText(topSampleText + 2).semanticsId
- val bottomText1 = rule.onNodeWithText(bottomSampleText + 1).semanticsId
- val bottomText2 = rule.onNodeWithText(bottomSampleText + 2).semanticsId
+ val topText1 = rule.onNodeWithText(topSampleText + 1).semanticsId()
+ val topText2 = rule.onNodeWithText(topSampleText + 2).semanticsId()
+ val bottomText1 = rule.onNodeWithText(bottomSampleText + 1).semanticsId()
+ val bottomText2 = rule.onNodeWithText(bottomSampleText + 2).semanticsId()
// Act.
rule.waitForIdle()
@@ -1149,10 +1150,10 @@
}
}
}
- val topText1 = rule.onNodeWithText(topSampleText + 1).semanticsId
- val topText2 = rule.onNodeWithText(topSampleText + 2).semanticsId
- val bottomText1 = rule.onNodeWithText(bottomSampleText + 1).semanticsId
- val bottomText2 = rule.onNodeWithText(bottomSampleText + 2).semanticsId
+ val topText1 = rule.onNodeWithText(topSampleText + 1).semanticsId()
+ val topText2 = rule.onNodeWithText(topSampleText + 2).semanticsId()
+ val bottomText1 = rule.onNodeWithText(bottomSampleText + 1).semanticsId()
+ val bottomText2 = rule.onNodeWithText(bottomSampleText + 2).semanticsId()
// Act.
rule.waitForIdle()
@@ -1197,8 +1198,8 @@
}
}
}
- val bottomText1 = rule.onNodeWithText(bottomSampleText + 1).semanticsId
- val bottomText2 = rule.onNodeWithText(bottomSampleText + 2).semanticsId
+ val bottomText1 = rule.onNodeWithText(bottomSampleText + 1).semanticsId()
+ val bottomText2 = rule.onNodeWithText(bottomSampleText + 2).semanticsId()
// Act.
val bottomText1ANI = rule.runOnIdle { createAccessibilityNodeInfo(bottomText1) }
@@ -1240,10 +1241,10 @@
)
}
}
- val bottomText1 = rule.onNodeWithText(bottomSampleText + 1).semanticsId
- val bottomText2 = rule.onNodeWithText(bottomSampleText + 2).semanticsId
- val bottomText3 = rule.onNodeWithText(bottomSampleText + 3).semanticsId
- val topText3 = rule.onNodeWithText(topSampleText + 3).semanticsId
+ val bottomText1 = rule.onNodeWithText(bottomSampleText + 1).semanticsId()
+ val bottomText2 = rule.onNodeWithText(bottomSampleText + 2).semanticsId()
+ val bottomText3 = rule.onNodeWithText(bottomSampleText + 3).semanticsId()
+ val topText3 = rule.onNodeWithText(topSampleText + 3).semanticsId()
// Act.
rule.waitForIdle()
@@ -1296,8 +1297,8 @@
)
}
}
- val bottomText1 = rule.onNodeWithText(bottomSampleText + 1).semanticsId
- val bottomText2 = rule.onNodeWithText(bottomSampleText + 2).semanticsId
+ val bottomText1 = rule.onNodeWithText(bottomSampleText + 1).semanticsId()
+ val bottomText2 = rule.onNodeWithText(bottomSampleText + 2).semanticsId()
// Act.
val bottomText1ANI = rule.runOnIdle { createAccessibilityNodeInfo(bottomText1) }
@@ -1336,8 +1337,8 @@
}
}
}
- val node1 = rule.onNodeWithText(text1).semanticsId
- val overlaidNode = rule.onNodeWithText(overlaidText).semanticsId
+ val node1 = rule.onNodeWithText(text1).semanticsId()
+ val overlaidNode = rule.onNodeWithText(overlaidText).semanticsId()
// Act.
val info = rule.runOnIdle { createAccessibilityNodeInfo(overlaidNode) }
@@ -1389,12 +1390,12 @@
Row { Text(text = text0) }
}
}
- val virtualViewId0 = rule.onNodeWithText(text0).semanticsId
- val virtualViewId1 = rule.onNodeWithText(text1).semanticsId
- val virtualViewId2 = rule.onNodeWithText(text2).semanticsId
- val virtualViewId3 = rule.onNodeWithText(text3).semanticsId
- val virtualViewId4 = rule.onNodeWithText(text4).semanticsId
- val virtualViewId5 = rule.onNodeWithText(text5).semanticsId
+ val virtualViewId0 = rule.onNodeWithText(text0).semanticsId()
+ val virtualViewId1 = rule.onNodeWithText(text1).semanticsId()
+ val virtualViewId2 = rule.onNodeWithText(text2).semanticsId()
+ val virtualViewId3 = rule.onNodeWithText(text3).semanticsId()
+ val virtualViewId4 = rule.onNodeWithText(text4).semanticsId()
+ val virtualViewId5 = rule.onNodeWithText(text5).semanticsId()
// Act.
rule.waitForIdle()
@@ -1444,8 +1445,8 @@
}
}
}
- val node3Id = rule.onNodeWithText(text3).semanticsId
- val overlayId = rule.onNodeWithText(overlaidText).semanticsId
+ val node3Id = rule.onNodeWithText(text3).semanticsId()
+ val overlayId = rule.onNodeWithText(overlaidText).semanticsId()
// Act.
val node3ANI = rule.runOnIdle { createAccessibilityNodeInfo(node3Id) }
@@ -1486,8 +1487,8 @@
}
}
}
- val node3Id = rule.onNodeWithText(text3).semanticsId
- val overlayId = rule.onNodeWithText(overlaidText).semanticsId
+ val node3Id = rule.onNodeWithText(text3).semanticsId()
+ val overlayId = rule.onNodeWithText(overlaidText).semanticsId()
// Act.
val node3ANI = rule.runOnIdle { createAccessibilityNodeInfo(node3Id) }
@@ -1506,8 +1507,8 @@
TopAppBar(title = { Text(text = topAppBarText) })
}
- val textBoxId = rule.onNodeWithTag(textBoxTag).semanticsId
- val topAppBarId = rule.onNodeWithText(topAppBarText).semanticsId
+ val textBoxId = rule.onNodeWithTag(textBoxTag).semanticsId()
+ val topAppBarId = rule.onNodeWithText(topAppBarText).semanticsId()
// Act.
val topAppBarANI = rule.runOnIdle { createAccessibilityNodeInfo(topAppBarId) }
@@ -1530,9 +1531,9 @@
repeat(100) { Text(sampleText + counter++) }
}
}
- val topAppBarId = rule.onNodeWithText(topAppBarText).semanticsId
- val node1Id = rule.onNodeWithText(sampleText1).semanticsId
- val node2Id = rule.onNodeWithText(sampleText2).semanticsId
+ val topAppBarId = rule.onNodeWithText(topAppBarText).semanticsId()
+ val node1Id = rule.onNodeWithText(sampleText1).semanticsId()
+ val node2Id = rule.onNodeWithText(sampleText2).semanticsId()
// Act.
rule.waitForIdle()
@@ -1569,9 +1570,9 @@
bottomBar = { BottomAppBar { Text(bottomAppBarText) } }
)
}
- val topAppBarId = rule.onNodeWithText(topAppBarText).semanticsId
- val contentId = rule.onNodeWithText(contentText).semanticsId
- val bottomAppBarId = rule.onNodeWithText(bottomAppBarText).semanticsId
+ val topAppBarId = rule.onNodeWithText(topAppBarText).semanticsId()
+ val contentId = rule.onNodeWithText(contentText).semanticsId()
+ val bottomAppBarId = rule.onNodeWithText(bottomAppBarText).semanticsId()
// Act.
rule.waitForIdle()
@@ -1610,9 +1611,9 @@
content = { padding -> Text(contentText, modifier = Modifier.padding(padding)) }
)
}
- val face1Id = rule.onNodeWithContentDescription(content1).semanticsId
- val face3Id = rule.onNodeWithContentDescription(content3).semanticsId
- val contentId = rule.onNodeWithText(contentText).semanticsId
+ val face1Id = rule.onNodeWithContentDescription(content1).semanticsId()
+ val face3Id = rule.onNodeWithContentDescription(content3).semanticsId()
+ val contentId = rule.onNodeWithText(contentText).semanticsId()
// Act.
rule.waitForIdle()
@@ -1665,10 +1666,10 @@
Box { BasicText("Child Three", Modifier.testTag(childThreeTag)) }
}
}
- val parentBox1Id = rule.onNodeWithTag(parentBox1Tag).semanticsId
- val childOneId = rule.onNodeWithTag(childOneTag, useUnmergedTree = true).semanticsId
- val childTwoId = rule.onNodeWithTag(childTwoTag, useUnmergedTree = true).semanticsId
- val childThreeId = rule.onNodeWithTag(childThreeTag, useUnmergedTree = true).semanticsId
+ val parentBox1Id = rule.onNodeWithTag(parentBox1Tag).semanticsId()
+ val childOneId = rule.onNodeWithTag(childOneTag, useUnmergedTree = true).semanticsId()
+ val childTwoId = rule.onNodeWithTag(childTwoTag, useUnmergedTree = true).semanticsId()
+ val childThreeId = rule.onNodeWithTag(childThreeTag, useUnmergedTree = true).semanticsId()
// Act.
rule.waitForIdle()
@@ -1727,9 +1728,9 @@
)
}
- val topAppBarId = rule.onNodeWithText(topAppBarText).semanticsId
- val firstContentId = rule.onNodeWithTag(firstContentText).semanticsId
- val lastContentId = rule.onNodeWithTag(lastContentText).semanticsId
+ val topAppBarId = rule.onNodeWithText(topAppBarText).semanticsId()
+ val firstContentId = rule.onNodeWithTag(firstContentText).semanticsId()
+ val lastContentId = rule.onNodeWithTag(lastContentText).semanticsId()
// Act.
rule.waitForIdle()
@@ -1765,8 +1766,8 @@
}
}
val root = rule.onNodeWithTag(rootTag).fetchSemanticsNode()
- val child1Id = rule.onNodeWithTag(childTag1).semanticsId
- val child2Id = rule.onNodeWithTag(childTag2).semanticsId
+ val child1Id = rule.onNodeWithTag(childTag1).semanticsId()
+ val child2Id = rule.onNodeWithTag(childTag2).semanticsId()
// Act.
rule.waitForIdle()
@@ -1794,8 +1795,8 @@
}
}
val root = rule.onNodeWithTag(rootTag).fetchSemanticsNode()
- val child1Id = rule.onNodeWithTag(childTag1).semanticsId
- val child2Id = rule.onNodeWithTag(childTag2).semanticsId
+ val child1Id = rule.onNodeWithTag(childTag1).semanticsId()
+ val child2Id = rule.onNodeWithTag(childTag2).semanticsId()
// Act.
rule.waitForIdle()
@@ -1825,8 +1826,8 @@
}
}
val root = rule.onNodeWithTag(rootTag).fetchSemanticsNode()
- val child1Id = rule.onNodeWithTag(childTag1).semanticsId
- val child2Id = rule.onNodeWithTag(childTag2).semanticsId
+ val child1Id = rule.onNodeWithTag(childTag1).semanticsId()
+ val child2Id = rule.onNodeWithTag(childTag2).semanticsId()
// Act.
rule.waitForIdle()
@@ -1856,8 +1857,8 @@
}
}
val root = rule.onNodeWithTag(rootTag).fetchSemanticsNode()
- val child1Id = rule.onNodeWithTag(childTag1).semanticsId
- val child2Id = rule.onNodeWithTag(childTag2).semanticsId
+ val child1Id = rule.onNodeWithTag(childTag1).semanticsId()
+ val child2Id = rule.onNodeWithTag(childTag2).semanticsId()
// Act.
rule.waitForIdle()
@@ -1887,8 +1888,8 @@
}
}
val root = rule.onNodeWithTag(rootTag).fetchSemanticsNode()
- val child1 = rule.onNodeWithTag(childTag1).semanticsId
- val child2 = rule.onNodeWithTag(childTag2).semanticsId
+ val child1 = rule.onNodeWithTag(childTag1).semanticsId()
+ val child2 = rule.onNodeWithTag(childTag2).semanticsId()
// Act.
rule.waitForIdle()
@@ -1928,8 +1929,8 @@
}
}
val root = rule.onNodeWithTag(rootTag).fetchSemanticsNode()
- val child1Id = rule.onNodeWithTag(childTag1).semanticsId
- val child2Id = rule.onNodeWithTag(childTag2).semanticsId
+ val child1Id = rule.onNodeWithTag(childTag1).semanticsId()
+ val child2Id = rule.onNodeWithTag(childTag2).semanticsId()
// Act.
val child2ANI = rule.runOnIdle { createAccessibilityNodeInfo(child2Id) }
@@ -1961,8 +1962,8 @@
}
}
val root = rule.onNodeWithTag(rootTag).fetchSemanticsNode()
- val child1Id = rule.onNodeWithTag(childTag1).semanticsId
- val child2Id = rule.onNodeWithTag(childTag2).semanticsId
+ val child1Id = rule.onNodeWithTag(childTag1).semanticsId()
+ val child2Id = rule.onNodeWithTag(childTag2).semanticsId()
// Act.
rule.waitForIdle()
@@ -1997,8 +1998,8 @@
}
}
val root = rule.onNodeWithTag(rootTag).fetchSemanticsNode()
- val child1Id = rule.onNodeWithTag(childTag1).semanticsId
- val child2Id = rule.onNodeWithTag(childTag2).semanticsId
+ val child1Id = rule.onNodeWithTag(childTag1).semanticsId()
+ val child2Id = rule.onNodeWithTag(childTag2).semanticsId()
// Act.
rule.waitForIdle()
@@ -2044,13 +2045,13 @@
}
}
val root = rule.onNodeWithTag(rootTag).fetchSemanticsNode()
- val child1Id = rule.onNodeWithText(childText1).semanticsId
- val child2Id = rule.onNodeWithText(childText2).semanticsId
- val child3Id = rule.onNodeWithText(childText3).semanticsId
+ val child1Id = rule.onNodeWithText(childText1).semanticsId()
+ val child2Id = rule.onNodeWithText(childText2).semanticsId()
+ val child3Id = rule.onNodeWithText(childText3).semanticsId()
- val rtlChild1Id = rule.onNodeWithText(rtlChildText1).semanticsId
- val rtlChild2Id = rule.onNodeWithText(rtlChildText2).semanticsId
- val rtlChild3Id = rule.onNodeWithText(rtlChildText3).semanticsId
+ val rtlChild1Id = rule.onNodeWithText(rtlChildText1).semanticsId()
+ val rtlChild2Id = rule.onNodeWithText(rtlChildText2).semanticsId()
+ val rtlChild3Id = rule.onNodeWithText(rtlChildText3).semanticsId()
// Act.
rule.waitForIdle()
@@ -2134,8 +2135,8 @@
androidComposeView.androidViewsHandler.layoutNodeToHolder[
colSemanticsNode.replacedChildren[1].layoutNode]
checkNotNull(viewHolder)
- val firstButtonId = rule.onNodeWithText(firstButtonText).semanticsId
- val lastButtonId = rule.onNodeWithText(lastButtonText).semanticsId
+ val firstButtonId = rule.onNodeWithText(firstButtonText).semanticsId()
+ val lastButtonId = rule.onNodeWithText(lastButtonText).semanticsId()
// Act.
rule.waitForIdle()
@@ -2232,9 +2233,9 @@
androidComposeView.androidViewsHandler.layoutNodeToHolder[
colSemanticsNode.replacedChildren[1].layoutNode]
checkNotNull(viewHolder) // Check that the View exists
- val firstButtonId = rule.onNodeWithText(firstButtonText).semanticsId
- val thirdButtonId = rule.onNodeWithText(thirdButtonText).semanticsId
- val fourthButtonId = rule.onNodeWithText(fourthButtonText).semanticsId
+ val firstButtonId = rule.onNodeWithText(firstButtonText).semanticsId()
+ val thirdButtonId = rule.onNodeWithText(thirdButtonText).semanticsId()
+ val fourthButtonId = rule.onNodeWithText(fourthButtonText).semanticsId()
// Act.
rule.waitForIdle()
@@ -2292,14 +2293,14 @@
assertThat(scrollState.value).isEqualTo(0)
val showOnScreen = android.R.id.accessibilityActionShowOnScreen
- val target1Id = rule.onNodeWithTag(target1Tag).semanticsId
+ val target1Id = rule.onNodeWithTag(target1Tag).semanticsId()
rule.runOnUiThread {
assertThat(provider.performAction(target1Id, showOnScreen, null)).isTrue()
}
rule.mainClock.advanceTimeBy(5000)
assertThat(scrollState.value).isGreaterThan(99)
- val target2Id = rule.onNodeWithTag(target2Tag).semanticsId
+ val target2Id = rule.onNodeWithTag(target2Tag).semanticsId()
rule.runOnUiThread {
assertThat(provider.performAction(target2Id, showOnScreen, null)).isTrue()
}
@@ -2329,7 +2330,7 @@
assertThat(lazyState.firstVisibleItemScrollOffset).isEqualTo(0)
val showOnScreen = android.R.id.accessibilityActionShowOnScreen
- val target1Id = rule.onNodeWithTag(target1Tag).semanticsId
+ val target1Id = rule.onNodeWithTag(target1Tag).semanticsId()
rule.runOnUiThread {
assertThat(provider.performAction(target1Id, showOnScreen, null)).isTrue()
}
@@ -2337,7 +2338,7 @@
assertThat(lazyState.firstVisibleItemIndex).isEqualTo(0)
assertThat(lazyState.firstVisibleItemScrollOffset).isGreaterThan(99)
- val target2Id = rule.onNodeWithTag(target2Tag).semanticsId
+ val target2Id = rule.onNodeWithTag(target2Tag).semanticsId()
rule.runOnUiThread {
assertThat(provider.performAction(target2Id, showOnScreen, null)).isTrue()
}
@@ -2383,7 +2384,7 @@
// influenced by or influencing the parent row.
// TODO(b/190865803): Is this the ultimate right behavior we want?
val showOnScreen = android.R.id.accessibilityActionShowOnScreen
- val target1Id = rule.onNodeWithTag(target1Tag).semanticsId
+ val target1Id = rule.onNodeWithTag(target1Tag).semanticsId()
rule.runOnUiThread {
assertThat(provider.performAction(target1Id, showOnScreen, null)).isTrue()
}
@@ -2392,7 +2393,7 @@
assertThat(lazyState.firstVisibleItemScrollOffset).isGreaterThan(99)
assertThat(parentLazyState.firstVisibleItemScrollOffset).isEqualTo(0)
- val target2Id = rule.onNodeWithTag(target2Tag).semanticsId
+ val target2Id = rule.onNodeWithTag(target2Tag).semanticsId()
rule.runOnUiThread {
assertThat(provider.performAction(target2Id, showOnScreen, null)).isTrue()
}
@@ -2406,7 +2407,8 @@
fun testPerformAction_focus() {
// Arrange.
setContent { Box(Modifier.testTag(tag).focusable()) { BasicText("focusable") } }
- val virtualViewId = rule.onNodeWithTag(tag).assert(expectValue(Focused, false)).semanticsId
+ val virtualViewId =
+ rule.onNodeWithTag(tag).assert(expectValue(Focused, false)).semanticsId()
// Act.
rule.runOnUiThread {
@@ -2431,7 +2433,7 @@
}
}
rule.runOnIdle { focusRequester.requestFocus() }
- val virtualViewId = rule.onNodeWithTag(tag).assert(expectValue(Focused, true)).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).assert(expectValue(Focused, true)).semanticsId()
// Act.
rule.runOnUiThread {
@@ -2454,7 +2456,7 @@
}
}
rule.onNodeWithTag(tag).assertIsDisplayed().assertIsOn()
- val toggleableNodeId = rule.onNodeWithTag(tag).semanticsId
+ val toggleableNodeId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val actionPerformed =
@@ -2481,7 +2483,7 @@
content = { BasicText("ToggleableText") }
)
}
- val toggleableId = rule.onNodeWithTag(tag).assertIsDisplayed().assertIsOn().semanticsId
+ val toggleableId = rule.onNodeWithTag(tag).assertIsDisplayed().assertIsOn().semanticsId()
// Act.
val actionPerformed =
@@ -2498,7 +2500,7 @@
setContent {
BasicTextField(modifier = Modifier.testTag(tag), value = "value", onValueChange = {})
}
- val textFieldNodeId = rule.onNodeWithTag(tag).assertIsDisplayed().semanticsId
+ val textFieldNodeId = rule.onNodeWithTag(tag).assertIsDisplayed().semanticsId()
// Act.
val actionPerformed =
@@ -2529,7 +2531,7 @@
onValueChange = { value = it }
)
}
- val textFieldId = rule.onNodeWithTag(tag).assertIsDisplayed().semanticsId
+ val textFieldId = rule.onNodeWithTag(tag).assertIsDisplayed().semanticsId()
val argument = Bundle()
argument.putInt(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_START_INT, 1)
argument.putInt(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_END_INT, 1)
@@ -2561,7 +2563,7 @@
)
}
}
- val textFieldId = rule.onNodeWithTag(tag).assert(expectValue(Focused, false)).semanticsId
+ val textFieldId = rule.onNodeWithTag(tag).assert(expectValue(Focused, false)).semanticsId()
// Act.
var actionPerformed =
@@ -2581,6 +2583,48 @@
}
@Test
+ fun testFindFocus_noInputFocus() {
+ // Arrange.
+ setContent {
+ Row {
+ // No focused item.
+ Box(Modifier.size(10.dp).focusable())
+ Box(Modifier.size(10.dp).focusable())
+ }
+ }
+
+ // Act.
+ val focusedNode = rule.runOnUiThread { provider.findFocus(FOCUS_INPUT) }
+
+ // Assert.
+ assertThat(focusedNode).isNull()
+ }
+
+ @Test
+ fun testFindFocus_hasInputFocus() {
+ // Arrange.
+ val focusRequester = FocusRequester()
+ setContent {
+ Row {
+ // Initially focused item.
+ Box(Modifier.size(10.dp).focusable())
+ Box(Modifier.testTag(tag).focusRequester(focusRequester).focusable()) {
+ BasicText("focusable")
+ }
+ }
+ }
+ rule.runOnIdle { focusRequester.requestFocus() }
+ val virtualViewId = rule.onNodeWithTag(tag).assert(expectValue(Focused, true)).semanticsId()
+ val expectedNode = provider.createAccessibilityNodeInfo(virtualViewId)
+
+ // Act.
+ val actualNode = rule.runOnUiThread { provider.findFocus(FOCUS_INPUT) }
+
+ // Assert.
+ assertThat(actualNode).isEqualTo(expectedNode)
+ }
+
+ @Test
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
@Suppress("DEPRECATION")
fun testAddExtraDataToAccessibilityNodeInfo_notMerged() {
@@ -2636,7 +2680,7 @@
fun getSemanticsNodeIdFromExtraData() {
// Arrange.
setContent { BasicText("texy") }
- val textId = rule.onNodeWithText("texy").semanticsId
+ val textId = rule.onNodeWithText("texy").semanticsId()
val info = AccessibilityNodeInfoCompat.obtain()
val argument = Bundle()
val idKey = "androidx.compose.ui.semantics.id"
@@ -2654,7 +2698,7 @@
fun sendClickedEvent_whenClick() {
// Arrange.
setContent { Box(Modifier.clickable(onClick = {}).testTag(tag)) { BasicText("Text") } }
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val actionPerformed =
@@ -2684,7 +2728,7 @@
setContent {
Box(Modifier.semantics { stateDescription = state }.testTag(tag)) { BasicText("Text") }
}
- val virtualViewId = rule.onNodeWithTag(tag).assertValueEquals("state one").semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).assertValueEquals("state one").semanticsId()
// Act.
rule.runOnIdle { state = "state two" }
@@ -2731,7 +2775,7 @@
BasicText("ToggleableText")
}
}
- val virtualViewId = rule.onNodeWithTag(tag).assertIsDisplayed().assertIsOn().semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).assertIsDisplayed().assertIsOn().semanticsId()
// Act.
rule.onNodeWithTag(tag).performClick()
@@ -2785,7 +2829,7 @@
}
}
}
- val toggleableVirtualViewId = rule.onNodeWithTag(tag).assertIsDisplayed().semanticsId
+ val toggleableVirtualViewId = rule.onNodeWithTag(tag).assertIsDisplayed().semanticsId()
// Act.
val actionPerformed =
@@ -2838,7 +2882,7 @@
}
}
val virtualViewId =
- rule.onNodeWithTag(tag).assertIsDisplayed().assertIsNotSelected().semanticsId
+ rule.onNodeWithTag(tag).assertIsDisplayed().assertIsNotSelected().semanticsId()
// Act.
rule.onNodeWithTag(tag).performClick()
@@ -2892,7 +2936,7 @@
}
}
val virtualViewId =
- rule.onNodeWithTag(tag).assertIsDisplayed().assertIsNotSelected().semanticsId
+ rule.onNodeWithTag(tag).assertIsDisplayed().assertIsNotSelected().semanticsId()
// Act.
rule.onNodeWithTag(tag).performClick()
@@ -2921,7 +2965,7 @@
// Arrange.
var current by mutableStateOf(0.5f)
setContent { Box(Modifier.progressSemantics(current).testTag(tag)) { BasicText("Text") } }
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
// Act.
rule.runOnIdle { current = 0.9f }
@@ -2993,7 +3037,7 @@
rule
.onNodeWithTag(tag)
.assert(expectValue(EditableText, AnnotatedString("HELLO")))
- .semanticsId
+ .semanticsId()
rule.runOnIdle {
verify(container, atLeastOnce())
.requestSendAccessibilityEvent(eq(androidComposeView), argument.capture())
@@ -3042,7 +3086,7 @@
}
}
}
- val columnId = rule.onNodeWithTag(columnTag).semanticsId
+ val columnId = rule.onNodeWithTag(columnTag).semanticsId()
rule.runOnIdle {
verify(container, atLeastOnce())
@@ -3097,7 +3141,7 @@
}
)
}
- val virtualViewId = rule.onNodeWithTag(tag).assertIsDisplayed().semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).assertIsDisplayed().semanticsId()
// TODO(b/272068594): Extra TYPE_WINDOW_CONTENT_CHANGED sent 100ms after setup.
rule.mainClock.advanceTimeBy(accessibilityEventLoopIntervalMs)
@@ -3150,7 +3194,7 @@
// Arrange.
val text = "h"
setContent { BasicText(text, Modifier.testTag(tag)) }
- val virtualViewId = rule.onNodeWithTag(tag).assertIsDisplayed().semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).assertIsDisplayed().semanticsId()
// TODO(b/272068594): Extra TYPE_WINDOW_CONTENT_CHANGED sent 100ms after setup.
rule.mainClock.advanceTimeBy(accessibilityEventLoopIntervalMs)
@@ -3207,7 +3251,7 @@
Box { BasicText("TextNode") }
}
}
- val toggleableId = rule.onNodeWithTag(tag).semanticsId
+ val toggleableId = rule.onNodeWithTag(tag).semanticsId()
val textNode =
rule
.onNodeWithText("TextNode", useUnmergedTree = true)
@@ -3229,9 +3273,7 @@
rule.runOnUiThread {
// Directly call onLayoutChange because this guarantees short time.
- for (i in 1..10) {
- delegate.onLayoutChange(textNode.layoutNode)
- }
+ repeat(10) { delegate.onLayoutChange(textNode.layoutNode) }
}
rule.mainClock.advanceTimeBy(accessibilityEventLoopIntervalMs)
@@ -3263,7 +3305,7 @@
}
}
- val toggleableId = rule.onNodeWithTag(tag).semanticsId
+ val toggleableId = rule.onNodeWithTag(tag).semanticsId()
val textNode =
rule
.onNodeWithText("TextNode", useUnmergedTree = true)
@@ -3286,7 +3328,7 @@
rule.runOnUiThread {
// Directly call onLayoutChange because this guarantees short time.
- for (i in 1..10) {
+ repeat(10) {
// layout change for the parent box node
delegate.onLayoutChange(textNode.layoutNode.parent!!)
}
@@ -3320,7 +3362,7 @@
BasicText("ToggleableText")
}
}
- val toggleableId = rule.onNodeWithTag(tag).semanticsId
+ val toggleableId = rule.onNodeWithTag(tag).semanticsId()
val toggleableBounds =
with(rule.density) { rule.onNodeWithTag(tag).getBoundsInRoot().toRect() }
@@ -3353,8 +3395,8 @@
}
}
}
- val childOneId = rule.onNodeWithTag(childOneTag).semanticsId
- val childTwoId = rule.onNodeWithTag(childTwoTag).semanticsId
+ val childOneId = rule.onNodeWithTag(childOneTag).semanticsId()
+ val childTwoId = rule.onNodeWithTag(childTwoTag).semanticsId()
val overlappedChildNodeBounds =
with(rule.density) { rule.onNodeWithTag(childTwoTag).getBoundsInRoot().toRect() }
@@ -3399,7 +3441,7 @@
assertThat(scrollState.value).isGreaterThan(199)
- val vitrualViewId = rule.onNodeWithTag(tag).semanticsId
+ val vitrualViewId = rule.onNodeWithTag(tag).semanticsId()
val childNodeBounds =
with(rule.density) { rule.onNodeWithTag(tag).getBoundsInRoot().toRect() }
val hitTestedId =
@@ -3500,7 +3542,7 @@
Box(Modifier.size(100.dp).clickable {}.testTag(innertag)) { BasicText("") }
}
}
- val outerNodeId = rule.onNodeWithTag(outertag).semanticsId
+ val outerNodeId = rule.onNodeWithTag(outertag).semanticsId()
val bounds =
with(rule.density) { rule.onNodeWithTag(innertag, true).getBoundsInRoot().toRect() }
@@ -3658,7 +3700,7 @@
)
}
- val virtualViewId = rule.onNodeWithTag(textTag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(textTag).semanticsId()
val bounds = with(rule.density) { rule.onNodeWithTag(textTag).getBoundsInRoot().toRect() }
rule.runOnUiThread {
val hoverEnter =
@@ -3796,9 +3838,9 @@
}
}
}
- val parentNodeId = rule.onNodeWithTag(parentTag).semanticsId
- val overlappedChildOneNodeId = rule.onNodeWithTag(childOneTag).semanticsId
- val overlappedChildTwoNodeId = rule.onNodeWithTag(childTwoTag).semanticsId
+ val parentNodeId = rule.onNodeWithTag(parentTag).semanticsId()
+ val overlappedChildOneNodeId = rule.onNodeWithTag(childOneTag).semanticsId()
+ val overlappedChildTwoNodeId = rule.onNodeWithTag(childTwoTag).semanticsId()
// Assert.
rule.runOnIdle {
@@ -3824,8 +3866,8 @@
}
}
}
- val parentNodeId = rule.onNodeWithTag(parentTag).semanticsId
- val childTwoId = rule.onNodeWithText("Child Two").semanticsId
+ val parentNodeId = rule.onNodeWithTag(parentTag).semanticsId()
+ val childTwoId = rule.onNodeWithText("Child Two").semanticsId()
val childTwoBounds = Rect()
// Act.
@@ -3855,8 +3897,8 @@
}
}
}
- val parentNodeId = rule.onNodeWithTag(parentTag).semanticsId
- val overlappedChildTwoNodeId = rule.onNodeWithTag(childTwoTag).semanticsId
+ val parentNodeId = rule.onNodeWithTag(parentTag).semanticsId()
+ val overlappedChildTwoNodeId = rule.onNodeWithTag(childTwoTag).semanticsId()
rule.runOnIdle {
assertThat(createAccessibilityNodeInfo(parentNodeId).childCount).isEqualTo(2)
@@ -3886,7 +3928,7 @@
.onNodeWithTag(tag)
.assert(expectValue(SemanticsProperties.PaneTitle, "pane title"))
.assertIsDisplayed()
- .semanticsId
+ .semanticsId()
rule.runOnIdle {
verify(container, times(1))
.requestSendAccessibilityEvent(
@@ -3929,7 +3971,7 @@
rule
.onNodeWithTag(tag)
.assert(expectValue(SemanticsProperties.PaneTitle, "new pane title"))
- .semanticsId
+ .semanticsId()
rule.runOnIdle {
verify(container, times(1))
.requestSendAccessibilityEvent(
@@ -4084,7 +4126,7 @@
}
}
}
- val virtualViewId = rule.onNodeWithTag("node").semanticsId
+ val virtualViewId = rule.onNodeWithTag("node").semanticsId()
var info = AccessibilityNodeInfoCompat.obtain()
rule.runOnUiThread { info = createAccessibilityNodeInfo(virtualViewId) }
@@ -4122,7 +4164,7 @@
}
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
var info = AccessibilityNodeInfoCompat.obtain()
rule.runOnUiThread { info = createAccessibilityNodeInfo(virtualViewId) }
val rect = Rect()
@@ -4184,8 +4226,8 @@
}
}
}
- val parentNodeId = rule.onNodeWithTag(parentTag).semanticsId
- val overlappedChildTwoNodeId = rule.onNodeWithTag(childTwoTag).semanticsId
+ val parentNodeId = rule.onNodeWithTag(parentTag).semanticsId()
+ val overlappedChildTwoNodeId = rule.onNodeWithTag(childTwoTag).semanticsId()
rule.runOnIdle {
assertThat(createAccessibilityNodeInfo(parentNodeId).childCount).isEqualTo(2)
@@ -4215,7 +4257,7 @@
}
}
}
- val virtualViewId = rule.onNodeWithText("text").semanticsId
+ val virtualViewId = rule.onNodeWithText("text").semanticsId()
var info = AccessibilityNodeInfoCompat.obtain()
rule.runOnUiThread { info = createAccessibilityNodeInfo(virtualViewId) }
@@ -4283,13 +4325,13 @@
}
}
}
- val box1Id = rule.onNodeWithTag(tag1).semanticsId
- val box2Id = rule.onNodeWithTag(tag2).semanticsId
- val box3Id = rule.onNodeWithTag(tag3).semanticsId
- val box4Id = rule.onNodeWithTag(tag4).semanticsId
- val box5Id = rule.onNodeWithTag(tag5).semanticsId
- val box6Id = rule.onNodeWithTag(tag6, true).semanticsId
- val box7Id = rule.onNodeWithTag(tag7, true).semanticsId
+ val box1Id = rule.onNodeWithTag(tag1).semanticsId()
+ val box2Id = rule.onNodeWithTag(tag2).semanticsId()
+ val box3Id = rule.onNodeWithTag(tag3).semanticsId()
+ val box4Id = rule.onNodeWithTag(tag4).semanticsId()
+ val box5Id = rule.onNodeWithTag(tag5).semanticsId()
+ val box6Id = rule.onNodeWithTag(tag6, true).semanticsId()
+ val box7Id = rule.onNodeWithTag(tag7, true).semanticsId()
// Act.
rule.waitForIdle()
@@ -4324,7 +4366,7 @@
}
}
}
- val virtualId = rule.onNodeWithTag(tag).semanticsId
+ val virtualId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { createAccessibilityNodeInfo(virtualId) }
@@ -4344,7 +4386,7 @@
}
}
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { createAccessibilityNodeInfo(virtualViewId) }
@@ -4361,7 +4403,7 @@
Box(Modifier.size(100.toDp()).testTag(tag).semantics { contentDescription = "Box" })
}
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { createAccessibilityNodeInfo(virtualViewId) }
@@ -4382,7 +4424,7 @@
)
}
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { createAccessibilityNodeInfo(virtualViewId) }
@@ -4408,7 +4450,7 @@
}
}
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { createAccessibilityNodeInfo(virtualViewId) }
@@ -4428,7 +4470,7 @@
}
}
}
- val virtualViewId = rule.onNodeWithTag("Row").semanticsId
+ val virtualViewId = rule.onNodeWithTag("Row").semanticsId()
// Act.
val info = rule.runOnIdle { createAccessibilityNodeInfo(virtualViewId) }
@@ -4455,7 +4497,7 @@
}
}
}
- val virtualViewId = rule.onNodeWithTag("tag").semanticsId
+ val virtualViewId = rule.onNodeWithTag("tag").semanticsId()
// Act.
val accessibilityNodeInfo =
@@ -4494,7 +4536,7 @@
}
}
- val virtualViewId = rule.onNodeWithTag("tag").semanticsId
+ val virtualViewId = rule.onNodeWithTag("tag").semanticsId()
// Act.
val accessibilityNodeInfo =
@@ -4529,7 +4571,7 @@
}
}
- val virtualViewId = rule.onNodeWithTag("tag").semanticsId
+ val virtualViewId = rule.onNodeWithTag("tag").semanticsId()
// Act.
val accessibilityNodeInfo = rule.runOnIdle { createAccessibilityNodeInfo(virtualViewId) }
@@ -4563,7 +4605,7 @@
}
}
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val accessibilityNodeInfo =
@@ -4597,7 +4639,7 @@
}
}
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val accessibilityNodeInfo = rule.runOnIdle { createAccessibilityNodeInfo(virtualViewId) }
@@ -4633,7 +4675,7 @@
}
}
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val accessibilityNodeInfo = rule.runOnIdle { createAccessibilityNodeInfo(virtualViewId) }
@@ -4879,7 +4921,7 @@
setContent {
Column(Modifier.semantics(true) {}) { BasicText("test", Modifier.testTag(tag)) }
}
- val virtualViewId = rule.onNodeWithTag(tag, useUnmergedTree = true).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag, useUnmergedTree = true).semanticsId()
// Act.
val childInfo = rule.runOnIdle { createAccessibilityNodeInfo(virtualViewId) }
@@ -4895,7 +4937,7 @@
setContent {
Column(Modifier.semantics(false) {}) { BasicText("test", Modifier.testTag(tag)) }
}
- val virtualViewId = rule.onNodeWithTag(tag, useUnmergedTree = true).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag, useUnmergedTree = true).semanticsId()
// Act.
val childInfo = rule.runOnIdle { createAccessibilityNodeInfo(virtualViewId) }
@@ -4911,7 +4953,7 @@
setContent {
Column(Modifier.semantics(false) {}) { Box(Modifier.testTag(tag).size(100.dp)) }
}
- val virtualViewId = rule.onNodeWithTag(tag, useUnmergedTree = true).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag, useUnmergedTree = true).semanticsId()
// Act.
val childInfo = rule.runOnIdle { createAccessibilityNodeInfo(virtualViewId) }
@@ -4928,7 +4970,7 @@
Image(ImageBitmap(100, 100), "Image", Modifier.testTag(tag))
}
}
- val virtualViewId = rule.onNodeWithTag(tag, true).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag, true).semanticsId()
// Act.
val imageInfo = rule.runOnIdle { createAccessibilityNodeInfo(virtualViewId) }
@@ -4945,7 +4987,7 @@
Image(ImageBitmap(100, 100), "Image", Modifier.testTag(tag))
}
}
- val virtualViewId = rule.onNodeWithTag(tag, true).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag, true).semanticsId()
// Act.
val imageInfo = rule.runOnIdle { createAccessibilityNodeInfo(virtualViewId) }
@@ -4966,7 +5008,7 @@
)
}
}
- val virtualViewId = rule.onNodeWithTag(tag, true).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag, true).semanticsId()
// Act.
val imageInfo = rule.runOnIdle { createAccessibilityNodeInfo(virtualViewId) }
@@ -4992,8 +5034,8 @@
}
}
}
- val columnId = rule.onNodeWithTag(tagColumn).semanticsId
- val rowId = rule.onNodeWithTag(tagRow).semanticsId
+ val columnId = rule.onNodeWithTag(tagColumn).semanticsId()
+ val rowId = rule.onNodeWithTag(tagRow).semanticsId()
// Act.
rule.waitForIdle()
@@ -5087,12 +5129,12 @@
)
}
}
- val parentId = rule.onNodeWithTag("parent").semanticsId
- val child1Id = rule.onNodeWithTag("child1").semanticsId
- val child2Id = rule.onNodeWithTag("child2").semanticsId
- val child3Id = rule.onNodeWithTag("child3").semanticsId
- val child4Id = rule.onNodeWithTag("child4").semanticsId
- val child5Id = rule.onNodeWithTag("child5").semanticsId
+ val parentId = rule.onNodeWithTag("parent").semanticsId()
+ val child1Id = rule.onNodeWithTag("child1").semanticsId()
+ val child2Id = rule.onNodeWithTag("child2").semanticsId()
+ val child3Id = rule.onNodeWithTag("child3").semanticsId()
+ val child4Id = rule.onNodeWithTag("child4").semanticsId()
+ val child5Id = rule.onNodeWithTag("child5").semanticsId()
// Act.
rule.waitForIdle()
@@ -5146,10 +5188,10 @@
)
}
}
- val parentId = rule.onNodeWithTag("parent").semanticsId
- val child1Id = rule.onNodeWithTag("child1").semanticsId
- val child2Id = rule.onNodeWithTag("child2").semanticsId
- val child3Id = rule.onNodeWithTag("child3").semanticsId
+ val parentId = rule.onNodeWithTag("parent").semanticsId()
+ val child1Id = rule.onNodeWithTag("child1").semanticsId()
+ val child2Id = rule.onNodeWithTag("child2").semanticsId()
+ val child3Id = rule.onNodeWithTag("child3").semanticsId()
// Act.
rule.waitForIdle()
@@ -5176,8 +5218,8 @@
BasicText("test", Modifier.testTag("child"))
}
}
- val boxId = rule.onNodeWithTag("box", useUnmergedTree = true).semanticsId
- val textId = rule.onNodeWithTag("child", useUnmergedTree = true).semanticsId
+ val boxId = rule.onNodeWithTag("box", useUnmergedTree = true).semanticsId()
+ val textId = rule.onNodeWithTag("child", useUnmergedTree = true).semanticsId()
// Act.
rule.waitForIdle()
@@ -5200,8 +5242,8 @@
BasicText("test", Modifier.testTag("child"))
}
}
- val boxId = rule.onNodeWithTag("box", useUnmergedTree = true).semanticsId
- val textId = rule.onNodeWithTag("child", useUnmergedTree = true).semanticsId
+ val boxId = rule.onNodeWithTag("box", useUnmergedTree = true).semanticsId()
+ val textId = rule.onNodeWithTag("child", useUnmergedTree = true).semanticsId()
// Act.
rule.waitForIdle()
@@ -5451,10 +5493,6 @@
Bottom
}
-// TODO(b/272068594): Add api to fetch the semantics id from SemanticsNodeInteraction directly.
-private val SemanticsNodeInteraction.semanticsId: Int
- get() = fetchSemanticsNode().id
-
// TODO(b/304359126): Move this to AccessibilityEventCompat and use it wherever we use obtain().
private fun AccessibilityEvent(): android.view.accessibility.AccessibilityEvent {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/AndroidComposeViewAccessibilityDelegateCompatTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/AndroidComposeViewAccessibilityDelegateCompatTest.kt
index fb3cf39..193b509 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/AndroidComposeViewAccessibilityDelegateCompatTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/AndroidComposeViewAccessibilityDelegateCompatTest.kt
@@ -73,6 +73,7 @@
import androidx.compose.ui.semantics.progressBarRangeInfo
import androidx.compose.ui.semantics.role
import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.semantics.semanticsId
import androidx.compose.ui.semantics.setProgress
import androidx.compose.ui.semantics.setSelection
import androidx.compose.ui.semantics.setText
@@ -81,7 +82,6 @@
import androidx.compose.ui.semantics.testTagsAsResourceId
import androidx.compose.ui.semantics.text
import androidx.compose.ui.semantics.textSelectionRange
-import androidx.compose.ui.test.SemanticsNodeInteraction
import androidx.compose.ui.test.TestActivity
import androidx.compose.ui.test.junit4.ComposeContentTestRule
import androidx.compose.ui.test.junit4.createAndroidComposeRule
@@ -143,7 +143,7 @@
}
)
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { androidComposeView.createAccessibilityNodeInfo(virtualViewId) }
@@ -174,7 +174,7 @@
rule.setContentWithAccessibilityEnabled {
Box(Modifier.size(10.dp).semantics(mergeDescendants = true) { testTag = tag })
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { androidComposeView.createAccessibilityNodeInfo(virtualViewId) }
@@ -189,7 +189,7 @@
rule.setContentWithAccessibilityEnabled {
Box(Modifier.size(10.dp).semantics(mergeDescendants = false) { testTag = tag })
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { androidComposeView.createAccessibilityNodeInfo(virtualViewId) }
@@ -209,7 +209,7 @@
}
)
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { androidComposeView.createAccessibilityNodeInfo(virtualViewId) }
@@ -245,7 +245,7 @@
}
)
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { androidComposeView.createAccessibilityNodeInfo(virtualViewId) }
@@ -293,7 +293,7 @@
}
)
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { androidComposeView.createAccessibilityNodeInfo(virtualViewId) }
@@ -313,7 +313,7 @@
}
)
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { androidComposeView.createAccessibilityNodeInfo(virtualViewId) }
@@ -333,7 +333,7 @@
}
)
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { androidComposeView.createAccessibilityNodeInfo(virtualViewId) }
@@ -353,7 +353,7 @@
}
)
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { androidComposeView.createAccessibilityNodeInfo(virtualViewId) }
@@ -373,7 +373,7 @@
}
)
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { androidComposeView.createAccessibilityNodeInfo(virtualViewId) }
@@ -393,7 +393,7 @@
}
)
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { androidComposeView.createAccessibilityNodeInfo(virtualViewId) }
@@ -413,7 +413,7 @@
}
)
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { androidComposeView.createAccessibilityNodeInfo(virtualViewId) }
@@ -429,7 +429,7 @@
rule.setContentWithAccessibilityEnabled {
Box(Modifier.size(10.dp).semantics(mergeDescendants = false) { testTag = tag })
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { androidComposeView.createAccessibilityNodeInfo(virtualViewId) }
@@ -445,7 +445,7 @@
rule.setContentWithAccessibilityEnabled {
Box(Modifier.size(10.dp).semantics(mergeDescendants = true) { testTag = tag })
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { androidComposeView.createAccessibilityNodeInfo(virtualViewId) }
@@ -468,7 +468,7 @@
}
)
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { androidComposeView.createAccessibilityNodeInfo(virtualViewId) }
@@ -490,7 +490,7 @@
}
)
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { androidComposeView.createAccessibilityNodeInfo(virtualViewId) }
@@ -511,7 +511,7 @@
}
)
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { androidComposeView.createAccessibilityNodeInfo(virtualViewId) }
@@ -535,7 +535,7 @@
}
)
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { androidComposeView.createAccessibilityNodeInfo(virtualViewId) }
@@ -557,7 +557,7 @@
Box(Modifier.semantics { text = AnnotatedString("foo") })
}
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { androidComposeView.createAccessibilityNodeInfo(virtualViewId) }
@@ -578,7 +578,7 @@
}
)
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { androidComposeView.createAccessibilityNodeInfo(virtualViewId) }
@@ -600,7 +600,7 @@
}
)
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { androidComposeView.createAccessibilityNodeInfo(virtualViewId) }
@@ -620,7 +620,7 @@
}
)
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { androidComposeView.createAccessibilityNodeInfo(virtualViewId) }
@@ -650,8 +650,8 @@
)
}
}
- val virtualViewId1 = rule.onNodeWithTag(testTag1).semanticsId
- val virtualViewId2 = rule.onNodeWithTag(testTag2).semanticsId
+ val virtualViewId1 = rule.onNodeWithTag(testTag1).semanticsId()
+ val virtualViewId2 = rule.onNodeWithTag(testTag2).semanticsId()
// Act.
lateinit var info1: AccessibilityNodeInfoCompat
@@ -680,7 +680,7 @@
}
)
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
val info1 = rule.runOnIdle { androidComposeView.createAccessibilityNodeInfo(virtualViewId) }
dispatchedAccessibilityEvents.clear()
@@ -717,7 +717,7 @@
}
)
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { androidComposeView.createAccessibilityNodeInfo(virtualViewId) }
@@ -763,7 +763,7 @@
}
)
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { androidComposeView.createAccessibilityNodeInfo(virtualViewId) }
@@ -821,7 +821,7 @@
}
)
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { androidComposeView.createAccessibilityNodeInfo(virtualViewId) }
@@ -846,7 +846,7 @@
}
)
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { androidComposeView.createAccessibilityNodeInfo(virtualViewId) }
@@ -879,7 +879,7 @@
}
)
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { androidComposeView.createAccessibilityNodeInfo(virtualViewId) }
@@ -907,7 +907,7 @@
}
)
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { androidComposeView.createAccessibilityNodeInfo(virtualViewId) }
@@ -930,7 +930,7 @@
}
)
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { androidComposeView.createAccessibilityNodeInfo(virtualViewId) }
@@ -958,7 +958,7 @@
}
)
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { androidComposeView.createAccessibilityNodeInfo(virtualViewId) }
@@ -987,7 +987,7 @@
}
)
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { androidComposeView.createAccessibilityNodeInfo(virtualViewId) }
@@ -1013,7 +1013,7 @@
}
)
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { androidComposeView.createAccessibilityNodeInfo(virtualViewId) }
@@ -1273,7 +1273,7 @@
}
)
}
- val virtualId = rule.onNodeWithTag(tag).semanticsId
+ val virtualId = rule.onNodeWithTag(tag).semanticsId()
// Act.
rule.runOnIdle { textChanged = true }
@@ -1426,10 +1426,6 @@
ViewCompat.getAccessibilityDelegate(this)
as AndroidComposeViewAccessibilityDelegateCompat
- // TODO(b/272068594): Add api to fetch the semantics id from SemanticsNodeInteraction directly.
- private val SemanticsNodeInteraction.semanticsId: Int
- get() = fetchSemanticsNode().id
-
// TODO(b/304359126): Move this to AccessibilityEventCompat and use it wherever we use obtain().
private fun AccessibilityEvent(): AccessibilityEvent =
if (SDK_INT >= R) {
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/accessibility/CollectionInfoTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/accessibility/CollectionInfoTest.kt
index a4a05d7..ab7504f 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/accessibility/CollectionInfoTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/accessibility/CollectionInfoTest.kt
@@ -38,7 +38,7 @@
import androidx.compose.ui.semantics.collectionInfo
import androidx.compose.ui.semantics.collectionItemInfo
import androidx.compose.ui.semantics.semantics
-import androidx.compose.ui.test.SemanticsNodeInteraction
+import androidx.compose.ui.semantics.semanticsId
import androidx.compose.ui.test.TestActivity
import androidx.compose.ui.test.junit4.ComposeContentTestRule
import androidx.compose.ui.test.junit4.createAndroidComposeRule
@@ -88,7 +88,7 @@
Box(Modifier.size(50.dp).selectable(selected = false, onClick = {}))
}
}
- val virtualId = rule.onNodeWithTag(tag).semanticsId
+ val virtualId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { composeView.createAccessibilityNodeInfo(virtualId) }
@@ -109,7 +109,7 @@
rule.setContentWithAccessibilityEnabled {
LazyColumn(Modifier.testTag(tag)) { items(2) { BasicText("Text") } }
}
- val virtualId = rule.onNodeWithTag(tag).semanticsId
+ val virtualId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { composeView.createAccessibilityNodeInfo(virtualId) }
@@ -132,7 +132,7 @@
items(2) { BasicText("Text") }
}
}
- val virtualId = rule.onNodeWithTag(tag).semanticsId
+ val virtualId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { composeView.createAccessibilityNodeInfo(virtualId) }
@@ -153,7 +153,7 @@
rule.setContentWithAccessibilityEnabled {
LazyColumn(Modifier.testTag(tag).selectableGroup()) { items(2) { BasicText("Text") } }
}
- val virtualId = rule.onNodeWithTag(tag).semanticsId
+ val virtualId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { composeView.createAccessibilityNodeInfo(virtualId) }
@@ -180,7 +180,7 @@
items(2) { BasicText("Text") }
}
}
- val virtualId = rule.onNodeWithTag(tag).semanticsId
+ val virtualId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { composeView.createAccessibilityNodeInfo(virtualId) }
@@ -205,7 +205,7 @@
Box(Modifier.size(50.dp).selectable(selected = false, onClick = {}))
}
}
- val virtualId = rule.onNodeWithTag(tag).semanticsId
+ val virtualId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { composeView.createAccessibilityNodeInfo(virtualId) }
@@ -230,7 +230,7 @@
itemsIndexed(listOf("Text", "Text")) { index, item -> BasicText(item + index) }
}
}
- val virtualId = rule.onNodeWithText("Text0").semanticsId
+ val virtualId = rule.onNodeWithText("Text0").semanticsId()
// Act.
val info = rule.runOnIdle { composeView.createAccessibilityNodeInfo(virtualId) }
@@ -254,7 +254,7 @@
}
}
}
- val virtualId = rule.onNodeWithText("Text0").semanticsId
+ val virtualId = rule.onNodeWithText("Text0").semanticsId()
// Act.
val info = rule.runOnIdle { composeView.createAccessibilityNodeInfo(virtualId) }
@@ -286,7 +286,7 @@
}
}
}
- val virtualId = rule.onNodeWithText("Text0").semanticsId
+ val virtualId = rule.onNodeWithText("Text0").semanticsId()
// Act.
val info = rule.runOnIdle { composeView.createAccessibilityNodeInfo(virtualId) }
@@ -318,7 +318,7 @@
}
}
}
- val virtualId = rule.onNodeWithText("Text0").semanticsId
+ val virtualId = rule.onNodeWithText("Text0").semanticsId()
// Act.
val info = rule.runOnIdle { composeView.createAccessibilityNodeInfo(virtualId) }
@@ -347,7 +347,7 @@
// items
}
}
- val virtualId = rule.onNodeWithTag(tag).semanticsId
+ val virtualId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { composeView.createAccessibilityNodeInfo(virtualId) }
@@ -370,7 +370,7 @@
Box(Modifier.size(10.dp).selectable(selected = false, onClick = {}))
}
}
- val virtualId = rule.onNodeWithTag(tag).semanticsId
+ val virtualId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { composeView.createAccessibilityNodeInfo(virtualId) }
@@ -390,7 +390,7 @@
// items
}
}
- val virtualId = rule.onNodeWithTag(tag).semanticsId
+ val virtualId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { composeView.createAccessibilityNodeInfo(virtualId) }
@@ -407,7 +407,7 @@
// items
}
}
- val virtualId = rule.onNodeWithTag(tag).semanticsId
+ val virtualId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { composeView.createAccessibilityNodeInfo(virtualId) }
@@ -441,9 +441,9 @@
)
}
}
- val virtualId0 = rule.onNodeWithTag("item0").semanticsId
- val virtualId1 = rule.onNodeWithTag("item1").semanticsId
- val virtualId2 = rule.onNodeWithTag("item2").semanticsId
+ val virtualId0 = rule.onNodeWithTag("item0").semanticsId()
+ val virtualId1 = rule.onNodeWithTag("item1").semanticsId()
+ val virtualId2 = rule.onNodeWithTag("item2").semanticsId()
// Act.
rule.waitForIdle()
@@ -466,7 +466,7 @@
rule.setContentWithAccessibilityEnabled {
HorizontalPager(rememberPagerState { pageCount }, Modifier.size(10.dp).testTag(tag)) {}
}
- val virtualId = rule.onNodeWithTag(tag).semanticsId
+ val virtualId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { composeView.createAccessibilityNodeInfo(virtualId) }
@@ -482,7 +482,7 @@
rule.setContentWithAccessibilityEnabled {
VerticalPager(rememberPagerState { pageCount }, Modifier.size(10.dp).testTag(tag)) {}
}
- val virtualId = rule.onNodeWithTag(tag).semanticsId
+ val virtualId = rule.onNodeWithTag(tag).semanticsId()
// Act.
val info = rule.runOnIdle { composeView.createAccessibilityNodeInfo(virtualId) }
@@ -491,10 +491,6 @@
rule.runOnIdle { assertThat(info.collectionInfo.rowCount).isEqualTo(pageCount) }
}
- // TODO(b/272068594): Add api to fetch the semantics id from SemanticsNodeInteraction directly.
- private val SemanticsNodeInteraction.semanticsId: Int
- get() = fetchSemanticsNode().id
-
private fun ComposeContentTestRule.setContentWithAccessibilityEnabled(
content: @Composable () -> Unit
) {
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/accessibility/ScrollingTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/accessibility/ScrollingTest.kt
index 8b1f629..7a5eb34 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/accessibility/ScrollingTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/accessibility/ScrollingTest.kt
@@ -47,9 +47,9 @@
import androidx.compose.ui.semantics.horizontalScrollAxisRange
import androidx.compose.ui.semantics.scrollBy
import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.semantics.semanticsId
import androidx.compose.ui.semantics.testTag
import androidx.compose.ui.semantics.verticalScrollAxisRange
-import androidx.compose.ui.test.SemanticsNodeInteraction
import androidx.compose.ui.test.TestActivity
import androidx.compose.ui.test.junit4.ComposeContentTestRule
import androidx.compose.ui.test.junit4.createAndroidComposeRule
@@ -113,7 +113,7 @@
}
}
rule.mainClock.advanceTimeBy(accessibilityEventLoopIntervalMs)
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
rule.runOnIdle { dispatchedAccessibilityEvents.clear() }
// Act.
@@ -461,7 +461,7 @@
)
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
rule.runOnIdle {
androidComposeView.accessibilityNodeProvider.performAction(
virtualViewId,
@@ -499,7 +499,7 @@
)
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
rule.runOnIdle {
androidComposeView.accessibilityNodeProvider.performAction(
virtualViewId,
@@ -608,10 +608,6 @@
ViewCompat.getAccessibilityDelegate(this)
as AndroidComposeViewAccessibilityDelegateCompat
- // TODO(b/272068594): Add api to fetch the semantics id from SemanticsNodeInteraction directly.
- private val SemanticsNodeInteraction.semanticsId: Int
- get() = fetchSemanticsNode().id
-
// TODO(b/304359126): Move this to AccessibilityEventCompat and use it wherever we use obtain().
private fun AccessibilityEvent(): AccessibilityEvent =
if (SDK_INT >= R) {
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/autofill/AndroidAutoFillTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/autofill/AndroidAutoFillTest.kt
index b3bc836..a89f445 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/autofill/AndroidAutoFillTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/autofill/AndroidAutoFillTest.kt
@@ -44,16 +44,16 @@
class AndroidAutoFillTest {
@get:Rule val rule = createComposeRule()
- private var autofill: Autofill? = null
- private lateinit var autofillTree: AutofillTree
+ private var autofill: @Suppress("Deprecation") Autofill? = null
+ private lateinit var autofillTree: @Suppress("Deprecation") AutofillTree
private lateinit var ownerView: View
@Before
fun setup() {
rule.setContent {
ownerView = LocalView.current
- autofill = LocalAutofill.current
- autofillTree = LocalAutofillTree.current
+ autofill = @Suppress("Deprecation") LocalAutofill.current
+ autofillTree = @Suppress("Deprecation") LocalAutofillTree.current
}
}
@@ -81,6 +81,7 @@
// Arrange.
val viewStructure: ViewStructure = FakeViewStructure()
val autofillNode =
+ @Suppress("Deprecation")
AutofillNode(
onFill = {},
autofillTypes = listOf(AutofillType.PersonFullName),
@@ -123,6 +124,7 @@
val expectedValue = "PersonName"
var autoFilledValue = ""
val autofillNode =
+ @Suppress("Deprecation")
AutofillNode(
onFill = { autoFilledValue = it },
autofillTypes = listOf(AutofillType.PersonFullName),
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/autofill/MixedAutofillTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/autofill/MixedAutofillTest.kt
index a955f9d..49e1bfd 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/autofill/MixedAutofillTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/autofill/MixedAutofillTest.kt
@@ -41,6 +41,7 @@
import androidx.compose.ui.semantics.contentType
import androidx.compose.ui.semantics.onAutofillText
import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.semantics.semanticsId
import androidx.compose.ui.semantics.testTag
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
@@ -65,9 +66,8 @@
@get:Rule val rule = createComposeRule()
private val height = 200.dp
private val width = 200.dp
-
- @OptIn(ExperimentalComposeUiApi::class)
- private val previousFlagValue = ComposeUiFlags.isSemanticAutofillEnabled
+ private val previousFlagValue =
+ @OptIn(ExperimentalComposeUiApi::class) ComposeUiFlags.isSemanticAutofillEnabled
@Before
fun enableAutofill() {
@@ -124,13 +124,14 @@
fun populateViewStructure_new_old_sameLayoutNode() {
// Arrange.
lateinit var view: View
- lateinit var autofillTree: AutofillTree
+ lateinit var autofillTree: @Suppress("DEPRECATION") AutofillTree
val viewStructure: ViewStructure = FakeViewStructure()
- lateinit var autofillNode: AutofillNode
+ lateinit var autofillNode: @Suppress("DEPRECATION") AutofillNode
rule.setContent {
view = LocalView.current
- autofillTree = LocalAutofillTree.current
+ autofillTree = @Suppress("DEPRECATION") LocalAutofillTree.current
autofillNode = remember {
+ @Suppress("DEPRECATION")
AutofillNode(
onFill = {},
autofillTypes = listOf(AutofillType.Password),
@@ -203,13 +204,14 @@
fun populateViewStructure_new_old_differentLayoutNodes() {
// Arrange.
lateinit var view: View
- lateinit var autofillTree: AutofillTree
+ lateinit var autofillTree: @Suppress("DEPRECATION") AutofillTree
val viewStructure: ViewStructure = FakeViewStructure()
- lateinit var autofillNode: AutofillNode
+ lateinit var autofillNode: @Suppress("DEPRECATION") AutofillNode
rule.setContent {
view = LocalView.current
- autofillTree = LocalAutofillTree.current
+ autofillTree = @Suppress("DEPRECATION") LocalAutofillTree.current
autofillNode = remember {
+ @Suppress("DEPRECATION")
AutofillNode(
onFill = {},
autofillTypes = listOf(AutofillType.Password),
@@ -288,14 +290,15 @@
fun autofill_new_old_sameLayoutNode() {
// Arrange.
lateinit var view: View
- lateinit var autofillTree: AutofillTree
- lateinit var autofillNode: AutofillNode
+ lateinit var autofillTree: @Suppress("DEPRECATION") AutofillTree
+ lateinit var autofillNode: @Suppress("DEPRECATION") AutofillNode
lateinit var autoFilledValueNewApi: String
lateinit var autoFilledValueOldApi: String
rule.setContent {
view = LocalView.current
- autofillTree = LocalAutofillTree.current
+ autofillTree = @Suppress("DEPRECATION") LocalAutofillTree.current
autofillNode = remember {
+ @Suppress("DEPRECATION")
AutofillNode(
onFill = { autoFilledValueOldApi = it },
autofillTypes = listOf(AutofillType.Password),
@@ -343,14 +346,15 @@
fun autofill_new_old_differentLayoutNodes() {
// Arrange.
lateinit var view: View
- lateinit var autofillTree: AutofillTree
- lateinit var autofillNode: AutofillNode
+ lateinit var autofillTree: @Suppress("DEPRECATION") AutofillTree
+ lateinit var autofillNode: @Suppress("DEPRECATION") AutofillNode
lateinit var autoFilledValueNewApi: String
lateinit var autoFilledValueOldApi: String
rule.setContent {
view = LocalView.current
- autofillTree = LocalAutofillTree.current
+ autofillTree = @Suppress("DEPRECATION") LocalAutofillTree.current
autofillNode = remember {
+ @Suppress("DEPRECATION")
AutofillNode(
onFill = { autoFilledValueOldApi = it },
autofillTypes = listOf(AutofillType.Password),
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/autofill/PerformAndroidAutofillManagerTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/autofill/PerformAndroidAutofillManagerTest.kt
index 4d142e5..e82f664 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/autofill/PerformAndroidAutofillManagerTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/autofill/PerformAndroidAutofillManagerTest.kt
@@ -54,12 +54,12 @@
import androidx.compose.ui.semantics.password
import androidx.compose.ui.semantics.requestFocus
import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.semantics.semanticsId
import androidx.compose.ui.semantics.setText
import androidx.compose.ui.semantics.toggleableState
import androidx.compose.ui.state.ToggleableState
import androidx.compose.ui.test.TestActivity
import androidx.compose.ui.test.assertTextEquals
-import androidx.compose.ui.test.isEnabled
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.requestFocus
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/autofill/TextFieldStateSemanticAutofillTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/autofill/TextFieldStateSemanticAutofillTest.kt
index c53af64..fa1cdfd 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/autofill/TextFieldStateSemanticAutofillTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/autofill/TextFieldStateSemanticAutofillTest.kt
@@ -33,6 +33,7 @@
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.semantics.contentType
import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.semantics.semanticsId
import androidx.compose.ui.test.TestActivity
import androidx.compose.ui.test.assertTextEquals
import androidx.compose.ui.test.junit4.createAndroidComposeRule
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/autofill/TextFieldsSemanticAutofillTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/autofill/TextFieldsSemanticAutofillTest.kt
index 1b5a6e5..bb5a3dc 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/autofill/TextFieldsSemanticAutofillTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/autofill/TextFieldsSemanticAutofillTest.kt
@@ -41,6 +41,7 @@
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.semantics.contentType
import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.semantics.semanticsId
import androidx.compose.ui.test.TestActivity
import androidx.compose.ui.test.assertTextEquals
import androidx.compose.ui.test.captureToImage
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/contentcapture/ContentCaptureTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/contentcapture/ContentCaptureTest.kt
index 147a430..0ccc6aa 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/contentcapture/ContentCaptureTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/contentcapture/ContentCaptureTest.kt
@@ -47,12 +47,12 @@
import androidx.compose.ui.semantics.clearTextSubstitution
import androidx.compose.ui.semantics.isShowingTextSubstitution
import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.semantics.semanticsId
import androidx.compose.ui.semantics.setTextSubstitution
import androidx.compose.ui.semantics.showTextSubstitution
import androidx.compose.ui.semantics.testTag
import androidx.compose.ui.semantics.text
import androidx.compose.ui.semantics.textSubstitution
-import androidx.compose.ui.test.SemanticsNodeInteraction
import androidx.compose.ui.test.TestActivity
import androidx.compose.ui.test.junit4.ComposeContentTestRule
import androidx.compose.ui.test.junit4.createAndroidComposeRule
@@ -585,7 +585,7 @@
)
}
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
val ids = LongArray(1).apply { this[0] = virtualViewId.toLong() }
val requestsCollector: Consumer<ViewTranslationRequest?> = mock()
@@ -639,7 +639,7 @@
)
}
}
- val virtualViewId = rule.onNodeWithTag(tag).semanticsId
+ val virtualViewId = rule.onNodeWithTag(tag).semanticsId()
// Act.
rule.runOnIdle {
@@ -785,10 +785,6 @@
}
}
- // TODO(b/272068594): Add api to fetch the semantics id from SemanticsNodeInteraction directly.
- private val SemanticsNodeInteraction.semanticsId: Int
- get() = fetchSemanticsNode().id
-
@Composable
private fun ContentCaptureTestLazyList(listState: LazyListState) {
val itemCount = 20
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/focus/FocusListenerTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/focus/FocusListenerTest.kt
index e1be2f7..12b5593 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/focus/FocusListenerTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/focus/FocusListenerTest.kt
@@ -30,6 +30,7 @@
import androidx.compose.ui.node.requireSemanticsInfo
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.semanticsId
import androidx.compose.ui.test.junit4.ComposeContentTestRule
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/LookaheadDelegatesTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/LookaheadDelegatesTest.kt
new file mode 100644
index 0000000..cb5de6d
--- /dev/null
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/LookaheadDelegatesTest.kt
@@ -0,0 +1,92 @@
+/*
+ * 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.compose.ui.layout
+
+import androidx.activity.ComponentActivity
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.requiredSize
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.movableContentOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.AndroidOwnerExtraAssertionsRule
+import androidx.compose.ui.test.junit4.createAndroidComposeRule
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class LookaheadDelegatesTest {
+ @get:Rule val rule = createAndroidComposeRule<ComponentActivity>()
+
+ @get:Rule val excessiveAssertions = AndroidOwnerExtraAssertionsRule()
+
+ @Test
+ fun testResetLookaheadPassDelegate() {
+ var placeChild by mutableStateOf(true)
+ var useLookaheadScope by mutableStateOf(true)
+ rule.setContent {
+ val movableContent = remember {
+ movableContentOf {
+ Row(Modifier.padding(5.dp).requiredSize(200.dp)) {
+ Box(Modifier.size(100.dp))
+ Box(Modifier.size(100.dp))
+ if (!useLookaheadScope) {
+ Box(Modifier.size(100.dp))
+ }
+ }
+ }
+ }
+ Box(
+ Modifier.layout { m, c ->
+ m.measure(c).run {
+ layout(width, height) {
+ if (placeChild) {
+ place(0, 0)
+ }
+ }
+ }
+ }
+ ) {
+ // Move moveableContent from a parent in LookaheadScope to a parent that is not
+ // in a LookaheadScope.
+ if (useLookaheadScope) {
+ Box { LookaheadScope { movableContent() } }
+ } else {
+ movableContent()
+ }
+ }
+ }
+
+ rule.waitForIdle()
+ placeChild = false
+ useLookaheadScope = !useLookaheadScope
+ rule.waitForIdle()
+
+ placeChild = true
+ rule.waitForIdle()
+ }
+}
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/LookaheadScopeTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/LookaheadScopeTest.kt
index 73d5f2c..57eb795 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/LookaheadScopeTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/LookaheadScopeTest.kt
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+@file:Suppress("DEPRECATION")
+
package androidx.compose.ui.layout
import androidx.activity.ComponentActivity
@@ -1282,6 +1284,7 @@
}
}
+ @Suppress("DEPRECATION")
@OptIn(ExperimentalLayoutApi::class)
@Test
fun testNestedLookaheadPlacement() {
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/OnGlobalLayoutListenerTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/OnGlobalLayoutListenerTest.kt
index 3ba5d10..1bc9aff 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/OnGlobalLayoutListenerTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/OnGlobalLayoutListenerTest.kt
@@ -42,6 +42,7 @@
import androidx.compose.ui.node.requireOwner
import androidx.compose.ui.platform.InspectorInfo
import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.semanticsId
import androidx.compose.ui.spatial.RelativeLayoutBounds
import androidx.compose.ui.test.SemanticsMatcher
import androidx.compose.ui.test.SemanticsNodeInteraction
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/SubcomposeLayoutTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/SubcomposeLayoutTest.kt
index 2846ab6..e8f8e7d 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/SubcomposeLayoutTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/layout/SubcomposeLayoutTest.kt
@@ -67,6 +67,7 @@
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.semantics.SemanticsNode
+import androidx.compose.ui.semantics.semanticsId
import androidx.compose.ui.test.SemanticsNodeInteraction
import androidx.compose.ui.test.TestActivity
import androidx.compose.ui.test.assertCountEquals
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/semantics/SemanticsListenerTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/semantics/SemanticsListenerTest.kt
index 4311904..5d7a5ff 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/semantics/SemanticsListenerTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/semantics/SemanticsListenerTest.kt
@@ -42,7 +42,6 @@
import androidx.compose.ui.node.invalidateSemantics
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.platform.testTag
-import androidx.compose.ui.test.SemanticsNodeInteraction
import androidx.compose.ui.test.junit4.ComposeContentTestRule
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
@@ -183,7 +182,7 @@
rule.runOnIdle { addModifier = true }
// Assert.
- val semanticsId = rule.onNodeWithTag("item").semanticsId
+ val semanticsId = rule.onNodeWithTag("item").semanticsId()
rule.runOnIdle {
if (isSemanticAutofillEnabled) {
assertThat(events)
@@ -220,7 +219,7 @@
rule.runOnIdle { removeModifier = true }
// Assert.
- val semanticsId = rule.onNodeWithTag("item").semanticsId
+ val semanticsId = rule.onNodeWithTag("item").semanticsId()
rule.runOnIdle {
if (isSemanticAutofillEnabled) {
assertThat(events)
@@ -248,7 +247,7 @@
rule.runOnIdle { text = AnnotatedString("text2") }
// Assert.
- val semanticsId = rule.onNodeWithTag("item").semanticsId
+ val semanticsId = rule.onNodeWithTag("item").semanticsId()
rule.runOnIdle {
if (isSemanticAutofillEnabled) {
assertThat(events)
@@ -281,7 +280,7 @@
rule.runOnIdle { text = AnnotatedString("text2") }
// Assert.
- val semanticsId = rule.onNodeWithTag("item").semanticsId
+ val semanticsId = rule.onNodeWithTag("item").semanticsId()
rule.runOnIdle {
if (isSemanticAutofillEnabled) {
assertThat(events)
@@ -319,7 +318,7 @@
}
// Assert.
- val semanticsId = rule.onNodeWithTag("item").semanticsId
+ val semanticsId = rule.onNodeWithTag("item").semanticsId()
rule.runOnIdle {
if (isSemanticAutofillEnabled) {
assertThat(events)
@@ -347,7 +346,7 @@
rule.runOnIdle { text = "text2" }
// Assert.
- val semanticsId = rule.onNodeWithTag("item").semanticsId
+ val semanticsId = rule.onNodeWithTag("item").semanticsId()
rule.runOnIdle {
if (isSemanticAutofillEnabled) {
assertThat(events)
@@ -376,7 +375,7 @@
rule.runOnIdle { text = "text3" }
// Assert.
- val semanticsId = rule.onNodeWithTag("item").semanticsId
+ val semanticsId = rule.onNodeWithTag("item").semanticsId()
rule.runOnIdle {
if (isSemanticAutofillEnabled) {
assertThat(events)
@@ -417,7 +416,7 @@
rule.runOnIdle { text = "text2" }
// Assert.
- val semanticsId = rule.onNodeWithTag("item").semanticsId
+ val semanticsId = rule.onNodeWithTag("item").semanticsId()
rule.runOnIdle {
if (isSemanticAutofillEnabled) {
assertThat(events)
@@ -455,8 +454,8 @@
rule.onNodeWithTag("item2").requestFocus()
// Assert.
- val item1 = rule.onNodeWithTag("item1").semanticsId
- val item2 = rule.onNodeWithTag("item2").semanticsId
+ val item1 = rule.onNodeWithTag("item1").semanticsId()
+ val item2 = rule.onNodeWithTag("item2").semanticsId()
rule.runOnIdle {
if (isSemanticAutofillEnabled) {
assertThat(events)
@@ -497,8 +496,8 @@
rule.onNodeWithTag("item2").requestFocus()
// Assert.
- val item1 = rule.onNodeWithTag("item1").semanticsId
- val item2 = rule.onNodeWithTag("item2").semanticsId
+ val item1 = rule.onNodeWithTag("item1").semanticsId()
+ val item2 = rule.onNodeWithTag("item2").semanticsId()
rule.runOnIdle {
if (isSemanticAutofillEnabled) {
assertThat(events)
@@ -543,10 +542,6 @@
data class Event<T>(val semanticsId: Int, val prevSemantics: T?, val newSemantics: T?)
- // TODO(b/272068594): Add api to fetch the semantics id from SemanticsNodeInteraction directly.
- private val SemanticsNodeInteraction.semanticsId: Int
- get() = fetchSemanticsNode().id
-
@Composable
private fun FocusableBox(
modifier: Modifier = Modifier,
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/test/TestEnum.java b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/semantics/SemanticsTestUtils.kt
similarity index 66%
copy from navigation/navigation-runtime/src/androidTest/java/androidx/navigation/test/TestEnum.java
copy to compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/semantics/SemanticsTestUtils.kt
index f3a5276..2f5767c 100644
--- a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/test/TestEnum.java
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/semantics/SemanticsTestUtils.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 The Android Open Source Project
+ * 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.
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-package androidx.navigation.test;
+package androidx.compose.ui.semantics
-public enum TestEnum {
- VALUE_ONE, VALUE_TWO
-}
+import androidx.compose.ui.test.SemanticsNodeInteraction
+
+/** Fetch the id of the semantics node. */
+internal fun SemanticsNodeInteraction.semanticsId(): Int = fetchSemanticsNode().id
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/viewinterop/FocusSearchLeftInteropTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/viewinterop/FocusSearchLeftInteropTest.kt
index dacdc31..cfeedac 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/viewinterop/FocusSearchLeftInteropTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/viewinterop/FocusSearchLeftInteropTest.kt
@@ -47,6 +47,7 @@
import androidx.compose.ui.test.requestFocus
import androidx.compose.ui.unit.dp
import androidx.test.filters.MediumTest
+import androidx.test.filters.SdkSuppress
import androidx.test.platform.app.InstrumentationRegistry
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
@@ -448,6 +449,7 @@
}
@Test
+ @SdkSuppress(minSdkVersion = 23) // b/387310914 -- flaky on API 21
fun viewComposableInRow() {
// Arrange.
setContent {
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/autofill/AndroidAutofill.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/autofill/AndroidAutofill.android.kt
index 680dcbd..4548793 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/autofill/AndroidAutofill.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/autofill/AndroidAutofill.android.kt
@@ -37,7 +37,10 @@
* @param autofillTree The autofill tree. This will be replaced by a semantic tree (b/138604305).
*/
@RequiresApi(Build.VERSION_CODES.O)
-internal class AndroidAutofill(val view: View, val autofillTree: AutofillTree) : Autofill {
+internal class AndroidAutofill(
+ val view: View,
+ val autofillTree: @Suppress("Deprecation") AutofillTree
+) : @Suppress("Deprecation") Autofill {
val autofillManager =
view.context.getSystemService(AutofillManager::class.java)
@@ -50,7 +53,7 @@
checkPreconditionNotNull(ViewCompatShims.getAutofillId(view)?.toAutofillId())
}
- override fun requestAutofillForNode(autofillNode: AutofillNode) {
+ override fun requestAutofillForNode(autofillNode: @Suppress("Deprecation") AutofillNode) {
val boundingBox =
autofillNode.boundingBox ?: error("requestAutofill called before onChildPositioned()")
@@ -69,7 +72,7 @@
)
}
- override fun cancelAutofillForNode(autofillNode: AutofillNode) {
+ override fun cancelAutofillForNode(autofillNode: @Suppress("Deprecation") AutofillNode) {
autofillManager.notifyViewExited(view, autofillNode.id)
}
}
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/autofill/AndroidAutofillType.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/autofill/AndroidAutofillType.android.kt
index cb92103..47e8a57 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/autofill/AndroidAutofillType.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/autofill/AndroidAutofillType.android.kt
@@ -57,6 +57,7 @@
* Gets the Android specific [AutofillHint][android.view.ViewStructure.setAutofillHints]
* corresponding to the current [AutofillType].
*/
+@Suppress("Deprecation")
internal val AutofillType.androidType: String
get() {
val androidAutofillType = androidAutofillTypes[this]
@@ -65,6 +66,7 @@
}
/** Maps each [AutofillType] to one of the autofill hints in [androidx.autofill.HintConstants] */
+@Suppress("Deprecation")
private val androidAutofillTypes: HashMap<AutofillType, String> =
hashMapOf(
AutofillType.EmailAddress to AUTOFILL_HINT_EMAIL_ADDRESS,
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt
index 811066f..54dec05 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeViewAccessibilityDelegateCompat.android.kt
@@ -107,6 +107,8 @@
import androidx.core.view.accessibility.AccessibilityEventCompat
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.FOCUS_ACCESSIBILITY
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.FOCUS_INPUT
import androidx.core.view.accessibility.AccessibilityNodeProviderCompat
import androidx.lifecycle.Lifecycle
import kotlin.math.abs
@@ -342,7 +344,9 @@
private val handler = Handler(Looper.getMainLooper())
private var nodeProvider = ComposeAccessibilityNodeProvider()
+ private var accessibilityFocusedVirtualViewId = InvalidId
private var focusedVirtualViewId = InvalidId
+ private var currentlyAccessibilityFocusedANI: AccessibilityNodeInfoCompat? = null
private var currentlyFocusedANI: AccessibilityNodeInfoCompat? = null
private var sendingFocusAffectingEvent = false
private val pendingHorizontalScrollEvents = MutableIntObjectMap<ScrollAxisRange>()
@@ -624,7 +628,7 @@
}
// Manage internal accessibility focus state.
- if (virtualViewId == focusedVirtualViewId) {
+ if (virtualViewId == accessibilityFocusedVirtualViewId) {
info.isAccessibilityFocused = true
info.addAction(AccessibilityActionCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS)
} else {
@@ -703,6 +707,7 @@
info.isFocused = semanticsNode.unmergedConfig[SemanticsProperties.Focused]
if (info.isFocused) {
info.addAction(AccessibilityNodeInfoCompat.ACTION_CLEAR_FOCUS)
+ focusedVirtualViewId = virtualViewId
} else {
info.addAction(AccessibilityNodeInfoCompat.ACTION_FOCUS)
}
@@ -1104,7 +1109,7 @@
* @return True if the view is accessibility focused.
*/
private fun isAccessibilityFocused(virtualViewId: Int): Boolean {
- return (focusedVirtualViewId == virtualViewId)
+ return (accessibilityFocusedVirtualViewId == virtualViewId)
}
/**
@@ -1125,15 +1130,15 @@
// TODO: Check virtual view visibility.
if (!isAccessibilityFocused(virtualViewId)) {
// Clear focus from the previously focused view, if applicable.
- if (focusedVirtualViewId != InvalidId) {
+ if (accessibilityFocusedVirtualViewId != InvalidId) {
sendEventForVirtualView(
- focusedVirtualViewId,
+ accessibilityFocusedVirtualViewId,
AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED
)
}
// Set focus on the new view.
- focusedVirtualViewId = virtualViewId
+ accessibilityFocusedVirtualViewId = virtualViewId
// TODO(b/272068594): Do we have to set currentlyFocusedANI object too?
view.invalidate()
@@ -1265,8 +1270,8 @@
*/
private fun clearAccessibilityFocus(virtualViewId: Int): Boolean {
if (isAccessibilityFocused(virtualViewId)) {
- focusedVirtualViewId = InvalidId
- currentlyFocusedANI = null
+ accessibilityFocusedVirtualViewId = InvalidId
+ currentlyAccessibilityFocusedANI = null
view.invalidate()
sendEventForVirtualView(
virtualViewId,
@@ -2496,6 +2501,20 @@
// Refresh the current "green box" bounds and invalidate the View to tell
// ViewRootImpl to redraw it at its latest position.
+ currentSemanticsNodes[accessibilityFocusedVirtualViewId]?.let {
+ try {
+ currentlyAccessibilityFocusedANI?.setBoundsInScreen(boundsInScreen(it))
+ } catch (e: IllegalStateException) {
+ // setBoundsInScreen could in theory throw an IllegalStateException if the
+ // system has previously sealed the AccessibilityNodeInfo. This cannot
+ // happen on stock AOSP, because ViewRootImpl only uses it for bounds
+ // checking, and never forwards it to an accessibility service. But that is
+ // a non-CTS-enforced implementation detail, so we should avoid crashing if
+ // this happens.
+ }
+ }
+ // Refresh the current "blue box" bounds and invalidate the View to tell
+ // ViewRootImpl to redraw it at its latest position.
currentSemanticsNodes[focusedVirtualViewId]?.let {
try {
currentlyFocusedANI?.setBoundsInScreen(boundsInScreen(it))
@@ -2820,8 +2839,13 @@
private inner class ComposeAccessibilityNodeProvider : AccessibilityNodeProviderCompat() {
override fun createAccessibilityNodeInfo(virtualViewId: Int): AccessibilityNodeInfoCompat? {
return createNodeInfo(virtualViewId).also {
- if (sendingFocusAffectingEvent && virtualViewId == focusedVirtualViewId) {
- currentlyFocusedANI = it
+ if (sendingFocusAffectingEvent) {
+ if (virtualViewId == accessibilityFocusedVirtualViewId) {
+ currentlyAccessibilityFocusedANI = it
+ }
+ if (virtualViewId == focusedVirtualViewId) {
+ currentlyFocusedANI = it
+ }
}
}
}
@@ -2840,7 +2864,15 @@
}
override fun findFocus(focus: Int): AccessibilityNodeInfoCompat? {
- return createAccessibilityNodeInfo(focusedVirtualViewId)
+ return when (focus) {
+ // TODO(b/364744967): add test for FOCUS_ACCESSIBILITY
+ FOCUS_ACCESSIBILITY ->
+ createAccessibilityNodeInfo(accessibilityFocusedVirtualViewId)
+ FOCUS_INPUT ->
+ if (focusedVirtualViewId == InvalidId) null
+ else createAccessibilityNodeInfo(focusedVirtualViewId)
+ else -> throw IllegalArgumentException("Unknown focus type: $focus")
+ }
}
}
diff --git a/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/autofill/AndroidAutofillTypeTest.kt b/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/autofill/AndroidAutofillTypeTest.kt
index b5c8886..bc1dca0 100644
--- a/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/autofill/AndroidAutofillTypeTest.kt
+++ b/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/autofill/AndroidAutofillTypeTest.kt
@@ -21,6 +21,7 @@
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
+@Suppress("Deprecation")
@RunWith(JUnit4::class)
class AndroidAutofillTypeTest {
diff --git a/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/autofill/AndroidPerformAutofillTest.kt b/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/autofill/AndroidPerformAutofillTest.kt
index 126bfc9..3b25b9d 100644
--- a/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/autofill/AndroidPerformAutofillTest.kt
+++ b/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/autofill/AndroidPerformAutofillTest.kt
@@ -32,7 +32,7 @@
@RunWith(RobolectricTestRunner::class)
@Config(minSdk = 26)
class AndroidPerformAutofillTest {
- private val autofillTree = AutofillTree()
+ private val autofillTree = @Suppress("Deprecation") AutofillTree()
private lateinit var androidAutofill: AndroidAutofill
@Before
@@ -50,6 +50,7 @@
val expectedValue = "Name"
var autoFilledValue = ""
val autofillNode =
+ @Suppress("Deprecation")
AutofillNode(
onFill = { autoFilledValue = it },
autofillTypes = listOf(AutofillType.PersonFullName),
@@ -75,6 +76,7 @@
val expectedValue = "email@google.com"
var autoFilledValue = ""
val autofillNode =
+ @Suppress("Deprecation")
AutofillNode(
onFill = { autoFilledValue = it },
autofillTypes = listOf(AutofillType.EmailAddress),
diff --git a/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/autofill/AndroidPopulateViewStructureTest.kt b/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/autofill/AndroidPopulateViewStructureTest.kt
index af54aa9..bfba901 100644
--- a/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/autofill/AndroidPopulateViewStructureTest.kt
+++ b/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/autofill/AndroidPopulateViewStructureTest.kt
@@ -32,7 +32,7 @@
@RunWith(RobolectricTestRunner::class)
@Config(manifest = Config.NONE, minSdk = 26)
class AndroidPopulateViewStructureTest {
- private val autofillTree = AutofillTree()
+ private val autofillTree = @Suppress("Deprecation") AutofillTree()
private lateinit var androidAutofill: AndroidAutofill
private lateinit var currentPackage: String
@@ -62,6 +62,7 @@
fun populateViewStructure_oneChild() {
// Arrange.
val autofillNode =
+ @Suppress("Deprecation")
AutofillNode(
onFill = {},
autofillTypes = listOf(AutofillType.PersonFullName),
@@ -94,6 +95,7 @@
fun populateViewStructure_twoChildren() {
// Arrange.
val nameAutofillNode =
+ @Suppress("Deprecation")
AutofillNode(
onFill = {},
autofillTypes = listOf(AutofillType.PersonFullName),
@@ -102,6 +104,7 @@
autofillTree += nameAutofillNode
val emailAutofillNode =
+ @Suppress("Deprecation")
AutofillNode(
onFill = {},
autofillTypes = listOf(AutofillType.EmailAddress),
diff --git a/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/autofill/AutofillNodeTest.kt b/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/autofill/AutofillNodeTest.kt
index debfc97..beef42e 100644
--- a/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/autofill/AutofillNodeTest.kt
+++ b/compose/ui/ui/src/androidUnitTest/kotlin/androidx/compose/ui/autofill/AutofillNodeTest.kt
@@ -39,7 +39,7 @@
private lateinit var androidAutofill: AndroidAutofill
private lateinit var autofillManager: ShadowAutofillManager
private lateinit var view: View
- private val autofillTree = AutofillTree()
+ private val autofillTree = @Suppress("Deprecation") AutofillTree()
@Before
fun setup() {
@@ -57,6 +57,7 @@
@Test
fun eachInstanceHasUniqueId() {
+ @Suppress("Deprecation")
assertThat(listOf(AutofillNode {}.id, AutofillNode {}.id, AutofillNode {}.id))
.containsNoDuplicates()
}
@@ -70,7 +71,7 @@
fun requestAutofillForNode_calls_notifyViewEntered() {
// Arrange.
val bounds = Rect(0f, 0f, 0f, 0f)
- val autofillNode = AutofillNode(onFill = {}, boundingBox = bounds)
+ val autofillNode = @Suppress("Deprecation") AutofillNode(onFill = {}, boundingBox = bounds)
// Act.
androidAutofill.requestAutofillForNode(autofillNode)
@@ -83,7 +84,7 @@
@Test
fun requestAutofillForNode_beforeComposableIsPositioned_throwsError() {
// Arrange - Before the composable is positioned, the boundingBox is null.
- val autofillNode = AutofillNode(onFill = {})
+ val autofillNode = @Suppress("Deprecation") AutofillNode(onFill = {})
// Act and assert.
val exception =
@@ -100,7 +101,7 @@
@Test
fun cancelAutofillForNode_calls_notifyViewExited() {
// Arrange.
- val autofillNode = AutofillNode(onFill = {})
+ val autofillNode = @Suppress("Deprecation") AutofillNode(onFill = {})
// Act.
androidAutofill.cancelAutofillForNode(autofillNode)
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/ComposeUiFlags.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/ComposeUiFlags.kt
index d1c4c32..dd3946e 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/ComposeUiFlags.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/ComposeUiFlags.kt
@@ -75,11 +75,10 @@
var NewNestedScrollFlingDispatchingEnabled: Boolean = true
/**
- * With this flag on, the new semantic version of Autofill will be enabled. Prior to the
- * semantics refactoring, this will introduce significant overhead, but can be used to test out
- * the new Autofill APIs and features introduced.
+ * With this flag on, the new semantic version of Autofill APIs will be enabled. Turning this
+ * flag off will disable the new Semantic Autofill APIs, and the new refactored semantics.
*/
- @Suppress("MutableBareField") @JvmField var isSemanticAutofillEnabled: Boolean = false
+ @Suppress("MutableBareField") @JvmField var isSemanticAutofillEnabled: Boolean = true
/**
* This enables fixes for View focus. The changes are large enough to require a flag to allow
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/autofill/Autofill.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/autofill/Autofill.kt
index bc0ac620..fd42810 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/autofill/Autofill.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/autofill/Autofill.kt
@@ -30,6 +30,14 @@
* request or cancel autofill as required. For instance, the TextField can call
* [requestAutofillForNode] when it gains focus, and [cancelAutofillForNode] when it loses focus.
*/
+@Deprecated(
+ """
+ You no longer have to call these apis when focus changes. They will be called
+ automatically when you Use the new semantics based APIs for autofill. Use the
+ androidx.compose.ui.autofill.ContentType and androidx.compose.ui.autofill.ContentDataType
+ semantics properties instead.
+ """
+)
interface Autofill {
/**
@@ -37,22 +45,22 @@
*
* @param autofillNode The node that needs to be auto-filled.
*
- * This function is usually called when an autofillable component gains focus.
+ * This function is usually called when an autofill-able component gains focus.
*/
- fun requestAutofillForNode(autofillNode: AutofillNode)
+ fun requestAutofillForNode(autofillNode: @Suppress("Deprecation") AutofillNode)
/**
* Cancel a previously supplied autofill request.
*
* @param autofillNode The node that needs to be auto-filled.
*
- * This function is usually called when an autofillable component loses focus.
+ * This function is usually called when an autofill-able component loses focus.
*/
- fun cancelAutofillForNode(autofillNode: AutofillNode)
+ fun cancelAutofillForNode(autofillNode: @Suppress("Deprecation") AutofillNode)
}
/**
- * Every autofillable composable will have an [AutofillNode]. (An autofill node will be created for
+ * Every autofill-able composable will have an [AutofillNode]. (An autofill node will be created for
* every semantics node that adds autofill properties). This node is used to request/cancel
* autofill, and it holds the [onFill] lambda which is called by the autofill framework.
*
@@ -67,8 +75,14 @@
* @property onFill The callback that is called by the autofill framework to perform autofill.
* @property id A virtual id that is automatically generated for each node.
*/
+@Deprecated(
+ """
+ Use the new semantics-based Autofill APIs androidx.compose.ui.autofill.ContentType and
+ androidx.compose.ui.autofill.ContentDataType instead.
+ """
+)
class AutofillNode(
- val autofillTypes: List<AutofillType> = listOf(),
+ val autofillTypes: List<@Suppress("Deprecation") AutofillType> = listOf(),
var boundingBox: Rect? = null,
val onFill: ((String) -> Unit)?
) {
@@ -87,7 +101,7 @@
override fun equals(other: Any?): Boolean {
if (this === other) return true
- if (other !is AutofillNode) return false
+ if (other !is @Suppress("Deprecation") AutofillNode) return false
if (autofillTypes != other.autofillTypes) return false
if (boundingBox != other.boundingBox) return false
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/autofill/AutofillManager.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/autofill/AutofillManager.kt
index f5ebdc7..a57c432 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/autofill/AutofillManager.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/autofill/AutofillManager.kt
@@ -39,7 +39,7 @@
*
* Call this function to notify the Autofill framework that the current session should be
* canceled. After calling this function, the framework will stop the current autofill session
- * without processing any information entered in the autofillable field.
+ * without processing any information entered in the autofill-able field.
*/
abstract fun cancel()
}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/autofill/AutofillTree.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/autofill/AutofillTree.kt
index ead55c5..5ed237c 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/autofill/AutofillTree.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/autofill/AutofillTree.kt
@@ -27,12 +27,18 @@
* Since this is a temporary implementation, it is implemented as a list of [children], which is
* essentially a tree of height = 1
*/
+@Deprecated(
+ """
+ Use the new semantics-based Autofill APIs androidx.compose.ui.autofill.ContentType and
+ androidx.compose.ui.autofill.ContentDataType instead.
+ """
+)
class AutofillTree {
- /** A map which contains [AutofillNode]s, where every node represents an autofillable field. */
- val children: MutableMap<Int, AutofillNode> = mutableMapOf()
+ /** A map which contains [AutofillNode]s, where every node represents an autofill-able field. */
+ val children: MutableMap<Int, @Suppress("Deprecation") AutofillNode> = mutableMapOf()
/** Add the specified [AutofillNode] to the [AutofillTree]. */
- operator fun plusAssign(autofillNode: AutofillNode) {
+ operator fun plusAssign(autofillNode: @Suppress("Deprecation") AutofillNode) {
children[autofillNode.id] = autofillNode
}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/autofill/AutofillType.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/autofill/AutofillType.kt
index 8449d95..957ec86 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/autofill/AutofillType.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/autofill/AutofillType.kt
@@ -24,7 +24,11 @@
* Autofill services use the [AutofillType] to determine what value to use to autofill fields
* associated with this type. If the [AutofillType] is not specified, the autofill services have to
* use heuristics to determine the right value to use while auto-filling the corresponding field.
+ *
+ * This has been deprecated in favor of a new semantics based API. Use
+ * [ContentType][androidx.compose.ui.semantics.SemanticsProperties.ContentType] instead.
*/
+@Deprecated("Use the new semantics-based API and androidx.compose.ui.autofill.ContentType instead.")
enum class AutofillType {
/** Indicates that the associated component can be auto-filled with an email address. */
EmailAddress,
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt
index ef5efb6..1ce3b5b 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNode.kt
@@ -118,6 +118,12 @@
if (newRoot != null) {
layoutDelegate.ensureLookaheadDelegateCreated()
forEachCoordinatorIncludingInner { it.ensureLookaheadDelegateCreated() }
+ } else {
+ // When lookahead root is set to null, clear the lookahead pass delegate.
+ // This can happen when lookaheadScope is removed in one of the parents, or
+ // more likely when movableContent moves from a parent in a LookaheadScope to
+ // a parent not in a LookaheadScope.
+ layoutDelegate.clearLookaheadDelegate()
}
invalidateMeasurements()
}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeLayoutDelegate.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeLayoutDelegate.kt
index cb30258..65d8e99 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeLayoutDelegate.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeLayoutDelegate.kt
@@ -368,6 +368,10 @@
measurePassDelegate.childDelegatesDirty = true
lookaheadPassDelegate?.let { it.childDelegatesDirty = true }
}
+
+ fun clearLookaheadDelegate() {
+ lookaheadPassDelegate = null
+ }
}
/**
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/Owner.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/Owner.kt
index ee7342a..7e031fd 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/Owner.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/Owner.kt
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-@file:Suppress("DEPRECATION")
package androidx.compose.ui.node
@@ -21,9 +20,7 @@
import androidx.collection.IntObjectMap
import androidx.compose.runtime.Applier
import androidx.compose.ui.InternalComposeUiApi
-import androidx.compose.ui.autofill.Autofill
import androidx.compose.ui.autofill.AutofillManager
-import androidx.compose.ui.autofill.AutofillTree
import androidx.compose.ui.draganddrop.DragAndDropManager
import androidx.compose.ui.focus.FocusDirection
import androidx.compose.ui.focus.FocusOwner
@@ -41,7 +38,6 @@
import androidx.compose.ui.modifier.ModifierLocalManager
import androidx.compose.ui.platform.AccessibilityManager
import androidx.compose.ui.platform.Clipboard
-import androidx.compose.ui.platform.ClipboardManager
import androidx.compose.ui.platform.PlatformTextInputModifierNode
import androidx.compose.ui.platform.PlatformTextInputSessionScope
import androidx.compose.ui.platform.SoftwareKeyboardController
@@ -52,7 +48,6 @@
import androidx.compose.ui.spatial.RectManager
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
-import androidx.compose.ui.text.input.TextInputService
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.LayoutDirection
@@ -88,7 +83,7 @@
val inputModeManager: InputModeManager
/** Provide clipboard manager to the user. Use the Android version of clipboard manager. */
- val clipboardManager: ClipboardManager
+ val clipboardManager: @Suppress("Deprecation") androidx.compose.ui.platform.ClipboardManager
/**
* Provide clipboard manager with suspend function to the user. Use the Android version of
@@ -114,17 +109,14 @@
/**
* A data structure used to store autofill information. It is used by components that want to
* provide autofill semantics.
- *
- * TODO(ralu): Replace with SemanticsTree. This is a temporary hack until we have a semantics
- * tree implemented.
*/
- val autofillTree: AutofillTree
+ val autofillTree: @Suppress("Deprecation") androidx.compose.ui.autofill.AutofillTree
/**
- * The [Autofill] class can be used to perform autofill operations. It is used as a
- * CompositionLocal.
+ * The [Autofill][androidx.compose.ui.autofill.Autofill] class can be used to perform autofill
+ * operations. It is used as a CompositionLocal.
*/
- val autofill: Autofill?
+ val autofill: @Suppress("Deprecation") androidx.compose.ui.autofill.Autofill?
/**
* The [AutofillManager] class can be used to perform autofill operations. It is used as a
@@ -134,7 +126,7 @@
val density: Density
- val textInputService: TextInputService
+ val textInputService: @Suppress("Deprecation") androidx.compose.ui.text.input.TextInputService
val softwareKeyboardController: SoftwareKeyboardController
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/platform/CompositionLocals.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/platform/CompositionLocals.kt
index f4fa22c..421ffde 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/platform/CompositionLocals.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/platform/CompositionLocals.kt
@@ -52,9 +52,12 @@
* The CompositionLocal that can be used to trigger autofill actions. Eg.
* [Autofill.requestAutofillForNode].
*/
-@Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
-@get:ExperimentalComposeUiApi
-@ExperimentalComposeUiApi
+@Deprecated(
+ """
+ Use the new semantics-based Autofill APIs androidx.compose.ui.autofill.ContentType and
+ androidx.compose.ui.autofill.ContentDataType instead.
+ """
+)
val LocalAutofill = staticCompositionLocalOf<Autofill?> { null }
/**
@@ -62,14 +65,18 @@
* androidx.compose.ui.autofill.AutofillNode]s to the autofill tree. The [AutofillTree] is a
* temporary data structure that will be replaced by Autofill Semantics (b/138604305).
*/
-@Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
+@Deprecated(
+ """
+ Use the new semantics-based Autofill APIs androidx.compose.ui.autofill.ContentType and
+ androidx.compose.ui.autofill.ContentDataType instead.
+ """
+)
val LocalAutofillTree =
staticCompositionLocalOf<AutofillTree> { noLocalProvidedFor("LocalAutofillTree") }
/**
* The CompositionLocal that can be used to trigger autofill actions. Eg. [AutofillManager.commit].
*/
-@Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
val LocalAutofillManager =
staticCompositionLocalOf<AutofillManager?> { noLocalProvidedFor("LocalAutofillManager") }
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/spatial/RectList.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/spatial/RectList.kt
index 259ae65..ee5c718 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/spatial/RectList.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/spatial/RectList.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-@file:Suppress("NOTHING_TO_INLINE")
+@file:Suppress("NOTHING_TO_INLINE", "KotlinRedundantDiagnosticSuppress")
package androidx.compose.ui.spatial
@@ -98,19 +98,23 @@
* keep this in mind if you call this method and have cached any of those values in a local
* variable, you may need to refresh them.
*/
- internal fun allocateItemsIndex(): Int {
+ private inline fun allocateItemsIndex(): Int {
val currentItems = items
val currentSize = itemsSize
itemsSize = currentSize + LongsPerItem
val actualSize = currentItems.size
if (actualSize <= currentSize + LongsPerItem) {
- val newSize = max(actualSize * 2, currentSize + LongsPerItem)
- items = currentItems.copyOf(newSize)
- stack = stack.copyOf(newSize)
+ resizeStorage(actualSize, currentSize, currentItems)
}
return currentSize
}
+ private fun resizeStorage(actualSize: Int, currentSize: Int, currentItems: LongArray) {
+ val newSize = max(actualSize * 2, currentSize + LongsPerItem)
+ items = currentItems.copyOf(newSize)
+ stack = stack.copyOf(newSize)
+ }
+
/**
* Insert a value and corresponding bounding rectangle into the RectList. This method does not
* check to see that [value] doesn't already exist somewhere in the list.
diff --git a/constraintlayout/constraintlayout-core/build.gradle b/constraintlayout/constraintlayout-core/build.gradle
index a2570c0..8826adc 100644
--- a/constraintlayout/constraintlayout-core/build.gradle
+++ b/constraintlayout/constraintlayout-core/build.gradle
@@ -29,6 +29,7 @@
}
dependencies {
+ api(libs.jspecify)
api("androidx.annotation:annotation:1.8.1")
testImplementation(libs.junit)
}
@@ -39,6 +40,4 @@
mavenVersion = LibraryVersions.CONSTRAINTLAYOUT_CORE
inceptionYear = "2022"
description = "This library contains engines and algorithms for constraint based layout and complex animations (it is used by the ConstraintLayout library)"
- // TODO: b/326456246
- optOutJSpecify = true
}
diff --git a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/parser/CLContainer.java b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/parser/CLContainer.java
index f3a66d0..9791396 100644
--- a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/parser/CLContainer.java
+++ b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/parser/CLContainer.java
@@ -15,7 +15,7 @@
*/
package androidx.constraintlayout.core.parser;
-import androidx.annotation.NonNull;
+import org.jspecify.annotations.NonNull;
import java.util.ArrayList;
import java.util.Objects;
@@ -352,9 +352,8 @@
return null;
}
- @NonNull
@Override
- public CLContainer clone() {
+ public @NonNull CLContainer clone() {
CLContainer clone = (CLContainer) super.clone();
ArrayList<CLElement> clonedArray = new ArrayList<>(mElements.size());
for (CLElement element: mElements) {
diff --git a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/parser/CLElement.java b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/parser/CLElement.java
index acb7a97..f8badbf 100644
--- a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/parser/CLElement.java
+++ b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/parser/CLElement.java
@@ -15,7 +15,7 @@
*/
package androidx.constraintlayout.core.parser;
-import androidx.annotation.NonNull;
+import org.jspecify.annotations.NonNull;
import java.util.Arrays;
import java.util.Objects;
@@ -204,9 +204,8 @@
return result;
}
- @NonNull
@Override
- public CLElement clone() {
+ public @NonNull CLElement clone() {
try {
return (CLElement) super.clone();
} catch (CloneNotSupportedException e) {
diff --git a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/parser/CLObject.java b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/parser/CLObject.java
index 479edae..49cdbfa 100644
--- a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/parser/CLObject.java
+++ b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/parser/CLObject.java
@@ -15,7 +15,7 @@
*/
package androidx.constraintlayout.core.parser;
-import androidx.annotation.NonNull;
+import org.jspecify.annotations.NonNull;
import java.util.Iterator;
@@ -106,9 +106,8 @@
}
}
- @NonNull
@Override
- public CLObject clone() {
+ public @NonNull CLObject clone() {
// Overriding to get expected return type
return (CLObject) super.clone();
}
diff --git a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/parser/CLString.java b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/parser/CLString.java
index 5ce2fd4..173657f 100644
--- a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/parser/CLString.java
+++ b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/parser/CLString.java
@@ -15,7 +15,7 @@
*/
package androidx.constraintlayout.core.parser;
-import androidx.annotation.NonNull;
+import org.jspecify.annotations.NonNull;
/**
* {@link CLElement} implementation for json Strings when used as property values or array elements.
@@ -34,8 +34,7 @@
/**
* Creates a {@link CLString} element from a String object.
*/
- @NonNull
- public static CLString from(@NonNull String content) {
+ public static @NonNull CLString from(@NonNull String content) {
CLString stringElement = new CLString(content.toCharArray());
stringElement.setStart(0L);
stringElement.setEnd(content.length() - 1);
diff --git a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/ConstraintReference.java b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/ConstraintReference.java
index 53e09a9..facf8ac 100644
--- a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/ConstraintReference.java
+++ b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/ConstraintReference.java
@@ -20,13 +20,14 @@
import static androidx.constraintlayout.core.widgets.ConstraintWidget.UNKNOWN;
import static androidx.constraintlayout.core.widgets.ConstraintWidget.VERTICAL;
-import androidx.annotation.Nullable;
import androidx.constraintlayout.core.motion.utils.TypedBundle;
import androidx.constraintlayout.core.motion.utils.TypedValues;
import androidx.constraintlayout.core.state.helpers.Facade;
import androidx.constraintlayout.core.widgets.ConstraintAnchor;
import androidx.constraintlayout.core.widgets.ConstraintWidget;
+import org.jspecify.annotations.Nullable;
+
import java.util.ArrayList;
import java.util.HashMap;
diff --git a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/ConstraintSetParser.java b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/ConstraintSetParser.java
index efd960d..1cf37f2 100644
--- a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/ConstraintSetParser.java
+++ b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/ConstraintSetParser.java
@@ -22,7 +22,6 @@
import static androidx.constraintlayout.core.widgets.ConstraintWidget.HORIZONTAL;
import static androidx.constraintlayout.core.widgets.ConstraintWidget.VERTICAL;
-import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
import androidx.constraintlayout.core.motion.utils.TypedBundle;
import androidx.constraintlayout.core.motion.utils.TypedValues;
@@ -42,6 +41,8 @@
import androidx.constraintlayout.core.widgets.ConstraintWidget;
import androidx.constraintlayout.core.widgets.Flow;
+import org.jspecify.annotations.NonNull;
+
import java.util.ArrayList;
import java.util.HashMap;
diff --git a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/State.java b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/State.java
index a0f208d..84056ff 100644
--- a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/State.java
+++ b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/State.java
@@ -20,7 +20,6 @@
import static androidx.constraintlayout.core.widgets.ConstraintWidget.CHAIN_SPREAD;
import static androidx.constraintlayout.core.widgets.ConstraintWidget.CHAIN_SPREAD_INSIDE;
-import androidx.annotation.NonNull;
import androidx.constraintlayout.core.state.helpers.AlignHorizontallyReference;
import androidx.constraintlayout.core.state.helpers.AlignVerticallyReference;
import androidx.constraintlayout.core.state.helpers.BarrierReference;
@@ -33,6 +32,8 @@
import androidx.constraintlayout.core.widgets.ConstraintWidgetContainer;
import androidx.constraintlayout.core.widgets.HelperWidget;
+import org.jspecify.annotations.NonNull;
+
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
@@ -423,8 +424,7 @@
* @param gridType type of Grid pattern - Grid, Row, or Column
* @return a GridReference object
*/
- @NonNull
- public GridReference getGrid(@NonNull Object key, @NonNull String gridType) {
+ public @NonNull GridReference getGrid(@NonNull Object key, @NonNull String gridType) {
ConstraintReference reference = constraints(key);
if (reference.getFacade() == null || !(reference.getFacade() instanceof GridReference)) {
State.Helper Type = Helper.GRID;
diff --git a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/Transition.java b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/Transition.java
index cf66b93..9b605dd 100644
--- a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/Transition.java
+++ b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/Transition.java
@@ -16,7 +16,6 @@
package androidx.constraintlayout.core.state;
-import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
import androidx.constraintlayout.core.motion.CustomVariable;
import androidx.constraintlayout.core.motion.Motion;
@@ -35,6 +34,8 @@
import androidx.constraintlayout.core.widgets.ConstraintWidget;
import androidx.constraintlayout.core.widgets.ConstraintWidgetContainer;
+import org.jspecify.annotations.NonNull;
+
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
diff --git a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/TransitionParser.java b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/TransitionParser.java
index 9801b39..29b7c16 100644
--- a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/TransitionParser.java
+++ b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/TransitionParser.java
@@ -18,7 +18,6 @@
import static androidx.constraintlayout.core.state.ConstraintSetParser.parseColorString;
-import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
import androidx.constraintlayout.core.motion.CustomVariable;
import androidx.constraintlayout.core.motion.utils.TypedBundle;
@@ -31,6 +30,8 @@
import androidx.constraintlayout.core.parser.CLObject;
import androidx.constraintlayout.core.parser.CLParsingException;
+import org.jspecify.annotations.NonNull;
+
/**
* Contains code for Parsing Transitions
*/
diff --git a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/WidgetFrame.java b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/WidgetFrame.java
index 4adf478..5ab1701 100644
--- a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/WidgetFrame.java
+++ b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/WidgetFrame.java
@@ -16,7 +16,6 @@
package androidx.constraintlayout.core.state;
-import androidx.annotation.NonNull;
import androidx.constraintlayout.core.motion.CustomAttribute;
import androidx.constraintlayout.core.motion.CustomVariable;
import androidx.constraintlayout.core.motion.utils.TypedBundle;
@@ -29,6 +28,8 @@
import androidx.constraintlayout.core.widgets.ConstraintAnchor;
import androidx.constraintlayout.core.widgets.ConstraintWidget;
+import org.jspecify.annotations.NonNull;
+
import java.util.HashMap;
import java.util.Set;
diff --git a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/helpers/ChainReference.java b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/helpers/ChainReference.java
index 51269c7..ea5b4a7 100644
--- a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/helpers/ChainReference.java
+++ b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/helpers/ChainReference.java
@@ -18,11 +18,12 @@
import static androidx.constraintlayout.core.widgets.ConstraintWidget.UNKNOWN;
-import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
import androidx.constraintlayout.core.state.HelperReference;
import androidx.constraintlayout.core.state.State;
+import org.jspecify.annotations.NonNull;
+
import java.util.HashMap;
/**
@@ -55,13 +56,13 @@
private HashMap<String, Float> mMapPreGoneMargin;
private HashMap<String, Float> mMapPostGoneMargin;
- protected @NonNull State.Chain mStyle = State.Chain.SPREAD;
+ protected State.@NonNull Chain mStyle = State.Chain.SPREAD;
- public ChainReference(@NonNull State state, @NonNull State.Helper type) {
+ public ChainReference(@NonNull State state, State.@NonNull Helper type) {
super(state, type);
}
- public @NonNull State.Chain getStyle() {
+ public State.@NonNull Chain getStyle() {
return State.Chain.SPREAD;
}
@@ -71,8 +72,7 @@
* @param style Defines the way the chain will lay out its elements
* @return This same instance
*/
- @NonNull
- public ChainReference style(@NonNull State.Chain style) {
+ public @NonNull ChainReference style(State.@NonNull Chain style) {
mStyle = style;
return this;
}
@@ -191,9 +191,8 @@
}
// @TODO: add description
- @NonNull
@Override
- public ChainReference bias(float bias) {
+ public @NonNull ChainReference bias(float bias) {
mBias = bias;
return this;
}
diff --git a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/helpers/GridReference.java b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/helpers/GridReference.java
index d2168ad..900ef31 100644
--- a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/helpers/GridReference.java
+++ b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/state/helpers/GridReference.java
@@ -16,13 +16,14 @@
package androidx.constraintlayout.core.state.helpers;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.constraintlayout.core.state.HelperReference;
import androidx.constraintlayout.core.state.State;
import androidx.constraintlayout.core.utils.GridCore;
import androidx.constraintlayout.core.widgets.HelperWidget;
+import org.jspecify.annotations.NonNull;
+import org.jspecify.annotations.Nullable;
+
/**
* A HelperReference of a Grid Helper that helps enable Grid in Compose
*/
@@ -31,7 +32,7 @@
private static final String SPANS_RESPECT_WIDGET_ORDER_STRING = "spansrespectwidgetorder";
private static final String SUB_GRID_BY_COL_ROW_STRING = "subgridbycolrow";
- public GridReference(@NonNull State state, @NonNull State.Helper type) {
+ public GridReference(@NonNull State state, State.@NonNull Helper type) {
super(state, type);
if (type == State.Helper.ROW) {
this.mRowsSet = 1;
@@ -292,8 +293,7 @@
* Get the row weights
* @return the row weights
*/
- @Nullable
- public String getRowWeights() {
+ public @Nullable String getRowWeights() {
return mRowWeights;
}
@@ -309,8 +309,7 @@
* Get the column weights
* @return the column weights
*/
- @Nullable
- public String getColumnWeights() {
+ public @Nullable String getColumnWeights() {
return mColumnWeights;
}
@@ -326,8 +325,7 @@
* Get the spans
* @return the spans
*/
- @Nullable
- public String getSpans() {
+ public @Nullable String getSpans() {
return mSpans;
}
@@ -343,8 +341,7 @@
* Get the skips
* @return the skips
*/
- @Nullable
- public String getSkips() {
+ public @Nullable String getSkips() {
return mSkips;
}
@@ -361,8 +358,7 @@
* @return the helper widget (Grid)
*/
@Override
- @NonNull
- public HelperWidget getHelperWidget() {
+ public @NonNull HelperWidget getHelperWidget() {
if (mGrid == null) {
mGrid = new GridCore();
}
diff --git a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/utils/GridCore.java b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/utils/GridCore.java
index 7424e64c..6d72d91 100644
--- a/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/utils/GridCore.java
+++ b/constraintlayout/constraintlayout-core/src/main/java/androidx/constraintlayout/core/utils/GridCore.java
@@ -18,13 +18,14 @@
import static androidx.constraintlayout.core.widgets.ConstraintWidget.DimensionBehaviour.MATCH_CONSTRAINT;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.constraintlayout.core.LinearSystem;
import androidx.constraintlayout.core.widgets.ConstraintWidget;
import androidx.constraintlayout.core.widgets.ConstraintWidgetContainer;
import androidx.constraintlayout.core.widgets.VirtualLayout;
+import org.jspecify.annotations.NonNull;
+import org.jspecify.annotations.Nullable;
+
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
@@ -181,8 +182,7 @@
*
* @return the parent ConstraintWidgetContainer
*/
- @Nullable
- public ConstraintWidgetContainer getContainer() {
+ public @Nullable ConstraintWidgetContainer getContainer() {
return mContainer;
}
@@ -278,8 +278,7 @@
*
* @return the string value of rowWeights
*/
- @Nullable
- public String getRowWeights() {
+ public @Nullable String getRowWeights() {
return mRowWeights;
}
@@ -301,8 +300,7 @@
*
* @return the string value of columnWeights
*/
- @Nullable
- public String getColumnWeights() {
+ public @Nullable String getColumnWeights() {
return mColumnWeights;
}
diff --git a/constraintlayout/constraintlayout-core/src/test/java/androidx/constraintlayout/core/AdvancedChainTest.java b/constraintlayout/constraintlayout-core/src/test/java/androidx/constraintlayout/core/AdvancedChainTest.java
index 2064aa3..cfdb016 100644
--- a/constraintlayout/constraintlayout-core/src/test/java/androidx/constraintlayout/core/AdvancedChainTest.java
+++ b/constraintlayout/constraintlayout-core/src/test/java/androidx/constraintlayout/core/AdvancedChainTest.java
@@ -28,7 +28,6 @@
import java.util.ArrayList;
-
public class AdvancedChainTest {
@Test
diff --git a/constraintlayout/constraintlayout-core/src/test/java/androidx/constraintlayout/core/ArrayLinkedVariablesTest.java b/constraintlayout/constraintlayout-core/src/test/java/androidx/constraintlayout/core/ArrayLinkedVariablesTest.java
index 991f996..adac6db 100644
--- a/constraintlayout/constraintlayout-core/src/test/java/androidx/constraintlayout/core/ArrayLinkedVariablesTest.java
+++ b/constraintlayout/constraintlayout-core/src/test/java/androidx/constraintlayout/core/ArrayLinkedVariablesTest.java
@@ -20,7 +20,6 @@
import org.junit.Test;
-
/**
* Test nested layout
*/
diff --git a/constraintlayout/constraintlayout-core/src/test/java/androidx/constraintlayout/core/BarrierTest.java b/constraintlayout/constraintlayout-core/src/test/java/androidx/constraintlayout/core/BarrierTest.java
index 64fd187..faa0e2c 100644
--- a/constraintlayout/constraintlayout-core/src/test/java/androidx/constraintlayout/core/BarrierTest.java
+++ b/constraintlayout/constraintlayout-core/src/test/java/androidx/constraintlayout/core/BarrierTest.java
@@ -27,7 +27,6 @@
import org.junit.Test;
-
/**
* Tests for Barriers
*/
diff --git a/constraintlayout/constraintlayout-core/src/test/java/androidx/constraintlayout/core/RandomLayoutTest.java b/constraintlayout/constraintlayout-core/src/test/java/androidx/constraintlayout/core/RandomLayoutTest.java
index de79560..c823105 100644
--- a/constraintlayout/constraintlayout-core/src/test/java/androidx/constraintlayout/core/RandomLayoutTest.java
+++ b/constraintlayout/constraintlayout-core/src/test/java/androidx/constraintlayout/core/RandomLayoutTest.java
@@ -28,7 +28,6 @@
import java.util.ArrayList;
import java.util.Random;
-
/**
* This test creates a random set of non overlapping rectangles uses the scout
* to add a sequence of constraints. Verify that the constraint engine will then layout the
diff --git a/constraintlayout/constraintlayout-core/src/test/java/androidx/constraintlayout/core/widgets/ChainHeadTest.java b/constraintlayout/constraintlayout-core/src/test/java/androidx/constraintlayout/core/widgets/ChainHeadTest.java
index 1c85799..bd9d2d1 100644
--- a/constraintlayout/constraintlayout-core/src/test/java/androidx/constraintlayout/core/widgets/ChainHeadTest.java
+++ b/constraintlayout/constraintlayout-core/src/test/java/androidx/constraintlayout/core/widgets/ChainHeadTest.java
@@ -24,7 +24,6 @@
import org.junit.Test;
-
public class ChainHeadTest {
@Test
diff --git a/constraintlayout/constraintlayout/build.gradle b/constraintlayout/constraintlayout/build.gradle
index c29662e..cf91f8b 100644
--- a/constraintlayout/constraintlayout/build.gradle
+++ b/constraintlayout/constraintlayout/build.gradle
@@ -29,6 +29,7 @@
}
dependencies {
+ api(libs.jspecify)
implementation("androidx.appcompat:appcompat:1.2.0")
implementation("androidx.core:core:1.3.2")
implementation(project(":constraintlayout:constraintlayout-core"))
@@ -48,6 +49,4 @@
mavenVersion = LibraryVersions.CONSTRAINTLAYOUT
inceptionYear = "2022"
description = "This library offers a flexible and adaptable way to position and animate widgets"
- // TODO: b/326456246
- optOutJSpecify = true
}
diff --git a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/helper/widget/Grid.java b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/helper/widget/Grid.java
index 5d23e12..d99fd40 100644
--- a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/helper/widget/Grid.java
+++ b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/helper/widget/Grid.java
@@ -24,12 +24,13 @@
import android.util.AttributeSet;
import android.view.View;
-import androidx.annotation.NonNull;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.ConstraintSet;
import androidx.constraintlayout.widget.R;
import androidx.constraintlayout.widget.VirtualLayout;
+import org.jspecify.annotations.NonNull;
+
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
diff --git a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/widget/MotionLayout.java b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/widget/MotionLayout.java
index ef888ce..bf3d856 100644
--- a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/widget/MotionLayout.java
+++ b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/widget/MotionLayout.java
@@ -49,8 +49,6 @@
import android.widget.TextView;
import androidx.annotation.IdRes;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.constraintlayout.core.motion.utils.KeyCache;
import androidx.constraintlayout.core.widgets.ConstraintAnchor;
import androidx.constraintlayout.core.widgets.ConstraintWidget;
@@ -68,13 +66,15 @@
import androidx.constraintlayout.widget.R;
import androidx.core.view.NestedScrollingParent3;
+import org.jspecify.annotations.NonNull;
+import org.jspecify.annotations.Nullable;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
-
/**
* A subclass of ConstraintLayout that supports animating between
* various states <b>Added in 2.0</b>
@@ -3161,7 +3161,7 @@
public void onNestedPreScroll(@NonNull View target,
int dx,
int dy,
- @NonNull int[] consumed,
+ int @NonNull [] consumed,
int type) {
MotionScene scene = mScene;
diff --git a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/widget/MotionPaths.java b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/widget/MotionPaths.java
index b695b5b..2baf01c 100644
--- a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/widget/MotionPaths.java
+++ b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/motion/widget/MotionPaths.java
@@ -21,11 +21,12 @@
import android.util.Log;
import android.view.View;
-import androidx.annotation.NonNull;
import androidx.constraintlayout.core.motion.utils.Easing;
import androidx.constraintlayout.widget.ConstraintAttribute;
import androidx.constraintlayout.widget.ConstraintSet;
+import org.jspecify.annotations.NonNull;
+
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Set;
diff --git a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/utils/widget/ImageFilterButton.java b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/utils/widget/ImageFilterButton.java
index a755550..db9c224 100644
--- a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/utils/widget/ImageFilterButton.java
+++ b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/utils/widget/ImageFilterButton.java
@@ -29,11 +29,12 @@
import android.view.View;
import android.view.ViewOutlineProvider;
-import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.constraintlayout.widget.R;
+import org.jspecify.annotations.NonNull;
+
/**
* An AppCompatImageButton that can display, combine and filter images. <b>Added in 2.0</b>
* <p>
diff --git a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/utils/widget/ImageFilterView.java b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/utils/widget/ImageFilterView.java
index 4ca975c..8890b9a 100644
--- a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/utils/widget/ImageFilterView.java
+++ b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/utils/widget/ImageFilterView.java
@@ -32,11 +32,12 @@
import android.view.ViewOutlineProvider;
import android.widget.ImageView;
-import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.constraintlayout.widget.R;
+import org.jspecify.annotations.NonNull;
+
/**
* An ImageView that can display, combine and filter images. <b>Added in 2.0</b>
* <p>
diff --git a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/utils/widget/MockView.java b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/utils/widget/MockView.java
index 389bfa7..d850868 100644
--- a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/utils/widget/MockView.java
+++ b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/utils/widget/MockView.java
@@ -26,9 +26,10 @@
import android.util.DisplayMetrics;
import android.view.View;
-import androidx.annotation.NonNull;
import androidx.constraintlayout.widget.R;
+import org.jspecify.annotations.NonNull;
+
/**
* A view that is useful for prototyping layouts. <b>Added in 2.0</b>
* <p>
diff --git a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/utils/widget/MotionButton.java b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/utils/widget/MotionButton.java
index 93d791c..4e7c7ba 100644
--- a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/utils/widget/MotionButton.java
+++ b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/utils/widget/MotionButton.java
@@ -27,10 +27,11 @@
import android.view.View;
import android.view.ViewOutlineProvider;
-import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.constraintlayout.widget.R;
+import org.jspecify.annotations.NonNull;
+
/**
* A MotionButton is an AppCompatButton that can round its edges. <b>Added in 2.0</b>
* <p>
diff --git a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/utils/widget/MotionLabel.java b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/utils/widget/MotionLabel.java
index 9d1a216..0b5765d 100644
--- a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/utils/widget/MotionLabel.java
+++ b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/utils/widget/MotionLabel.java
@@ -42,14 +42,15 @@
import android.view.View;
import android.view.ViewOutlineProvider;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.constraintlayout.motion.widget.Debug;
import androidx.constraintlayout.motion.widget.FloatLayout;
import androidx.constraintlayout.widget.R;
import androidx.core.widget.TextViewCompat;
+import org.jspecify.annotations.NonNull;
+import org.jspecify.annotations.Nullable;
+
import java.util.Objects;
/**
diff --git a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/utils/widget/MotionTelltales.java b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/utils/widget/MotionTelltales.java
index 38a2433..068fb17 100644
--- a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/utils/widget/MotionTelltales.java
+++ b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/utils/widget/MotionTelltales.java
@@ -24,10 +24,11 @@
import android.util.AttributeSet;
import android.view.ViewParent;
-import androidx.annotation.NonNull;
import androidx.constraintlayout.motion.widget.MotionLayout;
import androidx.constraintlayout.widget.R;
+import org.jspecify.annotations.NonNull;
+
/**
* A view that is useful for prototyping Views that will move in MotionLayout. <b>Added in 2.0</b>
* <p>
diff --git a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/widget/ConstraintHelper.java b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/widget/ConstraintHelper.java
index 7b1e135..c0aadaa 100644
--- a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/widget/ConstraintHelper.java
+++ b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/widget/ConstraintHelper.java
@@ -27,12 +27,13 @@
import android.view.ViewGroup;
import android.view.ViewParent;
-import androidx.annotation.NonNull;
import androidx.constraintlayout.core.widgets.ConstraintWidget;
import androidx.constraintlayout.core.widgets.ConstraintWidgetContainer;
import androidx.constraintlayout.core.widgets.Helper;
import androidx.constraintlayout.core.widgets.HelperWidget;
+import org.jspecify.annotations.NonNull;
+
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashMap;
diff --git a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/widget/ConstraintLayout.java b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/widget/ConstraintLayout.java
index 94ccc7b..163d4f5 100644
--- a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/widget/ConstraintLayout.java
+++ b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/widget/ConstraintLayout.java
@@ -41,8 +41,6 @@
import android.view.View;
import android.view.ViewGroup;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.constraintlayout.core.LinearSystem;
import androidx.constraintlayout.core.Metrics;
import androidx.constraintlayout.core.widgets.ConstraintAnchor;
@@ -52,6 +50,9 @@
import androidx.constraintlayout.core.widgets.Optimizer;
import androidx.constraintlayout.core.widgets.analyzer.BasicMeasure;
+import org.jspecify.annotations.NonNull;
+import org.jspecify.annotations.Nullable;
+
import java.util.ArrayList;
import java.util.HashMap;
diff --git a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/widget/ConstraintLayoutStatistics.java b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/widget/ConstraintLayoutStatistics.java
index a161340..c708831 100644
--- a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/widget/ConstraintLayoutStatistics.java
+++ b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/widget/ConstraintLayoutStatistics.java
@@ -22,7 +22,6 @@
import androidx.constraintlayout.core.Metrics;
import java.text.DecimalFormat;
-import java.util.ArrayList;
/**
* This provide metrics of the complexity of the layout that is being solved.
diff --git a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/widget/Guideline.java b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/widget/Guideline.java
index 577a8d7..8e41650 100644
--- a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/widget/Guideline.java
+++ b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/widget/Guideline.java
@@ -22,7 +22,7 @@
import android.util.AttributeSet;
import android.view.View;
-import androidx.annotation.NonNull;
+import org.jspecify.annotations.NonNull;
/**
* Utility class representing a Guideline helper object for
diff --git a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/widget/Placeholder.java b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/widget/Placeholder.java
index dd60e03..0ee1008 100644
--- a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/widget/Placeholder.java
+++ b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/widget/Placeholder.java
@@ -26,9 +26,10 @@
import android.util.AttributeSet;
import android.view.View;
-import androidx.annotation.NonNull;
import androidx.constraintlayout.core.widgets.ConstraintWidget;
+import org.jspecify.annotations.NonNull;
+
/**
* <b>Added in 1.1</b>
* <p>
diff --git a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/widget/ReactiveGuide.java b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/widget/ReactiveGuide.java
index 9f7bde2..fec9008 100644
--- a/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/widget/ReactiveGuide.java
+++ b/constraintlayout/constraintlayout/src/main/java/androidx/constraintlayout/widget/ReactiveGuide.java
@@ -23,9 +23,10 @@
import android.util.AttributeSet;
import android.view.View;
-import androidx.annotation.NonNull;
import androidx.constraintlayout.motion.widget.MotionLayout;
+import org.jspecify.annotations.NonNull;
+
/**
* Utility class representing a reactive Guideline helper object for {@link ConstraintLayout}.
*/
diff --git a/contentpager/contentpager/build.gradle b/contentpager/contentpager/build.gradle
index 4d04d51..b9a4313 100644
--- a/contentpager/contentpager/build.gradle
+++ b/contentpager/contentpager/build.gradle
@@ -29,6 +29,7 @@
}
dependencies {
+ api(libs.jspecify)
api("androidx.annotation:annotation:1.8.1")
api("androidx.core:core:1.1.0")
implementation("androidx.collection:collection:1.4.2")
@@ -47,8 +48,6 @@
inceptionYear = "2017"
description = "Library providing support for paging across content exposed via a ContentProvider. Use of this library allows a client to avoid expensive interprocess \"cursor window swaps\" on the UI thread."
failOnDeprecationWarnings = false
- // TODO: b/326456246
- optOutJSpecify = true
}
android {
diff --git a/contentpager/contentpager/src/androidTest/java/androidx/contentpager/content/ContentPagerTest.java b/contentpager/contentpager/src/androidTest/java/androidx/contentpager/content/ContentPagerTest.java
index 7b5f3c7..950e8eb 100644
--- a/contentpager/contentpager/src/androidTest/java/androidx/contentpager/content/ContentPagerTest.java
+++ b/contentpager/contentpager/src/androidTest/java/androidx/contentpager/content/ContentPagerTest.java
@@ -33,11 +33,11 @@
import android.os.Handler;
import android.os.Looper;
-import androidx.annotation.Nullable;
import androidx.contentpager.content.ContentPager.ContentCallback;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import org.jspecify.annotations.Nullable;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
diff --git a/contentpager/contentpager/src/androidTest/java/androidx/contentpager/content/LoaderQueryRunnerTest.java b/contentpager/contentpager/src/androidTest/java/androidx/contentpager/content/LoaderQueryRunnerTest.java
index a6eaaa8..6f4f4e0 100644
--- a/contentpager/contentpager/src/androidTest/java/androidx/contentpager/content/LoaderQueryRunnerTest.java
+++ b/contentpager/contentpager/src/androidTest/java/androidx/contentpager/content/LoaderQueryRunnerTest.java
@@ -22,12 +22,12 @@
import android.app.Activity;
import android.database.Cursor;
-import androidx.annotation.NonNull;
import androidx.contentpager.content.ContentPager.ContentCallback;
import androidx.contentpager.content.ContentPager.QueryRunner;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.MediumTest;
+import org.jspecify.annotations.NonNull;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
diff --git a/contentpager/contentpager/src/androidTest/java/androidx/contentpager/content/TestContentProvider.java b/contentpager/contentpager/src/androidTest/java/androidx/contentpager/content/TestContentProvider.java
index 885bd1e..88eeed4 100644
--- a/contentpager/contentpager/src/androidTest/java/androidx/contentpager/content/TestContentProvider.java
+++ b/contentpager/contentpager/src/androidTest/java/androidx/contentpager/content/TestContentProvider.java
@@ -30,9 +30,10 @@
import android.os.Bundle;
import android.os.CancellationSignal;
-import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import org.jspecify.annotations.Nullable;
+
/**
* A stub data paging provider used for testing of paging support.
* Ignores client supplied projections.
@@ -89,7 +90,7 @@
@Override
public Cursor query(
- Uri uri, @Nullable String[] projection, String selection, String[] selectionArgs,
+ Uri uri, String @Nullable [] projection, String selection, String[] selectionArgs,
String sortOrder) {
return query(uri, projection, null, null);
}
@@ -134,7 +135,7 @@
return value;
}
- @Nullable String argValue = uri.getQueryParameter(key);
+ String argValue = uri.getQueryParameter(key);
if (argValue != null) {
try {
return Integer.parseInt(argValue);
diff --git a/contentpager/contentpager/src/androidTest/java/androidx/contentpager/content/TestQueryCallback.java b/contentpager/contentpager/src/androidTest/java/androidx/contentpager/content/TestQueryCallback.java
index 320d463..737ae17 100644
--- a/contentpager/contentpager/src/androidTest/java/androidx/contentpager/content/TestQueryCallback.java
+++ b/contentpager/contentpager/src/androidTest/java/androidx/contentpager/content/TestQueryCallback.java
@@ -25,9 +25,10 @@
import android.net.Uri;
import android.os.Bundle;
-import androidx.annotation.Nullable;
import androidx.core.util.Pair;
+import org.jspecify.annotations.Nullable;
+
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
diff --git a/contentpager/contentpager/src/main/java/androidx/contentpager/content/ContentPager.java b/contentpager/contentpager/src/main/java/androidx/contentpager/content/ContentPager.java
index 2882853..2f5fbac 100644
--- a/contentpager/contentpager/src/main/java/androidx/contentpager/content/ContentPager.java
+++ b/contentpager/contentpager/src/main/java/androidx/contentpager/content/ContentPager.java
@@ -34,13 +34,14 @@
import androidx.annotation.GuardedBy;
import androidx.annotation.IntDef;
import androidx.annotation.MainThread;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.annotation.RequiresPermission;
import androidx.annotation.VisibleForTesting;
import androidx.annotation.WorkerThread;
import androidx.collection.LruCache;
+import org.jspecify.annotations.NonNull;
+import org.jspecify.annotations.Nullable;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.HashSet;
@@ -279,8 +280,8 @@
*/
@MainThread
public @NonNull Query query(
- @NonNull @RequiresPermission.Read Uri uri,
- @Nullable String[] projection,
+ @RequiresPermission.Read @NonNull Uri uri,
+ String @Nullable [] projection,
@NonNull Bundle queryArgs,
@Nullable CancellationSignal cancellationSignal,
@NonNull ContentCallback callback) {
diff --git a/contentpager/contentpager/src/main/java/androidx/contentpager/content/LoaderQueryRunner.java b/contentpager/contentpager/src/main/java/androidx/contentpager/content/LoaderQueryRunner.java
index 7054cfe..dbcbb05 100644
--- a/contentpager/contentpager/src/main/java/androidx/contentpager/content/LoaderQueryRunner.java
+++ b/contentpager/contentpager/src/main/java/androidx/contentpager/content/LoaderQueryRunner.java
@@ -25,7 +25,7 @@
import android.os.Bundle;
import android.util.Log;
-import androidx.annotation.NonNull;
+import org.jspecify.annotations.NonNull;
/**
* A {@link ContentPager.QueryRunner} that executes queries using a {@link LoaderManager}.
@@ -48,7 +48,7 @@
@Override
@SuppressWarnings({"unchecked", "deprecation"})
- public void query(final @NonNull Query query, @NonNull final Callback callback) {
+ public void query(final @NonNull Query query, final @NonNull Callback callback) {
if (DEBUG) Log.d(TAG, "Handling query: " + query);
android.app.LoaderManager.LoaderCallbacks<Cursor> callbacks =
diff --git a/contentpager/contentpager/src/main/java/androidx/contentpager/content/Query.java b/contentpager/contentpager/src/main/java/androidx/contentpager/content/Query.java
index a48c380..4c6d9fb 100644
--- a/contentpager/contentpager/src/main/java/androidx/contentpager/content/Query.java
+++ b/contentpager/contentpager/src/main/java/androidx/contentpager/content/Query.java
@@ -26,8 +26,8 @@
import android.os.CancellationSignal;
import android.util.Log;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
+import org.jspecify.annotations.NonNull;
+import org.jspecify.annotations.Nullable;
import java.util.Arrays;
@@ -41,7 +41,7 @@
private static final String TAG = "Query";
private final Uri mUri;
- private final @Nullable String[] mProjection;
+ private final String @Nullable [] mProjection;
private final Bundle mQueryArgs;
private final int mId;
@@ -53,10 +53,10 @@
Query(
@NonNull Uri uri,
- @Nullable String[] projection,
+ String @Nullable [] projection,
@NonNull Bundle args,
@Nullable CancellationSignal cancellationSignal,
- @NonNull ContentPager.ContentCallback callback) {
+ ContentPager.@NonNull ContentCallback callback) {
checkArgument(uri != null);
checkArgument(args != null);
@@ -107,7 +107,7 @@
return mLimit;
}
- @NonNull ContentPager.ContentCallback getCallback() {
+ ContentPager.@NonNull ContentCallback getCallback() {
return mCallback;
}
diff --git a/core/core-ktx/api/api_lint.ignore b/core/core-ktx/api/api_lint.ignore
index 3cac25a..2373a95 100644
--- a/core/core-ktx/api/api_lint.ignore
+++ b/core/core-ktx/api/api_lint.ignore
@@ -1,8 +1,4 @@
// Baseline format: 1.0
-AcronymName: androidx.core.database.sqlite.SQLiteDatabaseKt:
- Acronyms should not be capitalized in class names: was `SQLiteDatabaseKt`, should this be `SqLiteDatabaseKt`?
-
-
AutoBoxing: androidx.core.database.CursorKt#getDoubleOrNull(android.database.Cursor, int):
Must avoid boxed primitives (`java.lang.Double`)
AutoBoxing: androidx.core.database.CursorKt#getFloatOrNull(android.database.Cursor, int):
diff --git a/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/PreCallEndpointsTest.kt b/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/PreCallEndpointsTest.kt
index f1bcaff..196e174 100644
--- a/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/PreCallEndpointsTest.kt
+++ b/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/PreCallEndpointsTest.kt
@@ -112,4 +112,27 @@
val res = currentPreCallEndpoints.maybeRemoveCallEndpoint(defaultSpeaker)
assertEquals(PreCallEndpointsUpdater.STOP_TRACKING_REMOVED_ENDPOINT, res)
}
+
+ /**
+ * This test verifies that the updateClient() function returns an immutable list. It checks that
+ * attempting to modify the returned list (using reversed()) does not alter its contents.
+ */
+ @SmallTest
+ @Test
+ fun testPreCallEndpointUpdaterEmitsImmutableList() {
+ // Given: a PreCallEndpointsUpdater
+ val sendChannel = Channel<List<CallEndpointCompat>>(Channel.BUFFERED)
+ val currentPreCallEndpoints =
+ PreCallEndpointsUpdater(mutableListOf(defaultEarpiece, defaultSpeaker), sendChannel)
+ // When: an update is emitted to the client
+ val finalList = currentPreCallEndpoints.updateClient()
+ assertEquals(defaultSpeaker, finalList[0])
+ assertEquals(defaultEarpiece, finalList[1])
+ // Then: verify the list is immutable
+ finalList.reversed()
+ assertEquals(defaultSpeaker, finalList[0])
+ assertEquals(defaultEarpiece, finalList[1])
+ // cleanup
+ sendChannel.close()
+ }
}
diff --git a/core/core-telecom/src/main/java/androidx/core/telecom/internal/PreCallEndpointsUpdater.kt b/core/core-telecom/src/main/java/androidx/core/telecom/internal/PreCallEndpointsUpdater.kt
index 837d51c..b949f00 100644
--- a/core/core-telecom/src/main/java/androidx/core/telecom/internal/PreCallEndpointsUpdater.kt
+++ b/core/core-telecom/src/main/java/androidx/core/telecom/internal/PreCallEndpointsUpdater.kt
@@ -128,9 +128,7 @@
}
}
- private fun updateClient() {
- // Sort by endpoint type. The first element has the highest priority!
- mCurrentDevices.sort()
- mSendChannel.trySend(mCurrentDevices)
+ internal fun updateClient(): List<CallEndpointCompat> {
+ return mCurrentDevices.sorted().also { mSendChannel.trySend(it) }
}
}
diff --git a/core/core/api/api_lint.ignore b/core/core/api/api_lint.ignore
index c28cad6..966f30a 100644
--- a/core/core/api/api_lint.ignore
+++ b/core/core/api/api_lint.ignore
@@ -1,6 +1,4 @@
// Baseline format: 1.0
-AcronymName: androidx.core.database.sqlite.SQLiteCursorCompat:
- Acronyms should not be capitalized in class names: was `SQLiteCursorCompat`, should this be `SqLiteCursorCompat`?
AcronymName: androidx.core.graphics.ColorUtils#blendARGB(int, int, float):
Acronyms should not be capitalized in method names: was `blendARGB`, should this be `blendArgb`?
AcronymName: androidx.core.graphics.ColorUtils#blendHSL(float[], float[], float, float[]):
diff --git a/core/core/api/current.txt b/core/core/api/current.txt
index 45edc44..d8f9bf5 100644
--- a/core/core/api/current.txt
+++ b/core/core/api/current.txt
@@ -1870,6 +1870,7 @@
}
public final class BuildCompat {
+ method @ChecksSdkIntAtLeast(api=36, codename="Baklava") public static boolean isAtLeastB();
method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.N) public static boolean isAtLeastN();
method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.N_MR1) public static boolean isAtLeastNMR1();
method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.O) public static boolean isAtLeastO();
diff --git a/core/core/api/restricted_current.txt b/core/core/api/restricted_current.txt
index 46e97fe..dc7f4a4eb 100644
--- a/core/core/api/restricted_current.txt
+++ b/core/core/api/restricted_current.txt
@@ -2263,6 +2263,7 @@
}
public final class BuildCompat {
+ method @ChecksSdkIntAtLeast(api=36, codename="Baklava") public static boolean isAtLeastB();
method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.N) public static boolean isAtLeastN();
method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.N_MR1) public static boolean isAtLeastNMR1();
method @Deprecated @ChecksSdkIntAtLeast(api=android.os.Build.VERSION_CODES.O) public static boolean isAtLeastO();
diff --git a/core/core/src/androidTest/java/androidx/core/os/BuildCompatTest.java b/core/core/src/androidTest/java/androidx/core/os/BuildCompatTest.java
index 26087bd..e9764cd 100644
--- a/core/core/src/androidTest/java/androidx/core/os/BuildCompatTest.java
+++ b/core/core/src/androidTest/java/androidx/core/os/BuildCompatTest.java
@@ -51,6 +51,13 @@
assertFalse(BuildCompat.isAtLeastPreReleaseCodename("S", "REL"));
assertFalse(BuildCompat.isAtLeastPreReleaseCodename("RMR1", "REL"));
+
+ assertFalse(BuildCompat.isAtLeastPreReleaseCodename("RMR1", "REL"));
+
+ assertTrue(BuildCompat.isAtLeastPreReleaseCodename("VanillaIceCream", "VanillaIceCream"));
+ assertTrue(BuildCompat.isAtLeastPreReleaseCodename("VanillaIceCream", "Baklava"));
+ assertTrue(BuildCompat.isAtLeastPreReleaseCodename("Baklava", "Baklava"));
+ assertFalse(BuildCompat.isAtLeastPreReleaseCodename("Baklava", "VanillaIceCream"));
}
@Test
@@ -82,4 +89,10 @@
public void isAtLeastV_byMinSdk() {
assertTrue(BuildCompat.isAtLeastV());
}
+
+ @SdkSuppress(minSdkVersion = 36)
+ @Test
+ public void isAtLeastB_byMinSdk() {
+ assertTrue(BuildCompat.isAtLeastB());
+ }
}
diff --git a/core/core/src/main/java/androidx/core/os/BuildCompat.kt b/core/core/src/main/java/androidx/core/os/BuildCompat.kt
index 22937a3..0cfe5a7 100644
--- a/core/core/src/main/java/androidx/core/os/BuildCompat.kt
+++ b/core/core/src/main/java/androidx/core/os/BuildCompat.kt
@@ -40,13 +40,32 @@
@RestrictTo(RestrictTo.Scope.LIBRARY)
@VisibleForTesting
public fun isAtLeastPreReleaseCodename(codename: String, buildCodename: String): Boolean {
+ fun codenameToInt(codename: String): Int? =
+ when (codename.uppercase()) {
+ "BAKLAVA" -> 0
+ else -> null
+ }
+
// Special case "REL", which means the build is not a pre-release build.
if ("REL" == buildCodename) {
return false
}
- // Otherwise lexically compare them. Return true if the build codename is equal to or
- // greater than the requested codename.
- return buildCodename.uppercase() >= codename.uppercase()
+
+ // Starting with Baklava, the Android dessert names wrapped around to the start of the
+ // alphabet; handle these "new" codenames explicitly; lexically compare "old" codenames.
+ // Return true if the build codename is equal to or greater than the requested codename.
+ val buildCodenameInt = codenameToInt(buildCodename)
+ val codenameInt = codenameToInt(codename)
+ if (buildCodenameInt != null && codenameInt != null) {
+ // both codenames are "new" -> use hard-coded int values
+ return buildCodenameInt >= codenameInt
+ } else if (buildCodenameInt == null && codenameInt == null) {
+ // both codenames are "old" -> use lexical comparison
+ return buildCodename.uppercase() >= codename.uppercase()
+ } else {
+ // one codename is "new", one is "old"
+ return buildCodenameInt != null
+ }
}
/**
@@ -271,6 +290,22 @@
isAtLeastPreReleaseCodename("VanillaIceCream", Build.VERSION.CODENAME))
/**
+ * Checks if the device is running on a pre-release version of Android Baklava or a release
+ * version of Android Baklava or newer.
+ *
+ * **Note:** When Android Baklava is finalized for release, this method will be removed and all
+ * calls must be replaced with `Build.VERSION.SDK_INT >= 36`.
+ *
+ * @return `true` if Baklava APIs are available for use, `false` otherwise
+ */
+ @JvmStatic
+ @ChecksSdkIntAtLeast(api = 36, codename = "Baklava")
+ public fun isAtLeastB(): Boolean =
+ Build.VERSION.SDK_INT >= 36 ||
+ (Build.VERSION.SDK_INT >= 35 &&
+ isAtLeastPreReleaseCodename("Baklava", Build.VERSION.CODENAME))
+
+ /**
* Experimental feature set for pre-release SDK checks.
*
* Pre-release SDK checks **do not** guarantee correctness, as APIs may have been added or
diff --git a/credentials/credentials-e2ee/build.gradle b/credentials/credentials-e2ee/build.gradle
index 002d73b..b36889d 100644
--- a/credentials/credentials-e2ee/build.gradle
+++ b/credentials/credentials-e2ee/build.gradle
@@ -23,6 +23,7 @@
}
dependencies {
+ api(libs.jspecify)
api(libs.kotlinStdlib)
api("androidx.annotation:annotation:1.8.1")
implementation("com.google.crypto.tink:tink-android:1.8.0")
@@ -44,6 +45,4 @@
description = "Create Identity Keys, signing keys for E2EE in AOSP."
mavenVersion = LibraryVersions.CREDENTIALS_E2EE_QUARANTINE
legacyDisableKotlinStrictApiMode = true
- // TODO: b/326456246
- optOutJSpecify = true
}
diff --git a/credentials/credentials-e2ee/src/androidTest/java/androidx/credentials/e2ee/IdentityKeyJavaTest.java b/credentials/credentials-e2ee/src/androidTest/java/androidx/credentials/e2ee/IdentityKeyJavaTest.java
index 61795cb..cece85a 100644
--- a/credentials/credentials-e2ee/src/androidTest/java/androidx/credentials/e2ee/IdentityKeyJavaTest.java
+++ b/credentials/credentials-e2ee/src/androidTest/java/androidx/credentials/e2ee/IdentityKeyJavaTest.java
@@ -20,10 +20,9 @@
import android.util.Base64;
-import androidx.annotation.NonNull;
-
import com.google.common.io.BaseEncoding;
+import org.jspecify.annotations.NonNull;
import org.junit.Test;
import java.util.Random;
@@ -32,8 +31,7 @@
public class IdentityKeyJavaTest {
Random mRandom = new Random();
- @NonNull
- private byte[] randBytes(int numBytes) {
+ private byte @NonNull [] randBytes(int numBytes) {
byte[] bytes = new byte[numBytes];
mRandom.nextBytes(bytes);
return bytes;
diff --git a/credentials/credentials/build.gradle b/credentials/credentials/build.gradle
index a405ea3..450d9da 100644
--- a/credentials/credentials/build.gradle
+++ b/credentials/credentials/build.gradle
@@ -30,6 +30,7 @@
}
dependencies {
+ api(libs.jspecify)
api("androidx.annotation:annotation:1.8.1")
api("androidx.biometric:biometric:1.1.0")
api(libs.kotlinStdlib)
@@ -65,6 +66,4 @@
description = "Android Credentials Library"
legacyDisableKotlinStrictApiMode = true
samples(project(":credentials:credentials-samples"))
- // TODO: b/326456246
- optOutJSpecify = true
}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CredentialManagerJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/CredentialManagerJavaTest.java
index b9a7f1b..36472cb 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/CredentialManagerJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CredentialManagerJavaTest.java
@@ -23,7 +23,6 @@
import android.content.Context;
import android.os.Looper;
-import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.credentials.exceptions.ClearCredentialException;
import androidx.credentials.exceptions.ClearCredentialProviderConfigurationException;
@@ -39,6 +38,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import org.jspecify.annotations.NonNull;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/CredentialProviderFactoryTest.kt b/credentials/credentials/src/androidTest/java/androidx/credentials/CredentialProviderFactoryTest.kt
index 30a90a7..979a543 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/CredentialProviderFactoryTest.kt
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/CredentialProviderFactoryTest.kt
@@ -17,6 +17,7 @@
import android.os.Build
import androidx.credentials.ClearCredentialStateRequest.Companion.TYPE_CLEAR_RESTORE_CREDENTIAL
+import androidx.credentials.internal.FormFactorHelper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SdkSuppress
import androidx.test.filters.SmallTest
@@ -239,4 +240,36 @@
assertThat(credentialProviderFactory.getBestAvailableProvider(request))
.isEqualTo(expectedProvider)
}
+
+ @Test
+ fun getBestAvailableProvider_onTV_preUSuccess() {
+ if (!FormFactorHelper.isTV(context)) {
+ return
+ }
+ clearState()
+ val expectedPreUProvider = FakeProvider(success = true)
+ credentialProviderFactory.testMode = true
+ credentialProviderFactory.testPreUProvider = expectedPreUProvider
+
+ val actualProvider = credentialProviderFactory.getBestAvailableProvider()
+
+ assertThat(actualProvider).isNotNull()
+ assertThat(actualProvider).isEqualTo(expectedPreUProvider)
+ }
+
+ @Test
+ fun getBestAvailableProvider_onAuto_preUSuccess() {
+ if (!FormFactorHelper.isAuto(context)) {
+ return
+ }
+ clearState()
+ val expectedPreUProvider = FakeProvider(success = true)
+ credentialProviderFactory.testMode = true
+ credentialProviderFactory.testPreUProvider = expectedPreUProvider
+
+ val actualProvider = credentialProviderFactory.getBestAvailableProvider()
+
+ assertThat(actualProvider).isNotNull()
+ assertThat(actualProvider).isEqualTo(expectedPreUProvider)
+ }
}
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionJavaTest.java
index 7369666..90a03f8 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/GetPublicKeyCredentialOptionJavaTest.java
@@ -37,7 +37,6 @@
import java.util.Set;
-
@RunWith(AndroidJUnit4.class)
@SmallTest
public class GetPublicKeyCredentialOptionJavaTest {
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/GetRestoreCredentialOptionJavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/GetRestoreCredentialOptionJavaTest.java
index 9b6d76f..c595e38 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/GetRestoreCredentialOptionJavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/GetRestoreCredentialOptionJavaTest.java
@@ -26,7 +26,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-
@RunWith(AndroidJUnit4.class)
@SmallTest
public class GetRestoreCredentialOptionJavaTest {
diff --git a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/PendingIntentHandlerApi23JavaTest.java b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/PendingIntentHandlerApi23JavaTest.java
index 68ec0e6..fd558ba 100644
--- a/credentials/credentials/src/androidTest/java/androidx/credentials/provider/PendingIntentHandlerApi23JavaTest.java
+++ b/credentials/credentials/src/androidTest/java/androidx/credentials/provider/PendingIntentHandlerApi23JavaTest.java
@@ -38,7 +38,6 @@
import android.os.Build;
import android.os.Bundle;
-import androidx.annotation.Nullable;
import androidx.credentials.CreateCredentialRequest;
import androidx.credentials.CreateCredentialResponse;
import androidx.credentials.CreateCustomCredentialResponse;
@@ -64,6 +63,7 @@
import androidx.test.filters.SdkSuppress;
import androidx.test.filters.SmallTest;
+import org.jspecify.annotations.Nullable;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CredentialProviderFactory.kt b/credentials/credentials/src/main/java/androidx/credentials/CredentialProviderFactory.kt
index 0155a98..9d811357 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/CredentialProviderFactory.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/CredentialProviderFactory.kt
@@ -24,6 +24,7 @@
import androidx.annotation.RestrictTo
import androidx.annotation.VisibleForTesting
import androidx.credentials.ClearCredentialStateRequest.Companion.TYPE_CLEAR_RESTORE_CREDENTIAL
+import androidx.credentials.internal.FormFactorHelper
/** Factory that returns the credential provider to be used by Credential Manager. */
@OptIn(ExperimentalDigitalCredentialApi::class)
@@ -94,6 +95,10 @@
* library. Post-U, providers will be registered with the framework, and enabled by the user.
*/
fun getBestAvailableProvider(shouldFallbackToPreU: Boolean = true): CredentialProvider? {
+ if (FormFactorHelper.isTV(context) || FormFactorHelper.isAuto(context)) {
+ return tryCreateClosedSourceProviderFromManifest()
+ }
+
if (Build.VERSION.SDK_INT >= 34) { // Android U
val postUProvider = tryCreatePostUProvider()
if (postUProvider == null && shouldFallbackToPreU) {
diff --git a/credentials/credentials/src/main/java/androidx/credentials/internal/FormFactorHelper.kt b/credentials/credentials/src/main/java/androidx/credentials/internal/FormFactorHelper.kt
new file mode 100644
index 0000000..e669f38
--- /dev/null
+++ b/credentials/credentials/src/main/java/androidx/credentials/internal/FormFactorHelper.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2024 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.credentials.internal
+
+import android.content.Context
+import android.content.pm.PackageManager
+import androidx.annotation.RestrictTo
+
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+object FormFactorHelper {
+ /** Determines whether the device is a TV. */
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ @JvmStatic
+ fun isTV(ctx: Context): Boolean {
+ val pm = ctx.packageManager
+ return pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
+ }
+
+ /** Determines whether the device is a Wearable. */
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ @JvmStatic
+ fun isWear(ctx: Context): Boolean {
+ val pm = ctx.packageManager
+ return pm.hasSystemFeature(PackageManager.FEATURE_WATCH)
+ }
+
+ /** Determines whether the device is a Auto. */
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ @JvmStatic
+ fun isAuto(ctx: Context): Boolean {
+ val pm = ctx.packageManager
+ return pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
+ }
+}
diff --git a/credentials/registry/registry-provider/build.gradle b/credentials/registry/registry-provider/build.gradle
index 2cb6006..7ab03bca 100644
--- a/credentials/registry/registry-provider/build.gradle
+++ b/credentials/registry/registry-provider/build.gradle
@@ -30,6 +30,7 @@
}
dependencies {
+ api(libs.jspecify)
api(libs.kotlinStdlib)
api(project(":credentials:credentials"))
implementation(libs.kotlinCoroutinesCore)
@@ -54,6 +55,4 @@
type = LibraryType.PUBLISHED_LIBRARY
inceptionYear = "2024"
description = "register digital credentials with CredentialManager to support smooth sign-in, verification, and other user experience"
- // TODO: b/326456246
- optOutJSpecify = true
}
diff --git a/credentials/registry/registry-provider/src/androidTest/java/androidx/credentials/registry/provider/RegistryManagerJavaTest.java b/credentials/registry/registry-provider/src/androidTest/java/androidx/credentials/registry/provider/RegistryManagerJavaTest.java
index bbb97db..621e17c 100644
--- a/credentials/registry/registry-provider/src/androidTest/java/androidx/credentials/registry/provider/RegistryManagerJavaTest.java
+++ b/credentials/registry/registry-provider/src/androidTest/java/androidx/credentials/registry/provider/RegistryManagerJavaTest.java
@@ -20,12 +20,12 @@
import android.content.Context;
-import androidx.annotation.NonNull;
import androidx.credentials.CredentialManagerCallback;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import org.jspecify.annotations.NonNull;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/development/requirerelnote.py b/development/requirerelnote.py
new file mode 100755
index 0000000..2f734d3
--- /dev/null
+++ b/development/requirerelnote.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python3
+
+#
+# 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.
+#
+
+"""Script that enforces Relnote: any file path in the commit contains module substring"""
+
+import argparse
+import os.path
+import re
+import sys
+
+ERROR_RELNOTE_REQUIRED = """
+RELNOTE: is required for commits that contain changes in {}
+
+Please add a RELNOTE to the commit or RELNOTE: N/A if a release note is not applicable to the
+commit.
+
+A RELNOTE is required for all commits that changes the release artifacts.
+
+A RELNOTE can be N/A for commit messages that only effects tooling, documentation, directory
+structure, etc., but not the release artifacts.
+"""
+
+def main(args=None):
+ parser = argparse.ArgumentParser(
+ prog="requirerelnote",
+ description="Check if RELNOTE is required")
+ parser.add_argument('--file', nargs='+')
+ parser.add_argument('--module')
+ parser.add_argument('--commit')
+
+ args = parser.parse_args()
+
+ source_files = [f for f in args.file
+ if (not "buildSrc/" in f and
+ "/src/main/" in f or
+ "/src/commonMain/" in f or
+ "/src/androidMain/" in f)]
+ module_files = [f for f in source_files
+ if (args.module in f)]
+
+ if not module_files:
+ sys.exit(0)
+
+ """Following copied (with minor edits) from hooks.py:check_commit_msg_relnote_for_current_txt"""
+ """Check if the commit contain the 'Relnote:' stanza."""
+ field = 'Relnote'
+ regex = fr'^{field}: .+$'
+ check_re = re.compile(regex, re.IGNORECASE)
+
+ found = []
+ for line in args.commit.splitlines():
+ if check_re.match(line):
+ found.append(line)
+
+ if not found:
+ print(ERROR_RELNOTE_REQUIRED.format(args.module))
+ sys.exit(1)
+
+ sys.exit(0)
+
+if __name__ == '__main__':
+ main()
\ No newline at end of file
diff --git a/docs-tip-of-tree/build.gradle b/docs-tip-of-tree/build.gradle
index 5b7b2a45..7a218f8 100644
--- a/docs-tip-of-tree/build.gradle
+++ b/docs-tip-of-tree/build.gradle
@@ -249,7 +249,7 @@
docs(project(":navigation:navigation-fragment"))
docs(project(":navigation:navigation-fragment-compose"))
docs(project(":navigation:navigation-fragment-ktx"))
- docs(project(":navigation:navigation-runtime"))
+ kmpDocs(project(":navigation:navigation-runtime"))
docs(project(":navigation:navigation-runtime-ktx"))
docs(project(":navigation:navigation-testing"))
docs(project(":navigation:navigation-ui"))
diff --git a/docs/testing.md b/docs/testing.md
index b5b031f..95ae6b4 100644
--- a/docs/testing.md
+++ b/docs/testing.md
@@ -27,14 +27,13 @@
pre-release API level.
In practice, this is limited by device and emulator availability and
-reliability. As of November 2023, we run tests on the following API levels:
+reliability. As of January 2025, we run tests on the following API levels:
- API level 21: the lowest API level supported by Firebase Test Lab (FTL)
- API level 26: the lowest supported ARM-based emulator FTL runner, which has
much greater performance and stability
-- API level 28: provides coverage between 26 and 30
-- API levels 30, 31, 33: the latest supported API levels, which represent the
- majority of devices in the field
+- API levels 30, 33, 34, 35: the latest supported API levels, which represent
+ the majority of devices in the field
## Adding tests {#adding}
@@ -433,9 +432,10 @@
# Run instrumentation tests in Firebase Test Lab (remote)
./gradlew <project-name>:ftlnexus4api21
./gradlew <project-name>:ftlpixel2api26
-./gradlew <project-name>:ftlpixel2api28
./gradlew <project-name>:ftlpixel2api30
./gradlew <project-name>:ftlpixel2api33
+./gradlew <project-name>:ftlmediumphoneapi34
+./gradlew <project-name>:ftlmediumphoneapi35
# Run local unit tests
./gradlew <project-name>:test
diff --git a/graphics/graphics-core/src/androidTest/java/androidx/graphics/opengl/egl/EGLManagerTest.kt b/graphics/graphics-core/src/androidTest/java/androidx/graphics/opengl/egl/EGLManagerTest.kt
index 99d7e6f..1f9d6a8 100644
--- a/graphics/graphics-core/src/androidTest/java/androidx/graphics/opengl/egl/EGLManagerTest.kt
+++ b/graphics/graphics-core/src/androidTest/java/androidx/graphics/opengl/egl/EGLManagerTest.kt
@@ -62,7 +62,6 @@
@RunWith(AndroidJUnit4::class)
@SmallTest
-@Suppress("AcronymName")
class EGLManagerTest {
@Test
diff --git a/graphics/graphics-core/src/androidTest/java/androidx/graphics/opengl/egl/EGLTestActivity.kt b/graphics/graphics-core/src/androidTest/java/androidx/graphics/opengl/egl/EGLTestActivity.kt
index 622f973..ce80fb8 100644
--- a/graphics/graphics-core/src/androidTest/java/androidx/graphics/opengl/egl/EGLTestActivity.kt
+++ b/graphics/graphics-core/src/androidTest/java/androidx/graphics/opengl/egl/EGLTestActivity.kt
@@ -33,7 +33,6 @@
const val TAG: String = "EGLTestActivity"
-@Suppress("AcronymName")
class EGLTestActivity : Activity() {
private val mGLRenderer = GLRenderer()
diff --git a/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/GLFrontBufferedRenderer.kt b/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/GLFrontBufferedRenderer.kt
index 2fd880c..659b5a7 100644
--- a/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/GLFrontBufferedRenderer.kt
+++ b/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/GLFrontBufferedRenderer.kt
@@ -74,7 +74,6 @@
* https://developer.android.com/reference/android/hardware/HardwareBuffer
*/
@RequiresApi(Build.VERSION_CODES.Q)
-@Suppress("AcronymName")
class GLFrontBufferedRenderer<T>
@JvmOverloads
constructor(
diff --git a/graphics/graphics-core/src/main/java/androidx/graphics/opengl/GLFrameBufferRenderer.kt b/graphics/graphics-core/src/main/java/androidx/graphics/opengl/GLFrameBufferRenderer.kt
index 2a1683a..bad5d25 100644
--- a/graphics/graphics-core/src/main/java/androidx/graphics/opengl/GLFrameBufferRenderer.kt
+++ b/graphics/graphics-core/src/main/java/androidx/graphics/opengl/GLFrameBufferRenderer.kt
@@ -48,7 +48,6 @@
* buffers as well as fine grained control over synchronization of buffer content.
*/
@RequiresApi(Build.VERSION_CODES.Q)
-@Suppress("AcronymName")
class GLFrameBufferRenderer
internal constructor(
private val surfaceControlProvider: SurfaceControlProvider,
@@ -195,7 +194,6 @@
* thread
* @return The builder instance
*/
- @Suppress("AcronymName")
fun setGLRenderer(glRenderer: GLRenderer?): Builder {
mGLRenderer = glRenderer
return this
@@ -339,7 +337,7 @@
* OpenGL.
*/
val glRenderer: GLRenderer
- @Suppress("AcronymName") @JvmName("getGLRenderer") get() = mGLRenderer
+ @JvmName("getGLRenderer") get() = mGLRenderer
/**
* Returns the [SyncStrategy] used for determining when to create [SyncFenceCompat] objects in
diff --git a/graphics/graphics-core/src/main/java/androidx/graphics/opengl/GLRenderer.kt b/graphics/graphics-core/src/main/java/androidx/graphics/opengl/GLRenderer.kt
index 40ec7af..a595864 100644
--- a/graphics/graphics-core/src/main/java/androidx/graphics/opengl/GLRenderer.kt
+++ b/graphics/graphics-core/src/main/java/androidx/graphics/opengl/GLRenderer.kt
@@ -43,7 +43,6 @@
* the EGL context. This is invoked on the GL Thread
*/
// GL is the industry standard for referencing OpenGL vs Gl (lowercase l)
-@Suppress("AcronymName")
class GLRenderer(
eglSpecFactory: () -> EGLSpec = { EGLSpec.V14 },
eglConfigFactory: EGLManager.() -> EGLConfig = {
@@ -265,7 +264,6 @@
*
* These callbacks are invoked on the backing thread.
*/
- @Suppress("AcronymName")
fun registerEGLContextCallback(callback: EGLContextCallback) {
mEglContextCallback.add(callback)
mGLThread?.addEGLCallback(callback)
@@ -277,7 +275,6 @@
*
* These callbacks are invoked on the backing thread
*/
- @Suppress("AcronymName")
fun unregisterEGLContextCallback(callback: EGLContextCallback) {
mEglContextCallback.remove(callback)
mGLThread?.removeEGLCallback(callback)
@@ -288,7 +285,6 @@
* places to setup and tear down any dependencies that are used for drawing content within a
* frame (ex. compiling shaders)
*/
- @Suppress("AcronymName")
interface EGLContextCallback {
/**
@@ -297,7 +293,7 @@
* called. This will be invoked after [GLRenderer.start].
*/
// Suppressing CallbackMethodName due to b/238939160
- @Suppress("AcronymName", "CallbackMethodName")
+ @Suppress("CallbackMethodName")
@WorkerThread
fun onEGLContextCreated(eglManager: EGLManager)
@@ -306,7 +302,7 @@
* This is invoked after [GLRenderer.stop] is processed.
*/
// Suppressing CallbackMethodName due to b/238939160
- @Suppress("AcronymName", "CallbackMethodName")
+ @Suppress("CallbackMethodName")
@WorkerThread
fun onEGLContextDestroyed(eglManager: EGLManager)
}
diff --git a/graphics/graphics-core/src/main/java/androidx/graphics/opengl/egl/EGLConfigAttributes.kt b/graphics/graphics-core/src/main/java/androidx/graphics/opengl/egl/EGLConfigAttributes.kt
index d609721..86f1b5c 100644
--- a/graphics/graphics-core/src/main/java/androidx/graphics/opengl/egl/EGLConfigAttributes.kt
+++ b/graphics/graphics-core/src/main/java/androidx/graphics/opengl/egl/EGLConfigAttributes.kt
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-@file:Suppress("AcronymName")
-
package androidx.graphics.opengl.egl
import android.opengl.EGL14
@@ -89,7 +87,6 @@
* }
* ```
*/
-@Suppress("AcronymName")
class EGLConfigAttributes internal constructor(@PublishedApi internal val attrs: IntArray) {
/**
@@ -197,7 +194,6 @@
* EGL Attributes to create an 8 bit EGL config for red, green, blue, and alpha channels as
* well as an 8 bit stencil size
*/
- @Suppress("AcronymName")
@JvmField
val RGBA_8888 = EGLConfigAttributes {
EGL14.EGL_RENDERABLE_TYPE to EGL14.EGL_OPENGL_ES2_BIT
@@ -215,7 +211,6 @@
* EGL Attributes to create a 10 bit EGL config for red, green, blue, channels and a 2 bit
* alpha channels. This does not include any bits for depth and stencil buffers.
*/
- @Suppress("AcronymName")
@JvmField
val RGBA_1010102 = EGLConfigAttributes {
EGL14.EGL_RENDERABLE_TYPE to EGL14.EGL_OPENGL_ES2_BIT
@@ -232,7 +227,6 @@
* EGL Attributes to create a 16 bit floating point EGL config for red, green, blue and
* alpha channels without a depth or stencil channel.
*/
- @Suppress("AcronymName")
@JvmField
val RGBA_F16 = EGLConfigAttributes {
EGL14.EGL_RENDERABLE_TYPE to EGL14.EGL_OPENGL_ES2_BIT
diff --git a/graphics/graphics-core/src/main/java/androidx/graphics/opengl/egl/EGLManager.kt b/graphics/graphics-core/src/main/java/androidx/graphics/opengl/egl/EGLManager.kt
index 7540cf3..68afce4e 100644
--- a/graphics/graphics-core/src/main/java/androidx/graphics/opengl/egl/EGLManager.kt
+++ b/graphics/graphics-core/src/main/java/androidx/graphics/opengl/egl/EGLManager.kt
@@ -28,7 +28,6 @@
* Class responsible for configuration of EGL related resources. This includes initialization of the
* corresponding EGL Display as well as EGL Context, among other EGL related facilities.
*/
-@Suppress("AcronymName")
class EGLManager(eglSpec: EGLSpec = EGLSpec.V14) {
private var mEglConfig: EGLConfig? = null
@@ -124,27 +123,27 @@
}
val eglSpec: EGLSpec
- @Suppress("AcronymName") @JvmName("getEGLSpec") get() = mEglSpec
+ @JvmName("getEGLSpec") get() = mEglSpec
/**
* Returns the EGL version that is supported. This parameter is configured after [initialize] is
* invoked.
*/
val eglVersion: EGLVersion
- @Suppress("AcronymName") @JvmName("getEGLVersion") get() = mEglVersion
+ @JvmName("getEGLVersion") get() = mEglVersion
/**
* Returns the current EGLContext. This parameter is configured after [initialize] is invoked
*/
val eglContext: EGLContext?
- @Suppress("AcronymName") @JvmName("getEGLContext") get() = mEglContext
+ @JvmName("getEGLContext") get() = mEglContext
/**
* Returns the [EGLConfig] used to load the current [EGLContext]. This is configured after
* [createContext] is invoked.
*/
val eglConfig: EGLConfig?
- @Suppress("AcronymName") @JvmName("getEGLConfig") get() = mEglConfig
+ @JvmName("getEGLConfig") get() = mEglConfig
/**
* Determines whether the extension with the provided name is supported. The string provided is
diff --git a/graphics/graphics-core/src/main/java/androidx/graphics/opengl/egl/EGLSpec.kt b/graphics/graphics-core/src/main/java/androidx/graphics/opengl/egl/EGLSpec.kt
index a233503..fdc7e4f 100644
--- a/graphics/graphics-core/src/main/java/androidx/graphics/opengl/egl/EGLSpec.kt
+++ b/graphics/graphics-core/src/main/java/androidx/graphics/opengl/egl/EGLSpec.kt
@@ -45,7 +45,6 @@
*
* EGLSpec is not thread safe and is up to the caller of these methods to guarantee thread safety.
*/
-@Suppress("AcronymName")
interface EGLSpec {
/**
@@ -224,7 +223,6 @@
*
* See www.khronos.org/registry/EGL/extensions/ANDROID/EGL_ANDROID_get_native_client_buffer.txt
*/
- @Suppress("AcronymName")
@RequiresApi(Build.VERSION_CODES.O)
fun eglCreateImageFromHardwareBuffer(hardwareBuffer: HardwareBuffer): EGLImageKHR?
@@ -240,7 +238,7 @@
* @param image EGLImageKHR to be destroyed
* @return `true` if the destruction of the EGLImageKHR object was successful, `false` otherwise
*/
- @Suppress("AcronymName") fun eglDestroyImageKHR(image: EGLImageKHR): Boolean
+ fun eglDestroyImageKHR(image: EGLImageKHR): Boolean
/**
* Creates a sync object of the specified type associated with the specified display, and
@@ -260,7 +258,6 @@
* @return the [EGLSyncKHR] object to be used as a fence or null if this extension is not
* supported
*/
- @Suppress("AcronymName")
fun eglCreateSyncKHR(type: Int, attributes: EGLConfigAttributes?): EGLSyncKHR?
/**
@@ -280,7 +277,6 @@
* not matching the display that was used to create this sync object. Additionally if the
* queried attribute is not supported for the sync object, false is returned.
*/
- @Suppress("AcronymName")
fun eglGetSyncAttribKHR(
sync: EGLSyncKHR,
@EGLSyncAttribute attribute: Int,
@@ -301,7 +297,7 @@
* or if the display provided in this method does not match the display used to create this
* sync in [eglCreateSyncKHR].
*/
- @Suppress("AcronymName") fun eglDestroySyncKHR(sync: EGLSyncKHR): Boolean
+ fun eglDestroySyncKHR(sync: EGLSyncKHR): Boolean
/**
* Blocks the calling thread until the specified sync object is signalled or until
@@ -342,7 +338,6 @@
* [EGL_TIMEOUT_EXPIRED_KHR] if the sync did not signal within the specified timeout, or
* [EGL_FALSE] if an error occurs.
*/
- @Suppress("AcronymName")
fun eglClientWaitSyncKHR(
sync: EGLSyncKHR,
flags: Int,
@@ -550,7 +545,6 @@
* @param error Error code reported via eglGetError
* @param msg Optional message describing the exception being thrown
*/
-@Suppress("AcronymName")
class EGLException(val error: Int, val msg: String = "") : RuntimeException() {
override val message: String
@@ -563,7 +557,6 @@
* @param major Major version of the EGL implementation
* @param minor Minor version of the EGL implementation
*/
-@Suppress("AcronymName")
data class EGLVersion(val major: Int, val minor: Int) {
override fun toString(): String {
diff --git a/graphics/graphics-core/src/main/java/androidx/graphics/surface/SurfaceControlCompat.kt b/graphics/graphics-core/src/main/java/androidx/graphics/surface/SurfaceControlCompat.kt
index 182f845..0cc0d41 100644
--- a/graphics/graphics-core/src/main/java/androidx/graphics/surface/SurfaceControlCompat.kt
+++ b/graphics/graphics-core/src/main/java/androidx/graphics/surface/SurfaceControlCompat.kt
@@ -58,7 +58,6 @@
*
* Various transformations that can be applied to a buffer.
*/
- @Suppress("AcronymName")
@IntDef(
value =
[
diff --git a/graphics/graphics-core/src/main/java/androidx/opengl/EGLExt.kt b/graphics/graphics-core/src/main/java/androidx/opengl/EGLExt.kt
index 299c046..757b98b 100644
--- a/graphics/graphics-core/src/main/java/androidx/opengl/EGLExt.kt
+++ b/graphics/graphics-core/src/main/java/androidx/opengl/EGLExt.kt
@@ -28,7 +28,6 @@
import androidx.opengl.EGLExt.Companion.eglCreateSyncKHR
/** Utility class that provides some helper methods for interacting EGL Extension APIs */
-@Suppress("AcronymName")
class EGLExt private constructor() {
companion object {
@@ -191,7 +190,6 @@
/** Specifies the types of attributes that can be queried in [eglGetSyncAttribKHR] */
@RestrictTo(RestrictTo.Scope.LIBRARY)
- @Suppress("AcronymName")
@IntDef(value = [EGL_SYNC_TYPE_KHR, EGL_SYNC_STATUS_KHR, EGL_SYNC_CONDITION_KHR])
annotation class EGLSyncAttribute
@@ -246,7 +244,6 @@
/** Specifies the type of fence to create in [eglCreateSyncKHR] */
@RestrictTo(RestrictTo.Scope.LIBRARY)
- @Suppress("AcronymName")
@IntDef(value = [EGL_SYNC_FENCE_KHR, EGL_SYNC_NATIVE_FENCE_ANDROID])
annotation class EGLFenceType
@@ -290,7 +287,6 @@
/** Specifies various return values for the [eglClientWaitSyncKHR] method */
@RestrictTo(RestrictTo.Scope.LIBRARY)
@Target(AnnotationTarget.TYPE)
- @Suppress("AcronymName")
@IntDef(value = [EGL_CONDITION_SATISFIED_KHR, EGL_TIMEOUT_EXPIRED_KHR, EGL_FALSE])
annotation class EGLClientWaitResult
@@ -368,7 +364,6 @@
* @return True if the destruction of the EGLImageKHR object was successful, false otherwise
*/
@JvmStatic
- @Suppress("AcronymName")
fun eglDestroyImageKHR(eglDisplay: EGLDisplay, image: EGLImageKHR): Boolean =
EGLBindings.nDestroyImageKHR(eglDisplay.nativeHandle, image.nativeHandle)
@@ -406,7 +401,6 @@
* supported
*/
@JvmStatic
- @Suppress("AcronymName")
fun eglCreateSyncKHR(
eglDisplay: EGLDisplay,
@EGLFenceType type: Int,
@@ -441,7 +435,6 @@
* returned.
*/
@JvmStatic
- @Suppress("AcronymName")
fun eglGetSyncAttribKHR(
eglDisplay: EGLDisplay,
sync: EGLSyncKHR,
@@ -498,7 +491,6 @@
* [EGL_FALSE] if an error occurs.
*/
@JvmStatic
- @Suppress("AcronymName")
fun eglClientWaitSyncKHR(
eglDisplay: EGLDisplay,
sync: EGLSyncKHR,
@@ -557,7 +549,6 @@
* this sync in eglCreateSyncKHR.
*/
@JvmStatic
- @Suppress("AcronymName")
fun eglDestroySyncKHR(eglDisplay: EGLDisplay, eglSync: EGLSyncKHR): Boolean =
EGLBindings.nDestroySyncKHR(eglDisplay.nativeHandle, eglSync.nativeHandle)
diff --git a/graphics/graphics-core/src/main/java/androidx/opengl/EGLHandle.kt b/graphics/graphics-core/src/main/java/androidx/opengl/EGLHandle.kt
index 6c66383..8b30e32 100644
--- a/graphics/graphics-core/src/main/java/androidx/opengl/EGLHandle.kt
+++ b/graphics/graphics-core/src/main/java/androidx/opengl/EGLHandle.kt
@@ -17,7 +17,6 @@
package androidx.opengl
/** Interface used to wrap native EGL objects to create type safe objects */
-@Suppress("AcronymName")
interface EGLHandle {
/**
* Returns the native handle of the wrapped EGL object. This handle can be cast to the
diff --git a/graphics/graphics-core/src/main/java/androidx/opengl/EGLImageKHR.kt b/graphics/graphics-core/src/main/java/androidx/opengl/EGLImageKHR.kt
index 84ea358..eaea90d 100644
--- a/graphics/graphics-core/src/main/java/androidx/opengl/EGLImageKHR.kt
+++ b/graphics/graphics-core/src/main/java/androidx/opengl/EGLImageKHR.kt
@@ -21,7 +21,6 @@
* APIs). This is similar to EGL's EGLImage API except the KHR suffix indicates it is generated as
* part of the extension APIs namely through [EGLExt.eglCreateImageFromHardwareBuffer]
*/
-@Suppress("AcronymName")
class EGLImageKHR(override val nativeHandle: Long) : EGLHandle {
override fun equals(other: Any?): Boolean {
diff --git a/graphics/graphics-core/src/main/java/androidx/opengl/EGLSyncKHR.kt b/graphics/graphics-core/src/main/java/androidx/opengl/EGLSyncKHR.kt
index 3b20756..fc90ac4 100644
--- a/graphics/graphics-core/src/main/java/androidx/opengl/EGLSyncKHR.kt
+++ b/graphics/graphics-core/src/main/java/androidx/opengl/EGLSyncKHR.kt
@@ -21,7 +21,6 @@
* suffix indicates it is generated as part of the extension APIs namely through
* [EGLExt.eglCreateSyncKHR].
*/
-@Suppress("AcronymName")
class EGLSyncKHR(override val nativeHandle: Long) : EGLHandle {
override fun equals(other: Any?): Boolean {
if (this === other) return true
diff --git a/inspection/inspection/build.gradle b/inspection/inspection/build.gradle
index af4ba30..b721a23 100644
--- a/inspection/inspection/build.gradle
+++ b/inspection/inspection/build.gradle
@@ -30,6 +30,7 @@
}
dependencies {
+ api(libs.jspecify)
api("androidx.annotation:annotation:1.8.1")
androidTestImplementation(libs.kotlinStdlib)
androidTestImplementation(libs.testCore)
@@ -46,8 +47,6 @@
description = "Experimental AndroidX Inspection Project"
legacyDisableKotlinStrictApiMode = true
doNotDocumentReason = "Not shipped externally"
- // TODO: b/326456246
- optOutJSpecify = true
}
android {
diff --git a/inspection/inspection/src/main/java/androidx/inspection/ArtTooling.java b/inspection/inspection/src/main/java/androidx/inspection/ArtTooling.java
index 3727a8b..03ca297 100644
--- a/inspection/inspection/src/main/java/androidx/inspection/ArtTooling.java
+++ b/inspection/inspection/src/main/java/androidx/inspection/ArtTooling.java
@@ -16,8 +16,8 @@
package androidx.inspection;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
+import org.jspecify.annotations.NonNull;
+import org.jspecify.annotations.Nullable;
import java.util.List;
@@ -33,8 +33,7 @@
* @param clazz class whose instances should be looked up
* @return a list of instances of {@code clazz}
*/
- @NonNull
- <T> List<T> findInstances(@NonNull Class<T> clazz);
+ <T> @NonNull List<T> findInstances(@NonNull Class<T> clazz);
/**
* A callback invoked at the entry to an instrumented method.
diff --git a/inspection/inspection/src/main/java/androidx/inspection/ArtToolingImpl.java b/inspection/inspection/src/main/java/androidx/inspection/ArtToolingImpl.java
index 04a70dd..9185d2d 100644
--- a/inspection/inspection/src/main/java/androidx/inspection/ArtToolingImpl.java
+++ b/inspection/inspection/src/main/java/androidx/inspection/ArtToolingImpl.java
@@ -18,12 +18,13 @@
import android.annotation.SuppressLint;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.inspection.ArtTooling.EntryHook;
import androidx.inspection.ArtTooling.ExitHook;
+import org.jspecify.annotations.NonNull;
+import org.jspecify.annotations.Nullable;
+
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -46,8 +47,7 @@
* We don't have a way to undo bytecode manipulations, so to avoid duplicating doing the same
* transformations multiple times, this object lives forever.
*/
- @NonNull
- public static ArtToolingImpl instance() {
+ public static @NonNull ArtToolingImpl instance() {
if (sInstance == null) {
System.loadLibrary("art_tooling");
sInstance = new ArtToolingImpl(createNativeArtTooling());
@@ -84,8 +84,7 @@
/**
* Called from DefaultArtTooling
*/
- @NonNull
- public static <T> List<T> findInstances(@NonNull Class<T> clazz) {
+ public static <T> @NonNull List<T> findInstances(@NonNull Class<T> clazz) {
return Arrays.asList(nativeFindInstances(instance().mNativePtr, clazz));
}
@@ -150,8 +149,8 @@
}
/** Callback from native */
- @Nullable
- public static Object onExit(@NonNull String methodSignature, @Nullable Object returnObject) {
+ public static @Nullable Object onExit(@NonNull String methodSignature,
+ @Nullable Object returnObject) {
return onExitInternal(methodSignature, returnObject);
}
@@ -213,7 +212,7 @@
* receive the array: ["(Lcom/example/Receiver;Ljava/lang/String;)Lcom/example/Client;", this,
* r, message]
*/
- public static void onEntry(@NonNull Object[] signatureThisParams) {
+ public static void onEntry(Object @NonNull [] signatureThisParams) {
// Should always at least contain signature and "this"
assert (signatureThisParams.length >= 2);
String signature = (String) signatureThisParams[0];
diff --git a/inspection/inspection/src/main/java/androidx/inspection/Connection.java b/inspection/inspection/src/main/java/androidx/inspection/Connection.java
index fd0c3f2..cd309ee 100644
--- a/inspection/inspection/src/main/java/androidx/inspection/Connection.java
+++ b/inspection/inspection/src/main/java/androidx/inspection/Connection.java
@@ -16,7 +16,7 @@
package androidx.inspection;
-import androidx.annotation.NonNull;
+import org.jspecify.annotations.NonNull;
/**
* A class representing a connection between studio and inspectors.
@@ -28,6 +28,6 @@
*
* @param data An array of bytes. Up to inspectors to determine how to encode bytes.
*/
- public void sendEvent(@NonNull byte[] data) {
+ public void sendEvent(byte @NonNull [] data) {
}
}
diff --git a/inspection/inspection/src/main/java/androidx/inspection/DefaultArtTooling.java b/inspection/inspection/src/main/java/androidx/inspection/DefaultArtTooling.java
index fcceef9..6a67de7 100644
--- a/inspection/inspection/src/main/java/androidx/inspection/DefaultArtTooling.java
+++ b/inspection/inspection/src/main/java/androidx/inspection/DefaultArtTooling.java
@@ -16,9 +16,10 @@
package androidx.inspection;
-import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
+import org.jspecify.annotations.NonNull;
+
import java.util.List;
/**
@@ -32,9 +33,8 @@
mInspectorId = inspectorId;
}
- @NonNull
@Override
- public <T> List<T> findInstances(@NonNull Class<T> clazz) {
+ public <T> @NonNull List<T> findInstances(@NonNull Class<T> clazz) {
return ArtToolingImpl.findInstances(clazz);
}
diff --git a/inspection/inspection/src/main/java/androidx/inspection/Inspector.java b/inspection/inspection/src/main/java/androidx/inspection/Inspector.java
index f512df2..fe0506d 100644
--- a/inspection/inspection/src/main/java/androidx/inspection/Inspector.java
+++ b/inspection/inspection/src/main/java/androidx/inspection/Inspector.java
@@ -18,7 +18,7 @@
import android.annotation.SuppressLint;
-import androidx.annotation.NonNull;
+import org.jspecify.annotations.NonNull;
import java.util.concurrent.Executor;
@@ -30,8 +30,7 @@
*/
public abstract class Inspector {
- @NonNull
- private Connection mConnection;
+ private @NonNull Connection mConnection;
/**
* @param connection a connection object that allows to send events to studio
@@ -57,13 +56,12 @@
* @param data a raw byte array of the command sent by studio.
* @param callback a callback to reply on the given command.
*/
- public abstract void onReceiveCommand(@NonNull byte[] data, @NonNull CommandCallback callback);
+ public abstract void onReceiveCommand(byte @NonNull [] data, @NonNull CommandCallback callback);
/**
* Returns a connection that allows to send events to Studio.
*/
- @NonNull
- protected final Connection getConnection() {
+ protected final @NonNull Connection getConnection() {
return mConnection;
}
@@ -78,7 +76,7 @@
*/
// Users don't implement this callback, but call methods on it themselves
@SuppressLint("CallbackMethodName")
- void reply(@NonNull byte[] response);
+ void reply(byte @NonNull [] response);
/**
* Handles a signal sent from Studio that this command should be cancelled, if possible.
diff --git a/inspection/inspection/src/main/java/androidx/inspection/InspectorEnvironment.java b/inspection/inspection/src/main/java/androidx/inspection/InspectorEnvironment.java
index c8b22eb..e70c992 100644
--- a/inspection/inspection/src/main/java/androidx/inspection/InspectorEnvironment.java
+++ b/inspection/inspection/src/main/java/androidx/inspection/InspectorEnvironment.java
@@ -16,7 +16,7 @@
package androidx.inspection;
-import androidx.annotation.NonNull;
+import org.jspecify.annotations.NonNull;
/**
* This interface provides inspector specific utilities, such as
@@ -28,14 +28,12 @@
* Executors provided by App Inspection Platforms. Clients should use it instead of
* creating their own.
*/
- @NonNull
- default InspectorExecutors executors() {
+ default @NonNull InspectorExecutors executors() {
throw new UnsupportedOperationException();
}
/**
* Interface that provides ART TI capabilities.
*/
- @NonNull
- ArtTooling artTooling();
+ @NonNull ArtTooling artTooling();
}
diff --git a/inspection/inspection/src/main/java/androidx/inspection/InspectorExecutors.java b/inspection/inspection/src/main/java/androidx/inspection/InspectorExecutors.java
index 4bb081e..09ebfa5 100644
--- a/inspection/inspection/src/main/java/androidx/inspection/InspectorExecutors.java
+++ b/inspection/inspection/src/main/java/androidx/inspection/InspectorExecutors.java
@@ -18,7 +18,7 @@
import android.os.Handler;
-import androidx.annotation.NonNull;
+import org.jspecify.annotations.NonNull;
import java.util.concurrent.Executor;
@@ -57,8 +57,7 @@
* Even when a future wasn't dropped or lost, developers would still need to block one
* of the threads.
*/
- @NonNull
- Handler handler();
+ @NonNull Handler handler();
/**
* Primary single threaded executor for the given inspector.
@@ -70,12 +69,10 @@
* It is important to keep this executor responsive, so it can quickly process incoming
* messages.
*/
- @NonNull
- Executor primary();
+ @NonNull Executor primary();
/**
* An executor for offloading blocking IO tasks to a shared pool of threads.
*/
- @NonNull
- Executor io();
+ @NonNull Executor io();
}
diff --git a/inspection/inspection/src/main/java/androidx/inspection/InspectorFactory.java b/inspection/inspection/src/main/java/androidx/inspection/InspectorFactory.java
index 2f4c253..cbf8399 100644
--- a/inspection/inspection/src/main/java/androidx/inspection/InspectorFactory.java
+++ b/inspection/inspection/src/main/java/androidx/inspection/InspectorFactory.java
@@ -16,7 +16,7 @@
package androidx.inspection;
-import androidx.annotation.NonNull;
+import org.jspecify.annotations.NonNull;
/**
* A factory that is responsible for creation of an inspector for your library.
@@ -42,8 +42,7 @@
/**
* @return an id of an inspector that is served by this factory.
*/
- @NonNull
- public final String getInspectorId() {
+ public final @NonNull String getInspectorId() {
return mInspectorId;
}
@@ -54,7 +53,6 @@
* @param environment an environment that provides tooling utilities.
* @return a new instance of an inspector.
*/
- @NonNull
- public abstract T createInspector(@NonNull Connection connection,
+ public abstract @NonNull T createInspector(@NonNull Connection connection,
@NonNull InspectorEnvironment environment);
}
diff --git a/libraryversions.toml b/libraryversions.toml
index 7e33193..3e4aed9 100644
--- a/libraryversions.toml
+++ b/libraryversions.toml
@@ -1,5 +1,5 @@
[versions]
-ACTIVITY = "1.10.0-rc01"
+ACTIVITY = "1.11.0-alpha01"
ANNOTATION = "1.9.0-rc01"
ANNOTATION_EXPERIMENTAL = "1.5.0-alpha01"
APPCOMPAT = "1.8.0-alpha01"
@@ -8,7 +8,7 @@
ARCH_CORE = "2.3.0-alpha01"
ASYNCLAYOUTINFLATER = "1.1.0-alpha02"
AUTOFILL = "1.3.0-rc01"
-BENCHMARK = "1.4.0-alpha06"
+BENCHMARK = "1.4.0-alpha07"
BIOMETRIC = "1.4.0-alpha02"
BLUETOOTH = "1.0.0-alpha02"
BROWSER = "1.9.0-alpha01"
@@ -22,8 +22,8 @@
CAR_APP = "1.8.0-alpha01"
COLLECTION = "1.5.0-beta02"
COMPOSE = "1.8.0-alpha08"
-COMPOSE_MATERIAL3 = "1.4.0-alpha06"
-COMPOSE_MATERIAL3_ADAPTIVE = "1.1.0-alpha09"
+COMPOSE_MATERIAL3 = "1.4.0-alpha07"
+COMPOSE_MATERIAL3_ADAPTIVE = "1.1.0-beta01"
COMPOSE_MATERIAL3_COMMON = "1.0.0-alpha01"
COMPOSE_MATERIAL3_XR = "1.0.0-alpha02"
COMPOSE_RUNTIME = "1.8.0-beta01"
@@ -95,14 +95,14 @@
LEANBACK_TAB = "1.1.0-beta01"
LEGACY = "1.1.0-alpha01"
LIBYUV = "0.1.0-dev01"
-LIFECYCLE = "2.9.0-alpha08"
+LIFECYCLE = "2.9.0-alpha09"
LIFECYCLE_EXTENSIONS = "2.2.0"
LINT = "1.0.0-alpha03"
LOADER = "1.2.0-alpha01"
MEDIA = "1.8.0-alpha01"
MEDIAROUTER = "1.8.0-alpha02"
METRICS = "1.0.0-beta02"
-NAVIGATION = "2.9.0-alpha04"
+NAVIGATION = "2.9.0-alpha05"
NAVIGATION3 = "0.1.0-dev01"
PAGING = "3.4.0-alpha01"
PALETTE = "1.1.0-alpha01"
@@ -125,14 +125,14 @@
RESOURCEINSPECTION = "1.1.0-alpha01"
ROOM = "2.7.0-alpha12"
SAFEPARCEL = "1.0.0-alpha01"
-SAVEDSTATE = "1.3.0-alpha06"
+SAVEDSTATE = "1.3.0-alpha07"
SECURITY = "1.1.0-alpha07"
SECURITY_APP_AUTHENTICATOR = "1.0.0-rc01"
SECURITY_APP_AUTHENTICATOR_TESTING = "1.0.0-rc01"
SECURITY_BIOMETRIC = "1.0.0-alpha01"
SECURITY_IDENTITY_CREDENTIAL = "1.0.0-alpha04"
SECURITY_MLS = "1.0.0-alpha01"
-SECURITY_STATE = "1.0.0-alpha04"
+SECURITY_STATE = "1.0.0-alpha05"
SECURITY_STATE_PROVIDER = "1.0.0-alpha01"
SHARETARGET = "1.3.0-alpha01"
SLICE = "1.1.0-alpha03"
@@ -174,7 +174,7 @@
WEAR_TILES = "1.5.0-alpha07"
WEAR_TOOLING_PREVIEW = "1.0.0-rc01"
WEAR_WATCHFACE = "1.3.0-alpha05"
-WEBKIT = "1.13.0-alpha03"
+WEBKIT = "1.13.0-beta01"
# Adding a comment to prevent merge conflicts for Window artifact
WINDOW = "1.4.0-beta01"
WINDOW_EXTENSIONS = "1.4.0-rc01"
diff --git a/lifecycle/lifecycle-viewmodel-compose/api/current.txt b/lifecycle/lifecycle-viewmodel-compose/api/current.txt
index ace8748..8372225 100644
--- a/lifecycle/lifecycle-viewmodel-compose/api/current.txt
+++ b/lifecycle/lifecycle-viewmodel-compose/api/current.txt
@@ -29,3 +29,12 @@
}
+package androidx.lifecycle.viewmodel.compose.serialization.serializers {
+
+ public final class MutableStateSerializerKt {
+ method public static inline <reified T> kotlinx.serialization.KSerializer<androidx.compose.runtime.MutableState<T>> MutableStateSerializer();
+ method public static <T> kotlinx.serialization.KSerializer<androidx.compose.runtime.MutableState<T>> MutableStateSerializer(kotlinx.serialization.KSerializer<T> serializer);
+ }
+
+}
+
diff --git a/lifecycle/lifecycle-viewmodel-compose/api/restricted_current.txt b/lifecycle/lifecycle-viewmodel-compose/api/restricted_current.txt
index ace8748..8372225 100644
--- a/lifecycle/lifecycle-viewmodel-compose/api/restricted_current.txt
+++ b/lifecycle/lifecycle-viewmodel-compose/api/restricted_current.txt
@@ -29,3 +29,12 @@
}
+package androidx.lifecycle.viewmodel.compose.serialization.serializers {
+
+ public final class MutableStateSerializerKt {
+ method public static inline <reified T> kotlinx.serialization.KSerializer<androidx.compose.runtime.MutableState<T>> MutableStateSerializer();
+ method public static <T> kotlinx.serialization.KSerializer<androidx.compose.runtime.MutableState<T>> MutableStateSerializer(kotlinx.serialization.KSerializer<T> serializer);
+ }
+
+}
+
diff --git a/lifecycle/lifecycle-viewmodel-compose/build.gradle b/lifecycle/lifecycle-viewmodel-compose/build.gradle
index c63ee18..6b1230c 100644
--- a/lifecycle/lifecycle-viewmodel-compose/build.gradle
+++ b/lifecycle/lifecycle-viewmodel-compose/build.gradle
@@ -30,6 +30,7 @@
id("AndroidXPlugin")
id("com.android.library")
id("AndroidXComposePlugin")
+ alias(libs.plugins.kotlinSerialization)
}
androidXMultiplatform {
@@ -45,12 +46,17 @@
api(project(":lifecycle:lifecycle-viewmodel"))
api("androidx.annotation:annotation:1.8.1")
api("androidx.compose.runtime:runtime:1.6.0")
+ api(libs.kotlinSerializationCore)
implementation(libs.kotlinStdlib)
}
}
commonTest {
- // TODO(b/330323282): Move common dependencies here.
+ dependencies {
+ implementation(project(":lifecycle:lifecycle-viewmodel-savedstate"))
+ implementation(project(":lifecycle:lifecycle-viewmodel-testing"))
+ implementation(project(":lifecycle:lifecycle-runtime-testing"))
+ }
}
androidMain {
@@ -83,9 +89,7 @@
// but it doesn't work in androidx.
// See aosp/1804059
implementation(project(":lifecycle:lifecycle-common-java8"))
- implementation(project(":lifecycle:lifecycle-viewmodel-savedstate"))
implementation(project(":activity:activity-compose"))
- implementation(project(":lifecycle:lifecycle-runtime-testing"))
}
}
}
diff --git a/lifecycle/lifecycle-viewmodel-compose/src/androidInstrumentedTest/kotlin/androidx/lifecycle/viewmodel/compose/serialization/serializers/MutableStateSerializerTest.android.kt b/lifecycle/lifecycle-viewmodel-compose/src/androidInstrumentedTest/kotlin/androidx/lifecycle/viewmodel/compose/serialization/serializers/MutableStateSerializerTest.android.kt
new file mode 100644
index 0000000..0cbeb73
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel-compose/src/androidInstrumentedTest/kotlin/androidx/lifecycle/viewmodel/compose/serialization/serializers/MutableStateSerializerTest.android.kt
@@ -0,0 +1,63 @@
+/*
+ * 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.lifecycle.viewmodel.compose.serialization.serializers
+
+import androidx.compose.runtime.mutableStateOf
+import androidx.savedstate.serialization.decodeFromSavedState
+import androidx.savedstate.serialization.encodeToSavedState
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import kotlinx.serialization.InternalSerializationApi
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.serializer
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class MutableStateSerializerTest {
+
+ @Test
+ fun encodeDecode_withImplicitSerializer() {
+ val state = mutableStateOf(USER_JOHN_DOE)
+ val serializer = MutableStateSerializer<User>()
+
+ val encoded = encodeToSavedState(serializer, state)
+ val decoded = decodeFromSavedState(serializer, encoded)
+
+ assertThat(state.value).isEqualTo(decoded.value)
+ }
+
+ @Test
+ fun encodeDecode_withExplicitSerializer() {
+ val state = mutableStateOf(USER_JOHN_DOE)
+ val serializer = MutableStateSerializer(USER_SERIALIZER)
+
+ val encoded = encodeToSavedState(serializer, state)
+ val decoded = decodeFromSavedState(serializer, encoded)
+
+ assertThat(state.value).isEqualTo(decoded.value)
+ }
+
+ companion object {
+ val USER_JOHN_DOE = User(name = "John", surname = "Doe")
+ @OptIn(InternalSerializationApi::class) val USER_SERIALIZER = User::class.serializer()
+ }
+
+ @Serializable data class User(val name: String = "John", val surname: String = "Doe")
+}
diff --git a/lifecycle/lifecycle-viewmodel-compose/src/commonMain/kotlin/androidx/lifecycle/viewmodel/compose/serialization/serializers/MutableStateSerializer.kt b/lifecycle/lifecycle-viewmodel-compose/src/commonMain/kotlin/androidx/lifecycle/viewmodel/compose/serialization/serializers/MutableStateSerializer.kt
new file mode 100644
index 0000000..1bd43bd
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel-compose/src/commonMain/kotlin/androidx/lifecycle/viewmodel/compose/serialization/serializers/MutableStateSerializer.kt
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+@file:OptIn(InternalSerializationApi::class, ExperimentalTypeInference::class)
+
+package androidx.lifecycle.viewmodel.compose.serialization.serializers
+
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.mutableStateOf
+import kotlin.experimental.ExperimentalTypeInference
+import kotlinx.serialization.InternalSerializationApi
+import kotlinx.serialization.KSerializer
+import kotlinx.serialization.descriptors.SerialDescriptor
+import kotlinx.serialization.encoding.Decoder
+import kotlinx.serialization.encoding.Encoder
+import kotlinx.serialization.serializer
+
+/**
+ * Creates a [KSerializer] for a [MutableState] containing a [Serializable] value of type [T].
+ *
+ * This inline function infers the state type [T] automatically and retrieves the appropriate
+ * [KSerializer] for serialization and deserialization of [MutableState].
+ *
+ * @param T The type of the value stored in the [MutableState].
+ * @return A [KSerializer] for handling [MutableState] containing a [Serializable] type [T].
+ */
+@Suppress("FunctionName")
+public inline fun <reified T> MutableStateSerializer(): KSerializer<MutableState<T>> {
+ return MutableStateSerializer(serializer())
+}
+
+/**
+ * Creates a [KSerializer] for a [MutableState] containing a [Serializable] value of type [T].
+ *
+ * This function allows for explicit specification of the [KSerializer] for the state type [T]. It
+ * provides serialization and deserialization capabilities for [MutableState] objects.
+ *
+ * @param T The type of the value stored in the [MutableState].
+ * @param serializer The [KSerializer] for the [Serializable] type [T].
+ * @return A [KSerializer] for handling [MutableState] containing a [Serializable] type [T].
+ */
+@Suppress("FunctionName")
+public fun <T> MutableStateSerializer(serializer: KSerializer<T>): KSerializer<MutableState<T>> {
+ return MutableStateSerializerImpl<T>(serializer)
+}
+
+/**
+ * Internal implementation of [KSerializer] for [MutableState].
+ *
+ * This private class wraps a [KSerializer] for the inner value type [T], enabling serialization and
+ * deserialization of [MutableState] instances. The inner value serialization is delegated to the
+ * provided [valueSerializer].
+ *
+ * @param T The type of the value stored in the [MutableState].
+ * @property valueSerializer The [KSerializer] used to serialize and deserialize the inner value.
+ */
+private class MutableStateSerializerImpl<T>(
+ private val valueSerializer: KSerializer<T>,
+) : KSerializer<MutableState<T>> {
+
+ override val descriptor: SerialDescriptor = valueSerializer.descriptor
+
+ override fun serialize(encoder: Encoder, value: MutableState<T>) {
+ valueSerializer.serialize(encoder, value.value)
+ }
+
+ override fun deserialize(decoder: Decoder): MutableState<T> {
+ return mutableStateOf(valueSerializer.deserialize(decoder))
+ }
+}
diff --git a/lifecycle/lifecycle-viewmodel-navigation3/src/androidInstrumentedTest/kotlin/androidx/lifecycle/viewmodel/navigation3/ViewModelStoreNavContentWrapperTest.kt b/lifecycle/lifecycle-viewmodel-navigation3/src/androidInstrumentedTest/kotlin/androidx/lifecycle/viewmodel/navigation3/ViewModelStoreNavLocalProviderTest.kt
similarity index 100%
rename from lifecycle/lifecycle-viewmodel-navigation3/src/androidInstrumentedTest/kotlin/androidx/lifecycle/viewmodel/navigation3/ViewModelStoreNavContentWrapperTest.kt
rename to lifecycle/lifecycle-viewmodel-navigation3/src/androidInstrumentedTest/kotlin/androidx/lifecycle/viewmodel/navigation3/ViewModelStoreNavLocalProviderTest.kt
diff --git a/lifecycle/lifecycle-viewmodel-navigation3/src/androidMain/kotlin/androidx/lifecycle/viewmodel/navigation3/ViewModelStoreNavContentWrapper.android.kt b/lifecycle/lifecycle-viewmodel-navigation3/src/androidMain/kotlin/androidx/lifecycle/viewmodel/navigation3/ViewModelStoreNavLocalProvider.android.kt
similarity index 100%
rename from lifecycle/lifecycle-viewmodel-navigation3/src/androidMain/kotlin/androidx/lifecycle/viewmodel/navigation3/ViewModelStoreNavContentWrapper.android.kt
rename to lifecycle/lifecycle-viewmodel-navigation3/src/androidMain/kotlin/androidx/lifecycle/viewmodel/navigation3/ViewModelStoreNavLocalProvider.android.kt
diff --git a/lifecycle/lifecycle-viewmodel-savedstate/api/current.txt b/lifecycle/lifecycle-viewmodel-savedstate/api/current.txt
index 74041ee..e9b8ced 100644
--- a/lifecycle/lifecycle-viewmodel-savedstate/api/current.txt
+++ b/lifecycle/lifecycle-viewmodel-savedstate/api/current.txt
@@ -50,10 +50,8 @@
package androidx.lifecycle.serialization {
public final class SavedStateHandleDelegatesKt {
- method public static inline <reified T> kotlin.properties.ReadWriteProperty<java.lang.Object?,T> saved(androidx.lifecycle.SavedStateHandle, String key, kotlin.jvm.functions.Function0<? extends T> init);
- method public static <T> kotlin.properties.ReadWriteProperty<java.lang.Object?,T> saved(androidx.lifecycle.SavedStateHandle, String key, kotlinx.serialization.KSerializer<T> serializer, kotlin.jvm.functions.Function0<? extends T> init);
- method public static inline <reified T> kotlin.properties.ReadWriteProperty<java.lang.Object?,T> saved(androidx.lifecycle.SavedStateHandle, kotlin.jvm.functions.Function0<? extends T> init);
- method public static <T> kotlin.properties.ReadWriteProperty<java.lang.Object?,T> saved(androidx.lifecycle.SavedStateHandle, kotlinx.serialization.KSerializer<T> serializer, kotlin.jvm.functions.Function0<? extends T> init);
+ method public static inline <reified T> kotlin.properties.ReadWriteProperty<java.lang.Object?,T> saved(androidx.lifecycle.SavedStateHandle, optional String? key, kotlin.jvm.functions.Function0<? extends T> init);
+ method public static <T> kotlin.properties.ReadWriteProperty<java.lang.Object?,T> saved(androidx.lifecycle.SavedStateHandle, kotlinx.serialization.KSerializer<T> serializer, optional String? key, kotlin.jvm.functions.Function0<? extends T> init);
}
}
diff --git a/lifecycle/lifecycle-viewmodel-savedstate/api/restricted_current.txt b/lifecycle/lifecycle-viewmodel-savedstate/api/restricted_current.txt
index 74041ee..e9b8ced 100644
--- a/lifecycle/lifecycle-viewmodel-savedstate/api/restricted_current.txt
+++ b/lifecycle/lifecycle-viewmodel-savedstate/api/restricted_current.txt
@@ -50,10 +50,8 @@
package androidx.lifecycle.serialization {
public final class SavedStateHandleDelegatesKt {
- method public static inline <reified T> kotlin.properties.ReadWriteProperty<java.lang.Object?,T> saved(androidx.lifecycle.SavedStateHandle, String key, kotlin.jvm.functions.Function0<? extends T> init);
- method public static <T> kotlin.properties.ReadWriteProperty<java.lang.Object?,T> saved(androidx.lifecycle.SavedStateHandle, String key, kotlinx.serialization.KSerializer<T> serializer, kotlin.jvm.functions.Function0<? extends T> init);
- method public static inline <reified T> kotlin.properties.ReadWriteProperty<java.lang.Object?,T> saved(androidx.lifecycle.SavedStateHandle, kotlin.jvm.functions.Function0<? extends T> init);
- method public static <T> kotlin.properties.ReadWriteProperty<java.lang.Object?,T> saved(androidx.lifecycle.SavedStateHandle, kotlinx.serialization.KSerializer<T> serializer, kotlin.jvm.functions.Function0<? extends T> init);
+ method public static inline <reified T> kotlin.properties.ReadWriteProperty<java.lang.Object?,T> saved(androidx.lifecycle.SavedStateHandle, optional String? key, kotlin.jvm.functions.Function0<? extends T> init);
+ method public static <T> kotlin.properties.ReadWriteProperty<java.lang.Object?,T> saved(androidx.lifecycle.SavedStateHandle, kotlinx.serialization.KSerializer<T> serializer, optional String? key, kotlin.jvm.functions.Function0<? extends T> init);
}
}
diff --git a/lifecycle/lifecycle-viewmodel-savedstate/bcv/native/current.txt b/lifecycle/lifecycle-viewmodel-savedstate/bcv/native/current.txt
index 7a78cde..3b9bf81 100644
--- a/lifecycle/lifecycle-viewmodel-savedstate/bcv/native/current.txt
+++ b/lifecycle/lifecycle-viewmodel-savedstate/bcv/native/current.txt
@@ -42,7 +42,5 @@
final fun (androidx.lifecycle.viewmodel/CreationExtras).androidx.lifecycle/createSavedStateHandle(): androidx.lifecycle/SavedStateHandle // androidx.lifecycle/createSavedStateHandle|createSavedStateHandle@androidx.lifecycle.viewmodel.CreationExtras(){}[0]
final fun <#A: androidx.lifecycle/ViewModelStoreOwner & androidx.savedstate/SavedStateRegistryOwner> (#A).androidx.lifecycle/enableSavedStateHandles() // androidx.lifecycle/enableSavedStateHandles|enableSavedStateHandles@0:0(){0§<androidx.savedstate.SavedStateRegistryOwner&androidx.lifecycle.ViewModelStoreOwner>}[0]
-final fun <#A: kotlin/Any> (androidx.lifecycle/SavedStateHandle).androidx.lifecycle.serialization/saved(kotlin/String, kotlinx.serialization/KSerializer<#A>, kotlin/Function0<#A>): kotlin.properties/ReadWriteProperty<kotlin/Any?, #A> // androidx.lifecycle.serialization/saved|saved@androidx.lifecycle.SavedStateHandle(kotlin.String;kotlinx.serialization.KSerializer<0:0>;kotlin.Function0<0:0>){0§<kotlin.Any>}[0]
-final fun <#A: kotlin/Any> (androidx.lifecycle/SavedStateHandle).androidx.lifecycle.serialization/saved(kotlinx.serialization/KSerializer<#A>, kotlin/Function0<#A>): kotlin.properties/ReadWriteProperty<kotlin/Any?, #A> // androidx.lifecycle.serialization/saved|saved@androidx.lifecycle.SavedStateHandle(kotlinx.serialization.KSerializer<0:0>;kotlin.Function0<0:0>){0§<kotlin.Any>}[0]
-final inline fun <#A: reified kotlin/Any> (androidx.lifecycle/SavedStateHandle).androidx.lifecycle.serialization/saved(kotlin/String, noinline kotlin/Function0<#A>): kotlin.properties/ReadWriteProperty<kotlin/Any?, #A> // androidx.lifecycle.serialization/saved|saved@androidx.lifecycle.SavedStateHandle(kotlin.String;kotlin.Function0<0:0>){0§<kotlin.Any>}[0]
-final inline fun <#A: reified kotlin/Any> (androidx.lifecycle/SavedStateHandle).androidx.lifecycle.serialization/saved(noinline kotlin/Function0<#A>): kotlin.properties/ReadWriteProperty<kotlin/Any?, #A> // androidx.lifecycle.serialization/saved|saved@androidx.lifecycle.SavedStateHandle(kotlin.Function0<0:0>){0§<kotlin.Any>}[0]
+final fun <#A: kotlin/Any> (androidx.lifecycle/SavedStateHandle).androidx.lifecycle.serialization/saved(kotlinx.serialization/KSerializer<#A>, kotlin/String? = ..., kotlin/Function0<#A>): kotlin.properties/ReadWriteProperty<kotlin/Any?, #A> // androidx.lifecycle.serialization/saved|saved@androidx.lifecycle.SavedStateHandle(kotlinx.serialization.KSerializer<0:0>;kotlin.String?;kotlin.Function0<0:0>){0§<kotlin.Any>}[0]
+final inline fun <#A: reified kotlin/Any> (androidx.lifecycle/SavedStateHandle).androidx.lifecycle.serialization/saved(kotlin/String? = ..., noinline kotlin/Function0<#A>): kotlin.properties/ReadWriteProperty<kotlin/Any?, #A> // androidx.lifecycle.serialization/saved|saved@androidx.lifecycle.SavedStateHandle(kotlin.String?;kotlin.Function0<0:0>){0§<kotlin.Any>}[0]
diff --git a/lifecycle/lifecycle-viewmodel-savedstate/src/commonMain/kotlin/androidx/lifecycle/serialization/SavedStateHandleDelegates.kt b/lifecycle/lifecycle-viewmodel-savedstate/src/commonMain/kotlin/androidx/lifecycle/serialization/SavedStateHandleDelegates.kt
index a964d34..edd1bc10 100644
--- a/lifecycle/lifecycle-viewmodel-savedstate/src/commonMain/kotlin/androidx/lifecycle/serialization/SavedStateHandleDelegates.kt
+++ b/lifecycle/lifecycle-viewmodel-savedstate/src/commonMain/kotlin/androidx/lifecycle/serialization/SavedStateHandleDelegates.kt
@@ -27,67 +27,34 @@
/**
* Returns a property delegate that uses [SavedStateHandle] to save and restore a value of type [T]
- * with fully qualified property or variable name as key and the default serializer.
- *
- * @sample androidx.lifecycle.delegate
- * @param init The function to provide the initial value of the property.
- * @return A property delegate that manages the saving and restoring of the value.
- */
-public inline fun <reified T : Any> SavedStateHandle.saved(
- noinline init: () -> T,
-): ReadWriteProperty<Any?, T> {
- return saved(serializer(), init)
-}
-
-/**
- * Returns a property delegate that uses [SavedStateHandle] to save and restore a value of type [T]
* with the default serializer.
*
* @sample androidx.lifecycle.delegateExplicitKey
- * @param key The [String] key to use for storing the value in the [SavedStateHandle].
+ * @param key An optional [String] key to use for storing the value in the [SavedStateHandle]. A
+ * default key will be generated if it's omitted or when 'null' is passed.
* @param init The function to provide the initial value of the property.
* @return A property delegate that manages the saving and restoring of the value.
*/
public inline fun <reified T : Any> SavedStateHandle.saved(
- key: String,
+ key: String? = null,
noinline init: () -> T,
): ReadWriteProperty<Any?, T> {
- return saved(key, serializer(), init)
-}
-
-/**
- * Returns a property delegate that uses [SavedStateHandle] to save and restore a value of type [T]
- * with fully qualified property or variable name as key.
- *
- * @sample androidx.lifecycle.delegateExplicitSerializer
- * @param serializer The [KSerializer] to use for serializing and deserializing the value.
- * @param init The function to provide the initial value of the property.
- * @return A property delegate that manages the saving and restoring of the value.
- */
-public fun <T : Any> SavedStateHandle.saved(
- serializer: KSerializer<T>,
- init: () -> T,
-): ReadWriteProperty<Any?, T> {
- return SavedStateHandleDelegate(
- savedStateHandle = this,
- key = null,
- serializer = serializer,
- init = init
- )
+ return saved(serializer(), key, init)
}
/**
* Returns a property delegate that uses [SavedStateHandle] to save and restore a value of type [T].
*
* @sample androidx.lifecycle.delegateExplicitKeyAndSerializer
- * @param key The [String] key to use for storing the value in the [SavedStateHandle].
* @param serializer The [KSerializer] to use for serializing and deserializing the value.
+ * @param key An optional [String] key to use for storing the value in the [SavedStateHandle]. A
+ * default key will be generated if it's omitted or when 'null' is passed.
* @param init The function to provide the initial value of the property.
* @return A property delegate that manages the saving and restoring of the value.
*/
public fun <T : Any> SavedStateHandle.saved(
- key: String,
serializer: KSerializer<T>,
+ key: String? = null,
init: () -> T,
): ReadWriteProperty<Any?, T> {
return SavedStateHandleDelegate(
diff --git a/navigation/navigation-compose/src/main/java/androidx/navigation/compose/NavHost.kt b/navigation/navigation-compose/src/main/java/androidx/navigation/compose/NavHost.kt
index 0a5ea75..f34aa4c 100644
--- a/navigation/navigation-compose/src/main/java/androidx/navigation/compose/NavHost.kt
+++ b/navigation/navigation-compose/src/main/java/androidx/navigation/compose/NavHost.kt
@@ -687,8 +687,9 @@
// animating. In these cases the currentEntry will be null, and in those cases,
// AnimatedContent will just skip attempting to transition the old entry.
// See https://issuetracker.google.com/238686802
+ val isPredictiveBackCancelAnimation = transitionState.currentState == backStackEntry
val currentEntry =
- if (inPredictiveBack) {
+ if (inPredictiveBack || isPredictiveBackCancelAnimation) {
// We have to do this because the previous entry does not show up in
// visibleEntries
// even if we prepare it above as part of onBackStackChangeStarted
diff --git a/navigation/navigation-runtime/bcv/native/current.txt b/navigation/navigation-runtime/bcv/native/current.txt
new file mode 100644
index 0000000..47e0dc0
--- /dev/null
+++ b/navigation/navigation-runtime/bcv/native/current.txt
@@ -0,0 +1,11 @@
+// Klib ABI Dump
+// Targets: [iosArm64, iosSimulatorArm64, iosX64, linuxArm64, linuxX64, macosArm64, macosX64]
+// Rendering settings:
+// - Signature version: 2
+// - Show manifest properties: true
+// - Show declarations: true
+
+// Library unique name: <androidx.navigation:navigation-runtime>
+open annotation class androidx.navigation/NavDeepLinkSaveStateControl : kotlin/Annotation { // androidx.navigation/NavDeepLinkSaveStateControl|null[0]
+ constructor <init>() // androidx.navigation/NavDeepLinkSaveStateControl.<init>|<init>(){}[0]
+}
diff --git a/navigation/navigation-runtime/build.gradle b/navigation/navigation-runtime/build.gradle
index 1ad4631..c71a4c0 100644
--- a/navigation/navigation-runtime/build.gradle
+++ b/navigation/navigation-runtime/build.gradle
@@ -24,49 +24,132 @@
import androidx.build.KotlinTarget
import androidx.build.LibraryType
+import androidx.build.PlatformIdentifier
+import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
plugins {
id("AndroidXPlugin")
id("com.android.library")
- id("kotlin-android")
alias(libs.plugins.kotlinSerialization)
}
+androidXMultiplatform {
+ android()
+ desktop()
+ mac()
+ linux()
+ ios()
+
+ defaultPlatform(PlatformIdentifier.ANDROID)
+
+ sourceSets {
+ commonMain {
+ dependencies {
+ api(libs.kotlinStdlib)
+ api(libs.kotlinCoroutinesCore)
+ api(project(":navigation:navigation-common"))
+ api("androidx.lifecycle:lifecycle-common:2.6.2")
+ api("androidx.lifecycle:lifecycle-runtime:2.6.2")
+ api("androidx.lifecycle:lifecycle-viewmodel:2.6.2")
+
+ implementation(libs.kotlinSerializationCore)
+ implementation("androidx.collection:collection:1.4.2")
+ }
+ }
+ commonTest {
+ dependencies {
+ implementation(libs.kotlinTest)
+ }
+ }
+ nonAndroidMain {
+ dependsOn(commonMain)
+ }
+ nonAndroidTest {
+ dependsOn(commonTest)
+ }
+ jvmCommonMain {
+ dependsOn(commonMain)
+ }
+ jvmCommonTest {
+ dependsOn(commonTest)
+ }
+ desktopMain {
+ dependsOn(jvmCommonMain)
+ dependsOn(nonAndroidMain)
+ }
+ desktopTest {
+ dependsOn(jvmCommonTest)
+ dependsOn(nonAndroidTest)
+ }
+ androidMain {
+ dependsOn(jvmCommonMain)
+ dependencies {
+ api("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
+ api("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2")
+ api("androidx.activity:activity-ktx:1.7.1")
+ api("androidx.core:core-ktx:1.8.0")
+ api("androidx.annotation:annotation-experimental:1.4.1")
+ }
+ }
+ androidUnitTest {
+ dependsOn(commonTest)
+ }
+ androidInstrumentedTest {
+ dependsOn(commonTest)
+ dependencies {
+ implementation("androidx.annotation:annotation:1.8.0")
+ implementation("androidx.lifecycle:lifecycle-viewmodel-savedstate:2.6.2")
+ implementation("androidx.lifecycle:lifecycle-common:2.6.2")
+ implementation("androidx.lifecycle:lifecycle-runtime:2.6.2")
+ implementation("androidx.lifecycle:lifecycle-runtime-testing:2.6.2")
+ implementation(project(":internal-testutils-navigation"))
+ implementation(project(":internal-testutils-runtime"))
+ implementation(libs.hamcrestCore)
+ implementation(libs.junit)
+ implementation(libs.kotlinTest)
+ implementation(libs.testCore)
+ implementation(libs.testExtJunit)
+ implementation(libs.testExtTruth)
+ implementation(libs.testMonitor)
+ implementation(libs.testRunner)
+ implementation(libs.testRules)
+ implementation(libs.espressoIntents)
+ implementation(libs.truth)
+ implementation(libs.dexmakerMockito)
+ implementation(libs.mockitoCore)
+ }
+ }
+ nonJvmCommonMain {
+ dependsOn(nonAndroidMain)
+ }
+ nonJvmCommonTest {
+ dependsOn(nonAndroidTest)
+ }
+ nativeMain {
+ dependsOn(nonJvmCommonMain)
+ }
+ linuxMain.dependsOn(nativeMain)
+ darwinMain.dependsOn(nativeMain)
+ nativeTest {
+ dependsOn(nonJvmCommonTest)
+ }
+ linuxTest.dependsOn(nativeTest)
+ darwinTest.dependsOn(nativeTest)
+ targets.configureEach { target ->
+ if (target.platformType == KotlinPlatformType.native) {
+ if (target.konanTarget.family.appleFamily) {
+ target.compilations["main"].defaultSourceSet.dependsOn(darwinMain)
+ target.compilations["test"].defaultSourceSet.dependsOn(darwinTest)
+ } else if (target.konanTarget.family == org.jetbrains.kotlin.konan.target.Family.LINUX) {
+ target.compilations["main"].defaultSourceSet.dependsOn(linuxMain)
+ target.compilations["test"].defaultSourceSet.dependsOn(linuxTest)
+ }
+ }
+ }
+ }
+}
+
dependencies {
- api(libs.kotlinStdlib)
- api(libs.kotlinCoroutinesCore)
- api(project(":navigation:navigation-common"))
- api("androidx.activity:activity-ktx:1.7.1")
- api("androidx.core:core-ktx:1.8.0")
- api("androidx.lifecycle:lifecycle-common:2.6.2")
- api("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
- api("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2")
- api("androidx.annotation:annotation-experimental:1.4.1")
-
- implementation(libs.kotlinSerializationCore)
- implementation("androidx.collection:collection:1.4.2")
-
- androidTestImplementation("androidx.annotation:annotation:1.8.0")
- androidTestImplementation("androidx.lifecycle:lifecycle-viewmodel-savedstate:2.6.2")
- androidTestImplementation("androidx.lifecycle:lifecycle-common:2.6.2")
- androidTestImplementation("androidx.lifecycle:lifecycle-runtime:2.6.2")
- androidTestImplementation("androidx.lifecycle:lifecycle-runtime-testing:2.6.2")
- androidTestImplementation(project(":internal-testutils-navigation"))
- androidTestImplementation(project(":internal-testutils-runtime"))
- androidTestImplementation(libs.hamcrestCore)
- androidTestImplementation(libs.junit)
- androidTestImplementation(libs.kotlinTest)
- androidTestImplementation(libs.testCore)
- androidTestImplementation(libs.testExtJunit)
- androidTestImplementation(libs.testExtTruth)
- androidTestImplementation(libs.testMonitor)
- androidTestImplementation(libs.testRunner)
- androidTestImplementation(libs.testRules)
- androidTestImplementation(libs.espressoIntents)
- androidTestImplementation(libs.truth)
- androidTestImplementation(libs.dexmakerMockito)
- androidTestImplementation(libs.mockitoCore)
-
lintPublish(project(":navigation:navigation-runtime-lint"))
}
diff --git a/navigation/navigation-runtime/src/androidTest/AndroidManifest.xml b/navigation/navigation-runtime/src/androidInstrumentedTest/AndroidManifest.xml
similarity index 100%
rename from navigation/navigation-runtime/src/androidTest/AndroidManifest.xml
rename to navigation/navigation-runtime/src/androidInstrumentedTest/AndroidManifest.xml
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/ActivityNavigatorDestinationBuilderTest.kt b/navigation/navigation-runtime/src/androidInstrumentedTest/kotlin/androidx/navigation/ActivityNavigatorDestinationBuilderTest.kt
similarity index 100%
rename from navigation/navigation-runtime/src/androidTest/java/androidx/navigation/ActivityNavigatorDestinationBuilderTest.kt
rename to navigation/navigation-runtime/src/androidInstrumentedTest/kotlin/androidx/navigation/ActivityNavigatorDestinationBuilderTest.kt
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/ActivityNavigatorExtrasTest.kt b/navigation/navigation-runtime/src/androidInstrumentedTest/kotlin/androidx/navigation/ActivityNavigatorExtrasTest.kt
similarity index 100%
rename from navigation/navigation-runtime/src/androidTest/java/androidx/navigation/ActivityNavigatorExtrasTest.kt
rename to navigation/navigation-runtime/src/androidInstrumentedTest/kotlin/androidx/navigation/ActivityNavigatorExtrasTest.kt
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/ActivityNavigatorTest.kt b/navigation/navigation-runtime/src/androidInstrumentedTest/kotlin/androidx/navigation/ActivityNavigatorTest.kt
similarity index 100%
rename from navigation/navigation-runtime/src/androidTest/java/androidx/navigation/ActivityNavigatorTest.kt
rename to navigation/navigation-runtime/src/androidInstrumentedTest/kotlin/androidx/navigation/ActivityNavigatorTest.kt
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/ActivityTest.kt b/navigation/navigation-runtime/src/androidInstrumentedTest/kotlin/androidx/navigation/ActivityTest.kt
similarity index 100%
rename from navigation/navigation-runtime/src/androidTest/java/androidx/navigation/ActivityTest.kt
rename to navigation/navigation-runtime/src/androidInstrumentedTest/kotlin/androidx/navigation/ActivityTest.kt
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/FloatingWindowTest.kt b/navigation/navigation-runtime/src/androidInstrumentedTest/kotlin/androidx/navigation/FloatingWindowTest.kt
similarity index 100%
rename from navigation/navigation-runtime/src/androidTest/java/androidx/navigation/FloatingWindowTest.kt
rename to navigation/navigation-runtime/src/androidInstrumentedTest/kotlin/androidx/navigation/FloatingWindowTest.kt
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavBackStackEntryLifecycleTest.kt b/navigation/navigation-runtime/src/androidInstrumentedTest/kotlin/androidx/navigation/NavBackStackEntryLifecycleTest.kt
similarity index 100%
rename from navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavBackStackEntryLifecycleTest.kt
rename to navigation/navigation-runtime/src/androidInstrumentedTest/kotlin/androidx/navigation/NavBackStackEntryLifecycleTest.kt
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavBackStackEntryTest.kt b/navigation/navigation-runtime/src/androidInstrumentedTest/kotlin/androidx/navigation/NavBackStackEntryTest.kt
similarity index 100%
rename from navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavBackStackEntryTest.kt
rename to navigation/navigation-runtime/src/androidInstrumentedTest/kotlin/androidx/navigation/NavBackStackEntryTest.kt
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerActivityTest.kt b/navigation/navigation-runtime/src/androidInstrumentedTest/kotlin/androidx/navigation/NavControllerActivityTest.kt
similarity index 100%
rename from navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerActivityTest.kt
rename to navigation/navigation-runtime/src/androidInstrumentedTest/kotlin/androidx/navigation/NavControllerActivityTest.kt
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerRouteTest.kt b/navigation/navigation-runtime/src/androidInstrumentedTest/kotlin/androidx/navigation/NavControllerRouteTest.kt
similarity index 100%
rename from navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerRouteTest.kt
rename to navigation/navigation-runtime/src/androidInstrumentedTest/kotlin/androidx/navigation/NavControllerRouteTest.kt
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerTest.kt b/navigation/navigation-runtime/src/androidInstrumentedTest/kotlin/androidx/navigation/NavControllerTest.kt
similarity index 100%
rename from navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerTest.kt
rename to navigation/navigation-runtime/src/androidInstrumentedTest/kotlin/androidx/navigation/NavControllerTest.kt
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerViewModelTest.kt b/navigation/navigation-runtime/src/androidInstrumentedTest/kotlin/androidx/navigation/NavControllerViewModelTest.kt
similarity index 100%
rename from navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerViewModelTest.kt
rename to navigation/navigation-runtime/src/androidInstrumentedTest/kotlin/androidx/navigation/NavControllerViewModelTest.kt
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavDeepLinkBuilderTest.kt b/navigation/navigation-runtime/src/androidInstrumentedTest/kotlin/androidx/navigation/NavDeepLinkBuilderTest.kt
similarity index 100%
rename from navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavDeepLinkBuilderTest.kt
rename to navigation/navigation-runtime/src/androidInstrumentedTest/kotlin/androidx/navigation/NavDeepLinkBuilderTest.kt
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavHostTest.kt b/navigation/navigation-runtime/src/androidInstrumentedTest/kotlin/androidx/navigation/NavHostTest.kt
similarity index 100%
rename from navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavHostTest.kt
rename to navigation/navigation-runtime/src/androidInstrumentedTest/kotlin/androidx/navigation/NavHostTest.kt
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavInflaterTest.kt b/navigation/navigation-runtime/src/androidInstrumentedTest/kotlin/androidx/navigation/NavInflaterTest.kt
similarity index 100%
rename from navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavInflaterTest.kt
rename to navigation/navigation-runtime/src/androidInstrumentedTest/kotlin/androidx/navigation/NavInflaterTest.kt
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/ViewTest.kt b/navigation/navigation-runtime/src/androidInstrumentedTest/kotlin/androidx/navigation/ViewTest.kt
similarity index 100%
rename from navigation/navigation-runtime/src/androidTest/java/androidx/navigation/ViewTest.kt
rename to navigation/navigation-runtime/src/androidInstrumentedTest/kotlin/androidx/navigation/ViewTest.kt
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/test/FloatingTestNavigator.kt b/navigation/navigation-runtime/src/androidInstrumentedTest/kotlin/androidx/navigation/test/FloatingTestNavigator.kt
similarity index 100%
rename from navigation/navigation-runtime/src/androidTest/java/androidx/navigation/test/FloatingTestNavigator.kt
rename to navigation/navigation-runtime/src/androidInstrumentedTest/kotlin/androidx/navigation/test/FloatingTestNavigator.kt
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/test/SupportingFloatingTestNavigator.kt b/navigation/navigation-runtime/src/androidInstrumentedTest/kotlin/androidx/navigation/test/SupportingFloatingTestNavigator.kt
similarity index 100%
rename from navigation/navigation-runtime/src/androidTest/java/androidx/navigation/test/SupportingFloatingTestNavigator.kt
rename to navigation/navigation-runtime/src/androidInstrumentedTest/kotlin/androidx/navigation/test/SupportingFloatingTestNavigator.kt
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/test/SupportingTestNavigator.kt b/navigation/navigation-runtime/src/androidInstrumentedTest/kotlin/androidx/navigation/test/SupportingTestNavigator.kt
similarity index 100%
rename from navigation/navigation-runtime/src/androidTest/java/androidx/navigation/test/SupportingTestNavigator.kt
rename to navigation/navigation-runtime/src/androidInstrumentedTest/kotlin/androidx/navigation/test/SupportingTestNavigator.kt
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/test/TestEnum.java b/navigation/navigation-runtime/src/androidInstrumentedTest/kotlin/androidx/navigation/test/TestEnum.kt
similarity index 86%
rename from navigation/navigation-runtime/src/androidTest/java/androidx/navigation/test/TestEnum.java
rename to navigation/navigation-runtime/src/androidInstrumentedTest/kotlin/androidx/navigation/test/TestEnum.kt
index f3a5276..11e2eb167 100644
--- a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/test/TestEnum.java
+++ b/navigation/navigation-runtime/src/androidInstrumentedTest/kotlin/androidx/navigation/test/TestEnum.kt
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-package androidx.navigation.test;
+package androidx.navigation.test
-public enum TestEnum {
- VALUE_ONE, VALUE_TWO
+public enum class TestEnum {
+ VALUE_ONE,
+ VALUE_TWO
}
diff --git a/navigation/navigation-runtime/src/androidTest/res/navigation/nav_applicationid_arg.xml b/navigation/navigation-runtime/src/androidInstrumentedTest/res/navigation/nav_applicationid_arg.xml
similarity index 100%
rename from navigation/navigation-runtime/src/androidTest/res/navigation/nav_applicationid_arg.xml
rename to navigation/navigation-runtime/src/androidInstrumentedTest/res/navigation/nav_applicationid_arg.xml
diff --git a/navigation/navigation-runtime/src/androidTest/res/navigation/nav_arguments.xml b/navigation/navigation-runtime/src/androidInstrumentedTest/res/navigation/nav_arguments.xml
similarity index 100%
rename from navigation/navigation-runtime/src/androidTest/res/navigation/nav_arguments.xml
rename to navigation/navigation-runtime/src/androidInstrumentedTest/res/navigation/nav_arguments.xml
diff --git a/navigation/navigation-runtime/src/androidTest/res/navigation/nav_deeplink.xml b/navigation/navigation-runtime/src/androidInstrumentedTest/res/navigation/nav_deeplink.xml
similarity index 100%
rename from navigation/navigation-runtime/src/androidTest/res/navigation/nav_deeplink.xml
rename to navigation/navigation-runtime/src/androidInstrumentedTest/res/navigation/nav_deeplink.xml
diff --git a/navigation/navigation-runtime/src/androidTest/res/navigation/nav_default_arguments.xml b/navigation/navigation-runtime/src/androidInstrumentedTest/res/navigation/nav_default_arguments.xml
similarity index 100%
rename from navigation/navigation-runtime/src/androidTest/res/navigation/nav_default_arguments.xml
rename to navigation/navigation-runtime/src/androidInstrumentedTest/res/navigation/nav_default_arguments.xml
diff --git a/navigation/navigation-runtime/src/androidTest/res/navigation/nav_first.xml b/navigation/navigation-runtime/src/androidInstrumentedTest/res/navigation/nav_first.xml
similarity index 100%
rename from navigation/navigation-runtime/src/androidTest/res/navigation/nav_first.xml
rename to navigation/navigation-runtime/src/androidInstrumentedTest/res/navigation/nav_first.xml
diff --git a/navigation/navigation-runtime/src/androidTest/res/navigation/nav_floating.xml b/navigation/navigation-runtime/src/androidInstrumentedTest/res/navigation/nav_floating.xml
similarity index 100%
rename from navigation/navigation-runtime/src/androidTest/res/navigation/nav_floating.xml
rename to navigation/navigation-runtime/src/androidInstrumentedTest/res/navigation/nav_floating.xml
diff --git a/navigation/navigation-runtime/src/androidTest/res/navigation/nav_invalid_argument_arg_type.xml b/navigation/navigation-runtime/src/androidInstrumentedTest/res/navigation/nav_invalid_argument_arg_type.xml
similarity index 100%
rename from navigation/navigation-runtime/src/androidTest/res/navigation/nav_invalid_argument_arg_type.xml
rename to navigation/navigation-runtime/src/androidInstrumentedTest/res/navigation/nav_invalid_argument_arg_type.xml
diff --git a/navigation/navigation-runtime/src/androidTest/res/navigation/nav_invalid_argument_default_value.xml b/navigation/navigation-runtime/src/androidInstrumentedTest/res/navigation/nav_invalid_argument_default_value.xml
similarity index 100%
rename from navigation/navigation-runtime/src/androidTest/res/navigation/nav_invalid_argument_default_value.xml
rename to navigation/navigation-runtime/src/androidInstrumentedTest/res/navigation/nav_invalid_argument_default_value.xml
diff --git a/navigation/navigation-runtime/src/androidTest/res/navigation/nav_invalid_start_destination.xml b/navigation/navigation-runtime/src/androidInstrumentedTest/res/navigation/nav_invalid_start_destination.xml
similarity index 100%
rename from navigation/navigation-runtime/src/androidTest/res/navigation/nav_invalid_start_destination.xml
rename to navigation/navigation-runtime/src/androidInstrumentedTest/res/navigation/nav_invalid_start_destination.xml
diff --git a/navigation/navigation-runtime/src/androidTest/res/navigation/nav_missing_start_destination.xml b/navigation/navigation-runtime/src/androidInstrumentedTest/res/navigation/nav_missing_start_destination.xml
similarity index 100%
rename from navigation/navigation-runtime/src/androidTest/res/navigation/nav_missing_start_destination.xml
rename to navigation/navigation-runtime/src/androidInstrumentedTest/res/navigation/nav_missing_start_destination.xml
diff --git a/navigation/navigation-runtime/src/androidTest/res/navigation/nav_multiple_navigation.xml b/navigation/navigation-runtime/src/androidInstrumentedTest/res/navigation/nav_multiple_navigation.xml
similarity index 100%
rename from navigation/navigation-runtime/src/androidTest/res/navigation/nav_multiple_navigation.xml
rename to navigation/navigation-runtime/src/androidInstrumentedTest/res/navigation/nav_multiple_navigation.xml
diff --git a/navigation/navigation-runtime/src/androidTest/res/navigation/nav_nested_start_destination.xml b/navigation/navigation-runtime/src/androidInstrumentedTest/res/navigation/nav_nested_start_destination.xml
similarity index 100%
rename from navigation/navigation-runtime/src/androidTest/res/navigation/nav_nested_start_destination.xml
rename to navigation/navigation-runtime/src/androidInstrumentedTest/res/navigation/nav_nested_start_destination.xml
diff --git a/navigation/navigation-runtime/src/androidTest/res/navigation/nav_non_start_nest.xml b/navigation/navigation-runtime/src/androidInstrumentedTest/res/navigation/nav_non_start_nest.xml
similarity index 100%
rename from navigation/navigation-runtime/src/androidTest/res/navigation/nav_non_start_nest.xml
rename to navigation/navigation-runtime/src/androidInstrumentedTest/res/navigation/nav_non_start_nest.xml
diff --git a/navigation/navigation-runtime/src/androidTest/res/navigation/nav_root.xml b/navigation/navigation-runtime/src/androidInstrumentedTest/res/navigation/nav_root.xml
similarity index 100%
rename from navigation/navigation-runtime/src/androidTest/res/navigation/nav_root.xml
rename to navigation/navigation-runtime/src/androidInstrumentedTest/res/navigation/nav_root.xml
diff --git a/navigation/navigation-runtime/src/androidTest/res/navigation/nav_second.xml b/navigation/navigation-runtime/src/androidInstrumentedTest/res/navigation/nav_second.xml
similarity index 100%
rename from navigation/navigation-runtime/src/androidTest/res/navigation/nav_second.xml
rename to navigation/navigation-runtime/src/androidInstrumentedTest/res/navigation/nav_second.xml
diff --git a/navigation/navigation-runtime/src/androidTest/res/navigation/nav_simple.xml b/navigation/navigation-runtime/src/androidInstrumentedTest/res/navigation/nav_simple.xml
similarity index 100%
rename from navigation/navigation-runtime/src/androidTest/res/navigation/nav_simple.xml
rename to navigation/navigation-runtime/src/androidInstrumentedTest/res/navigation/nav_simple.xml
diff --git a/navigation/navigation-runtime/src/androidTest/res/navigation/nav_start_destination.xml b/navigation/navigation-runtime/src/androidInstrumentedTest/res/navigation/nav_start_destination.xml
similarity index 100%
rename from navigation/navigation-runtime/src/androidTest/res/navigation/nav_start_destination.xml
rename to navigation/navigation-runtime/src/androidInstrumentedTest/res/navigation/nav_start_destination.xml
diff --git a/navigation/navigation-runtime/src/androidTest/res/values/styles.xml b/navigation/navigation-runtime/src/androidInstrumentedTest/res/values/styles.xml
similarity index 100%
rename from navigation/navigation-runtime/src/androidTest/res/values/styles.xml
rename to navigation/navigation-runtime/src/androidInstrumentedTest/res/values/styles.xml
diff --git a/navigation/navigation-runtime/src/androidTest/res/values/values.xml b/navigation/navigation-runtime/src/androidInstrumentedTest/res/values/values.xml
similarity index 100%
rename from navigation/navigation-runtime/src/androidTest/res/values/values.xml
rename to navigation/navigation-runtime/src/androidInstrumentedTest/res/values/values.xml
diff --git a/navigation/navigation-runtime/src/main/AndroidManifest.xml b/navigation/navigation-runtime/src/androidMain/AndroidManifest.xml
similarity index 100%
rename from navigation/navigation-runtime/src/main/AndroidManifest.xml
rename to navigation/navigation-runtime/src/androidMain/AndroidManifest.xml
diff --git a/navigation/navigation-runtime/src/main/baseline-prof.txt b/navigation/navigation-runtime/src/androidMain/baseline-prof.txt
similarity index 100%
rename from navigation/navigation-runtime/src/main/baseline-prof.txt
rename to navigation/navigation-runtime/src/androidMain/baseline-prof.txt
diff --git a/navigation/navigation-runtime/src/main/java/androidx/navigation/package-info.java b/navigation/navigation-runtime/src/androidMain/java/androidx/navigation/package-info.java
similarity index 100%
rename from navigation/navigation-runtime/src/main/java/androidx/navigation/package-info.java
rename to navigation/navigation-runtime/src/androidMain/java/androidx/navigation/package-info.java
diff --git a/navigation/navigation-runtime/src/main/java/androidx/navigation/Activity.kt b/navigation/navigation-runtime/src/androidMain/kotlin/androidx/navigation/Activity.android.kt
similarity index 94%
rename from navigation/navigation-runtime/src/main/java/androidx/navigation/Activity.kt
rename to navigation/navigation-runtime/src/androidMain/kotlin/androidx/navigation/Activity.android.kt
index 8bf71cd..a0f3b6b 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/Activity.kt
+++ b/navigation/navigation-runtime/src/androidMain/kotlin/androidx/navigation/Activity.android.kt
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+@file:JvmName("ActivityKt")
+@file:JvmMultifileClass
+
package androidx.navigation
import android.app.Activity
diff --git a/navigation/navigation-runtime/src/main/java/androidx/navigation/ActivityNavArgsLazy.kt b/navigation/navigation-runtime/src/androidMain/kotlin/androidx/navigation/ActivityNavArgsLazy.android.kt
similarity index 95%
rename from navigation/navigation-runtime/src/main/java/androidx/navigation/ActivityNavArgsLazy.kt
rename to navigation/navigation-runtime/src/androidMain/kotlin/androidx/navigation/ActivityNavArgsLazy.android.kt
index f1d9052..bf50861 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/ActivityNavArgsLazy.kt
+++ b/navigation/navigation-runtime/src/androidMain/kotlin/androidx/navigation/ActivityNavArgsLazy.android.kt
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+@file:JvmName("ActivityNavArgsLazyKt")
+@file:JvmMultifileClass
+
package androidx.navigation
import android.app.Activity
diff --git a/navigation/navigation-runtime/src/main/java/androidx/navigation/ActivityNavigator.kt b/navigation/navigation-runtime/src/androidMain/kotlin/androidx/navigation/ActivityNavigator.android.kt
similarity index 95%
rename from navigation/navigation-runtime/src/main/java/androidx/navigation/ActivityNavigator.kt
rename to navigation/navigation-runtime/src/androidMain/kotlin/androidx/navigation/ActivityNavigator.android.kt
index 4fb24ac..6838cad 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/ActivityNavigator.kt
+++ b/navigation/navigation-runtime/src/androidMain/kotlin/androidx/navigation/ActivityNavigator.android.kt
@@ -21,7 +21,6 @@
import android.content.ContextWrapper
import android.content.Intent
import android.net.Uri
-import android.os.Bundle
import android.util.AttributeSet
import android.util.Log
import androidx.annotation.CallSuper
@@ -29,6 +28,8 @@
import androidx.core.app.ActivityCompat
import androidx.core.app.ActivityOptionsCompat
import androidx.core.content.res.use
+import androidx.savedstate.SavedState
+import androidx.savedstate.read
import java.util.regex.Pattern
/** ActivityNavigator implements cross-activity navigation. */
@@ -75,7 +76,7 @@
@Suppress("DEPRECATION")
override fun navigate(
destination: Destination,
- args: Bundle?,
+ args: SavedState?,
navOptions: NavOptions?,
navigatorExtras: Navigator.Extras?
): NavDestination? {
@@ -92,17 +93,19 @@
val fillInPattern = Pattern.compile("\\{(.+?)\\}")
val matcher = fillInPattern.matcher(dataPattern)
while (matcher.find()) {
- val argName = matcher.group(1)
- require(args.containsKey(argName)) {
- "Could not find $argName in $args to fill data pattern $dataPattern"
+ args.read {
+ val argName = matcher.group(1)!!
+ require(contains(argName)) {
+ "Could not find $argName in $args to fill data pattern $dataPattern"
+ }
+ matcher.appendReplacement(data, "")
+ // Serialize with NavType if present, otherwise fallback to toString()
+ val navType = destination.arguments[argName]?.type
+ val value =
+ navType?.serializeAsValue(navType[args, argName])
+ ?: Uri.encode(args.get(argName).toString())
+ data.append(value)
}
- matcher.appendReplacement(data, "")
- // Serialize with NavType if present, otherwise fallback to toString()
- val navType = destination.arguments[argName!!]?.type
- val value =
- navType?.serializeAsValue(navType[args, argName])
- ?: Uri.encode(args[argName].toString())
- data.append(value)
}
matcher.appendTail(data)
intent.data = Uri.parse(data.toString())
diff --git a/navigation/navigation-runtime/src/main/java/androidx/navigation/ActivityNavigatorDestinationBuilder.kt b/navigation/navigation-runtime/src/androidMain/kotlin/androidx/navigation/ActivityNavigatorDestinationBuilder.android.kt
similarity index 97%
rename from navigation/navigation-runtime/src/main/java/androidx/navigation/ActivityNavigatorDestinationBuilder.kt
rename to navigation/navigation-runtime/src/androidMain/kotlin/androidx/navigation/ActivityNavigatorDestinationBuilder.android.kt
index 8fdb188..9be7470 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/ActivityNavigatorDestinationBuilder.kt
+++ b/navigation/navigation-runtime/src/androidMain/kotlin/androidx/navigation/ActivityNavigatorDestinationBuilder.android.kt
@@ -14,7 +14,8 @@
* limitations under the License.
*/
-@file:Suppress("NOTHING_TO_INLINE")
+@file:JvmName("ActivityNavigatorDestinationBuilderKt")
+@file:JvmMultifileClass
package androidx.navigation
diff --git a/navigation/navigation-runtime/src/main/java/androidx/navigation/ActivityNavigatorExtras.kt b/navigation/navigation-runtime/src/androidMain/kotlin/androidx/navigation/ActivityNavigatorExtras.android.kt
similarity index 94%
rename from navigation/navigation-runtime/src/main/java/androidx/navigation/ActivityNavigatorExtras.kt
rename to navigation/navigation-runtime/src/androidMain/kotlin/androidx/navigation/ActivityNavigatorExtras.android.kt
index f280a1a..1611117 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/ActivityNavigatorExtras.kt
+++ b/navigation/navigation-runtime/src/androidMain/kotlin/androidx/navigation/ActivityNavigatorExtras.android.kt
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+@file:JvmName("ActivityNavigatorExtrasKt")
+@file:JvmMultifileClass
+
package androidx.navigation
import androidx.core.app.ActivityOptionsCompat
diff --git a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavBackStackEntryState.kt b/navigation/navigation-runtime/src/androidMain/kotlin/androidx/navigation/NavBackStackEntryState.android.kt
similarity index 94%
rename from navigation/navigation-runtime/src/main/java/androidx/navigation/NavBackStackEntryState.kt
rename to navigation/navigation-runtime/src/androidMain/kotlin/androidx/navigation/NavBackStackEntryState.android.kt
index 2fec191..be20880 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavBackStackEntryState.kt
+++ b/navigation/navigation-runtime/src/androidMain/kotlin/androidx/navigation/NavBackStackEntryState.android.kt
@@ -17,23 +17,23 @@
import android.annotation.SuppressLint
import android.content.Context
-import android.os.Bundle
import android.os.Parcel
import android.os.Parcelable
import androidx.lifecycle.Lifecycle
+import androidx.savedstate.SavedState
@SuppressLint("BanParcelableUsage")
internal class NavBackStackEntryState : Parcelable {
val id: String
val destinationId: Int
- val args: Bundle?
- val savedState: Bundle
+ val args: SavedState?
+ val savedState: SavedState
constructor(entry: NavBackStackEntry) {
id = entry.id
destinationId = entry.destination.id
args = entry.arguments
- savedState = Bundle()
+ savedState = SavedState()
entry.saveState(savedState)
}
diff --git a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt b/navigation/navigation-runtime/src/androidMain/kotlin/androidx/navigation/NavController.android.kt
similarity index 93%
rename from navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt
rename to navigation/navigation-runtime/src/androidMain/kotlin/androidx/navigation/NavController.android.kt
index d592f33..145193d 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt
+++ b/navigation/navigation-runtime/src/androidMain/kotlin/androidx/navigation/NavController.android.kt
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+@file:JvmName("NavControllerKt")
+@file:JvmMultifileClass
+
package androidx.navigation
import android.app.Activity
@@ -21,7 +24,6 @@
import android.content.ContextWrapper
import android.content.Intent
import android.net.Uri
-import android.os.Bundle
import android.os.Parcelable
import android.util.Log
import androidx.activity.OnBackPressedCallback
@@ -33,7 +35,6 @@
import androidx.annotation.RestrictTo
import androidx.core.app.TaskStackBuilder
import androidx.core.net.toUri
-import androidx.core.os.bundleOf
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.LifecycleObserver
@@ -46,6 +47,10 @@
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.serialization.generateHashCode
import androidx.navigation.serialization.generateRouteWithArgs
+import androidx.savedstate.SavedState
+import androidx.savedstate.read
+import androidx.savedstate.savedState
+import androidx.savedstate.write
import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.atomic.AtomicInteger
import kotlin.collections.removeFirst as removeFirstKt
@@ -109,7 +114,7 @@
setGraph(graph, null)
}
- private var navigatorStateToRestore: Bundle? = null
+ private var navigatorStateToRestore: SavedState? = null
private var backStackToRestore: Array<Parcelable>? = null
private var deepLinkHandled = false
@@ -239,7 +244,7 @@
public fun onDestinationChanged(
controller: NavController,
destination: NavDestination,
- arguments: Bundle?
+ arguments: SavedState?
)
}
@@ -333,7 +338,7 @@
super.push(backStackEntry)
}
- override fun createBackStackEntry(destination: NavDestination, arguments: Bundle?) =
+ override fun createBackStackEntry(destination: NavDestination, arguments: SavedState?) =
NavBackStackEntry.create(context, destination, arguments, hostLifecycleState, viewModel)
override fun pop(popUpTo: NavBackStackEntry, saveState: Boolean) {
@@ -1005,7 +1010,7 @@
val extras = intent.extras
val deepLinkIds = extras!!.getIntArray(KEY_DEEP_LINK_IDS)!!.toMutableList()
- val deepLinkArgs = extras.getParcelableArrayList<Bundle>(KEY_DEEP_LINK_ARGS)
+ val deepLinkArgs = extras.getParcelableArrayList<SavedState>(KEY_DEEP_LINK_ARGS)
// Remove the leaf destination to pop up to one level above it
var leafDestinationId = deepLinkIds.removeLastKt()
@@ -1031,8 +1036,10 @@
val navDeepLinkBuilder = createDeepLink()
// Attach the original global arguments, and also the original calling Intent.
- val arguments = bundleOf(KEY_DEEP_LINK_INTENT to intent)
- extras.getBundle(KEY_DEEP_LINK_EXTRAS)?.let { arguments.putAll(it) }
+ val arguments = savedState {
+ putParcelable(KEY_DEEP_LINK_INTENT, intent)
+ extras.getBundle(KEY_DEEP_LINK_EXTRAS)?.let { putAll(it) }
+ }
navDeepLinkBuilder.setArguments(arguments)
deepLinkIds.forEachIndexed { index, deepLinkId ->
@@ -1054,29 +1061,30 @@
var parent = currentDestination.parent
while (parent != null) {
if (parent.startDestinationId != destId) {
- val args = Bundle()
- if (activity != null && activity!!.intent != null) {
- val data = activity!!.intent.data
+ val args = savedState {
+ if (activity != null && activity!!.intent != null) {
+ val data = activity!!.intent.data
- // We were started via a URI intent.
- if (data != null) {
- // Include the original deep link Intent so the Destinations can
- // synthetically generate additional arguments as necessary.
- args.putParcelable(KEY_DEEP_LINK_INTENT, activity!!.intent)
- val currGraph = backQueue.getTopGraph()
- val matchingDeepLink =
- currGraph.matchDeepLinkComprehensive(
- navDeepLinkRequest = NavDeepLinkRequest(activity!!.intent),
- searchChildren = true,
- searchParent = true,
- lastVisited = currGraph
- )
- if (matchingDeepLink?.matchingArgs != null) {
- val destinationArgs =
- matchingDeepLink.destination.addInDefaultArgs(
- matchingDeepLink.matchingArgs
+ // We were started via a URI intent.
+ if (data != null) {
+ // Include the original deep link Intent so the Destinations can
+ // synthetically generate additional arguments as necessary.
+ putParcelable(KEY_DEEP_LINK_INTENT, activity!!.intent)
+ val currGraph = backQueue.getTopGraph()
+ val matchingDeepLink =
+ currGraph.matchDeepLinkComprehensive(
+ navDeepLinkRequest = NavDeepLinkRequest(activity!!.intent),
+ searchChildren = true,
+ searchParent = true,
+ lastVisited = currGraph
)
- args.putAll(destinationArgs)
+ if (matchingDeepLink?.matchingArgs != null) {
+ val destinationArgs =
+ matchingDeepLink.destination.addInDefaultArgs(
+ matchingDeepLink.matchingArgs
+ )
+ destinationArgs?.let { putAll(it) }
+ }
}
}
}
@@ -1349,7 +1357,7 @@
*/
@MainThread
@CallSuper
- public open fun setGraph(@NavigationRes graphResId: Int, startDestinationArgs: Bundle?) {
+ public open fun setGraph(@NavigationRes graphResId: Int, startDestinationArgs: SavedState?) {
setGraph(navInflater.inflate(graphResId), startDestinationArgs)
}
@@ -1366,7 +1374,7 @@
*/
@MainThread
@CallSuper
- public open fun setGraph(graph: NavGraph, startDestinationArgs: Bundle?) {
+ public open fun setGraph(graph: NavGraph, startDestinationArgs: SavedState?) {
check(backQueue.isEmpty() || hostLifecycleState != Lifecycle.State.DESTROYED) {
"You cannot set a new graph on a NavController with entries on the back stack " +
"after the NavController has been destroyed. Please ensure that your NavHost " +
@@ -1413,16 +1421,15 @@
}
@MainThread
- private fun onGraphCreated(startDestinationArgs: Bundle?) {
- navigatorStateToRestore?.let { navigatorStateToRestore ->
- val navigatorNames =
- navigatorStateToRestore.getStringArrayList(KEY_NAVIGATOR_STATE_NAMES)
- if (navigatorNames != null) {
+ private fun onGraphCreated(startDestinationArgs: SavedState?) {
+ navigatorStateToRestore?.read {
+ if (contains(KEY_NAVIGATOR_STATE_NAMES)) {
+ val navigatorNames = getStringList(KEY_NAVIGATOR_STATE_NAMES)
for (name in navigatorNames) {
val navigator = _navigatorProvider.getNavigator<Navigator<*>>(name)
- val bundle = navigatorStateToRestore.getBundle(name)
- if (bundle != null) {
- navigator.onRestoreState(bundle)
+ if (contains(name)) {
+ val savedState = getSavedState(name)
+ navigator.onRestoreState(savedState)
}
}
}
@@ -1508,11 +1515,11 @@
Log.e(TAG, "handleDeepLink() could not extract deepLink from $intent", e)
null
}
- var deepLinkArgs = extras?.getParcelableArrayList<Bundle>(KEY_DEEP_LINK_ARGS)
- val globalArgs = Bundle()
+ var deepLinkArgs = extras?.getParcelableArrayList<SavedState>(KEY_DEEP_LINK_ARGS)
+ val globalArgs = savedState()
val deepLinkExtras = extras?.getBundle(KEY_DEEP_LINK_EXTRAS)
if (deepLinkExtras != null) {
- globalArgs.putAll(deepLinkExtras)
+ globalArgs.write { putAll(deepLinkExtras) }
}
if (deepLink == null || deepLink.isEmpty()) {
val currGraph = backQueue.getTopGraph()
@@ -1529,7 +1536,7 @@
deepLinkArgs = null
val destinationArgs = destination.addInDefaultArgs(matchingDeepLink.matchingArgs)
if (destinationArgs != null) {
- globalArgs.putAll(destinationArgs)
+ globalArgs.write { putAll(destinationArgs) }
}
}
}
@@ -1545,15 +1552,16 @@
)
return false
}
- globalArgs.putParcelable(KEY_DEEP_LINK_INTENT, intent)
- val args = arrayOfNulls<Bundle>(deepLink.size)
+ globalArgs.write { putParcelable(KEY_DEEP_LINK_INTENT, intent) }
+ val args = arrayOfNulls<SavedState>(deepLink.size)
for (index in args.indices) {
- val arguments = Bundle()
- arguments.putAll(globalArgs)
- if (deepLinkArgs != null) {
- val deepLinkArguments = deepLinkArgs[index]
- if (deepLinkArguments != null) {
- arguments.putAll(deepLinkArguments)
+ val arguments = savedState {
+ putAll(globalArgs)
+ if (deepLinkArgs != null) {
+ val deepLinkArguments = deepLinkArgs[index]
+ if (deepLinkArguments != null) {
+ putAll(deepLinkArguments)
+ }
}
}
args[index] = arguments
@@ -1604,15 +1612,15 @@
if (matchingDeepLink != null) {
val destination = matchingDeepLink.destination
val deepLink = destination.buildDeepLinkIds()
- val globalArgs = Bundle()
- val destinationArgs = destination.addInDefaultArgs(matchingDeepLink.matchingArgs)
- if (destinationArgs != null) {
- globalArgs.putAll(destinationArgs)
+ val globalArgs = savedState {
+ val destinationArgs = destination.addInDefaultArgs(matchingDeepLink.matchingArgs)
+ if (destinationArgs != null) {
+ putAll(destinationArgs)
+ }
}
- val args = arrayOfNulls<Bundle>(deepLink.size)
+ val args = arrayOfNulls<SavedState>(deepLink.size)
for (index in args.indices) {
- val arguments = Bundle()
- arguments.putAll(globalArgs)
+ val arguments = savedState { putAll(globalArgs) }
args[index] = arguments
}
return handleDeepLink(deepLink, args, true)
@@ -1622,7 +1630,7 @@
private fun handleDeepLink(
deepLink: IntArray,
- args: Array<Bundle?>,
+ args: Array<SavedState?>,
newTask: Boolean
): Boolean {
if (newTask) {
@@ -1883,7 +1891,7 @@
* destination
*/
@MainThread
- public open fun navigate(@IdRes resId: Int, args: Bundle?) {
+ public open fun navigate(@IdRes resId: Int, args: SavedState?) {
navigate(resId, args, null)
}
@@ -1902,7 +1910,7 @@
* destination
*/
@MainThread
- public open fun navigate(@IdRes resId: Int, args: Bundle?, navOptions: NavOptions?) {
+ public open fun navigate(@IdRes resId: Int, args: SavedState?, navOptions: NavOptions?) {
navigate(resId, args, navOptions, null)
}
@@ -1925,7 +1933,7 @@
@MainThread
public open fun navigate(
@IdRes resId: Int,
- args: Bundle?,
+ args: SavedState?,
navOptions: NavOptions?,
navigatorExtras: Navigator.Extras?
) {
@@ -1939,7 +1947,7 @@
@IdRes var destId = resId
val navAction = currentNode.getAction(resId)
- var combinedArgs: Bundle? = null
+ var combinedArgs: SavedState? = null
if (navAction != null) {
if (finalNavOptions == null) {
finalNavOptions = navAction.navOptions
@@ -1947,15 +1955,14 @@
destId = navAction.destinationId
val navActionArgs = navAction.defaultArguments
if (navActionArgs != null) {
- combinedArgs = Bundle()
- combinedArgs.putAll(navActionArgs)
+ combinedArgs = savedState { putAll(navActionArgs) }
}
}
if (args != null) {
if (combinedArgs == null) {
- combinedArgs = Bundle()
+ combinedArgs = savedState()
}
- combinedArgs.putAll(args)
+ combinedArgs.write { putAll(args) }
}
// just pop and return if destId is invalid
if (
@@ -2109,14 +2116,14 @@
)
if (deepLinkMatch != null) {
val destination = deepLinkMatch.destination
- val args = destination.addInDefaultArgs(deepLinkMatch.matchingArgs) ?: Bundle()
+ val args = destination.addInDefaultArgs(deepLinkMatch.matchingArgs) ?: savedState()
val node = deepLinkMatch.destination
val intent =
Intent().apply {
setDataAndType(request.uri, request.mimeType)
action = request.action
}
- args.putParcelable(KEY_DEEP_LINK_INTENT, intent)
+ args.write { putParcelable(KEY_DEEP_LINK_INTENT, intent) }
navigate(node, args, navOptions, navigatorExtras)
} else {
throw IllegalArgumentException(
@@ -2130,7 +2137,7 @@
@MainThread
private fun navigate(
node: NavDestination,
- args: Bundle?,
+ args: SavedState?,
navOptions: NavOptions?,
navigatorExtras: Navigator.Extras?
) {
@@ -2205,7 +2212,7 @@
}
}
- private fun launchSingleTopInternal(node: NavDestination, args: Bundle?): Boolean {
+ private fun launchSingleTopInternal(node: NavDestination, args: SavedState?): Boolean {
val currentBackStackEntry = currentBackStackEntry
val nodeIndex = backQueue.indexOfLast { it.destination === node }
// early return when node isn't even in backQueue
@@ -2254,7 +2261,7 @@
private fun restoreStateInternal(
id: Int,
- args: Bundle?,
+ args: SavedState?,
navOptions: NavOptions?,
navigatorExtras: Navigator.Extras?
): Boolean {
@@ -2303,7 +2310,7 @@
private fun executeRestoreState(
entries: List<NavBackStackEntry>,
- args: Bundle?,
+ args: SavedState?,
navOptions: NavOptions?,
navigatorExtras: Navigator.Extras?
): Boolean {
@@ -2373,7 +2380,7 @@
private fun addEntryToBackStack(
node: NavDestination,
- finalArgs: Bundle?,
+ finalArgs: SavedState?,
backStackEntry: NavBackStackEntry,
restoredEntries: List<NavBackStackEntry> = emptyList()
) {
@@ -2430,7 +2437,7 @@
) {
val parent = destination.parent
if (parent != null) {
- val args = if (finalArgs?.isEmpty == true) null else finalArgs
+ val args = if (finalArgs?.read { isEmpty() } == true) null else finalArgs
val entry =
restoredEntries.lastOrNull { restoredEntry ->
restoredEntry.destination == parent
@@ -2580,14 +2587,14 @@
)
if (deepLinkMatch != null) {
val destination = deepLinkMatch.destination
- val args = destination.addInDefaultArgs(deepLinkMatch.matchingArgs) ?: Bundle()
+ val args = destination.addInDefaultArgs(deepLinkMatch.matchingArgs) ?: savedState()
val node = deepLinkMatch.destination
val intent =
Intent().apply {
setDataAndType(createRoute(destination.route).toUri(), null)
action = null
}
- args.putParcelable(KEY_DEEP_LINK_INTENT, intent)
+ args.write { putParcelable(KEY_DEEP_LINK_INTENT, intent) }
navigate(node, args, navOptions, navigatorExtras)
} else {
throw IllegalArgumentException(
@@ -2649,44 +2656,45 @@
}
/**
- * Saves all navigation controller state to a Bundle.
+ * Saves all navigation controller state to a SavedState.
*
- * State may be restored from a bundle returned from this method by calling [restoreState].
+ * State may be restored from a SavedState returned from this method by calling [restoreState].
* Saving controller state is the responsibility of a [NavHost].
*
* @return saved state for this controller
*/
@CallSuper
- public open fun saveState(): Bundle? {
- var b: Bundle? = null
+ public open fun saveState(): SavedState? {
+ var b: SavedState? = null
val navigatorNames = ArrayList<String>()
- val navigatorState = Bundle()
+ val navigatorState = savedState()
for ((name, value) in _navigatorProvider.navigators) {
val savedState = value.onSaveState()
if (savedState != null) {
navigatorNames.add(name)
- navigatorState.putBundle(name, savedState)
+ navigatorState.write { putSavedState(name, savedState) }
}
}
if (navigatorNames.isNotEmpty()) {
- b = Bundle()
- navigatorState.putStringArrayList(KEY_NAVIGATOR_STATE_NAMES, navigatorNames)
- b.putBundle(KEY_NAVIGATOR_STATE, navigatorState)
+ b = savedState {
+ navigatorState.write { putStringList(KEY_NAVIGATOR_STATE_NAMES, navigatorNames) }
+ putSavedState(KEY_NAVIGATOR_STATE, navigatorState)
+ }
}
if (backQueue.isNotEmpty()) {
if (b == null) {
- b = Bundle()
+ b = savedState()
}
val backStack = arrayOfNulls<Parcelable>(backQueue.size)
var index = 0
for (backStackEntry in this.backQueue) {
backStack[index++] = NavBackStackEntryState(backStackEntry)
}
- b.putParcelableArray(KEY_BACK_STACK, backStack)
+ b.write { putParcelableList(KEY_BACK_STACK, backStack.toList().filterNotNull()) }
}
if (backStackMap.isNotEmpty()) {
if (b == null) {
- b = Bundle()
+ b = savedState()
}
val backStackDestIds = IntArray(backStackMap.size)
val backStackIds = ArrayList<String?>()
@@ -2695,12 +2703,14 @@
backStackDestIds[index++] = destId
backStackIds += id
}
- b.putIntArray(KEY_BACK_STACK_DEST_IDS, backStackDestIds)
- b.putStringArrayList(KEY_BACK_STACK_IDS, backStackIds)
+ b.write {
+ putIntArray(KEY_BACK_STACK_DEST_IDS, backStackDestIds)
+ putStringList(KEY_BACK_STACK_IDS, backStackIds.toList().filterNotNull())
+ }
}
if (backStackStates.isNotEmpty()) {
if (b == null) {
- b = Bundle()
+ b = savedState()
}
val backStackStateIds = ArrayList<String>()
for ((id, backStackStates) in backStackStates) {
@@ -2709,56 +2719,73 @@
backStackStates.forEachIndexed { stateIndex, backStackState ->
states[stateIndex] = backStackState
}
- b.putParcelableArray(KEY_BACK_STACK_STATES_PREFIX + id, states)
+ b.write {
+ putParcelableList(
+ KEY_BACK_STACK_STATES_PREFIX + id,
+ states.toList().filterNotNull()
+ )
+ }
}
- b.putStringArrayList(KEY_BACK_STACK_STATES_IDS, backStackStateIds)
+ b.write { putStringList(KEY_BACK_STACK_STATES_IDS, backStackStateIds) }
}
if (deepLinkHandled) {
if (b == null) {
- b = Bundle()
+ b = savedState()
}
- b.putBoolean(KEY_DEEP_LINK_HANDLED, deepLinkHandled)
+ b.write { putBoolean(KEY_DEEP_LINK_HANDLED, deepLinkHandled) }
}
return b
}
/**
- * Restores all navigation controller state from a bundle. This should be called before any call
- * to [setGraph].
+ * Restores all navigation controller state from a SavedState. This should be called before any
+ * call to [setGraph].
*
- * State may be saved to a bundle by calling [saveState]. Restoring controller state is the
+ * State may be saved to a SavedState by calling [saveState]. Restoring controller state is the
* responsibility of a [NavHost].
*
- * @param navState state bundle to restore
+ * @param navState SavedState to restore
*/
@CallSuper
- @Suppress("DEPRECATION")
- public open fun restoreState(navState: Bundle?) {
+ public open fun restoreState(navState: SavedState?) {
if (navState == null) {
return
}
navState.classLoader = context.classLoader
- navigatorStateToRestore = navState.getBundle(KEY_NAVIGATOR_STATE)
- backStackToRestore = navState.getParcelableArray(KEY_BACK_STACK)
- backStackStates.clear()
- val backStackDestIds = navState.getIntArray(KEY_BACK_STACK_DEST_IDS)
- val backStackIds = navState.getStringArrayList(KEY_BACK_STACK_IDS)
- if (backStackDestIds != null && backStackIds != null) {
- backStackDestIds.forEachIndexed { index, id -> backStackMap[id] = backStackIds[index] }
- }
- val backStackStateIds = navState.getStringArrayList(KEY_BACK_STACK_STATES_IDS)
- backStackStateIds?.forEach { id ->
- val backStackState = navState.getParcelableArray(KEY_BACK_STACK_STATES_PREFIX + id)
- if (backStackState != null) {
- backStackStates[id] =
- ArrayDeque<NavBackStackEntryState>(backStackState.size).apply {
- for (parcelable in backStackState) {
- add(parcelable as NavBackStackEntryState)
- }
- }
+ navState.read {
+ navigatorStateToRestore =
+ if (contains(KEY_NAVIGATOR_STATE)) {
+ getSavedState(KEY_NAVIGATOR_STATE)
+ } else null
+ backStackToRestore =
+ if (contains(KEY_BACK_STACK)) {
+ getParcelableList<Parcelable>(KEY_BACK_STACK).toTypedArray()
+ } else null
+ backStackStates.clear()
+ if (contains(KEY_BACK_STACK_DEST_IDS) && contains(KEY_BACK_STACK_IDS)) {
+ val backStackDestIds = getIntArray(KEY_BACK_STACK_DEST_IDS)
+ val backStackIds = getStringArray(KEY_BACK_STACK_IDS)
+ backStackDestIds.forEachIndexed { index, id ->
+ backStackMap[id] = backStackIds[index]
+ }
}
+ if (contains(KEY_BACK_STACK_STATES_IDS)) {
+ val backStackStateIds = getStringArray(KEY_BACK_STACK_STATES_IDS)
+ backStackStateIds.forEach { id ->
+ if (contains(KEY_BACK_STACK_STATES_PREFIX + id)) {
+ val backStackState =
+ getParcelableList<Parcelable>(KEY_BACK_STACK_STATES_PREFIX + id)
+ backStackStates[id] =
+ ArrayDeque<NavBackStackEntryState>(backStackState.size).apply {
+ for (parcelable in backStackState) {
+ add(parcelable as NavBackStackEntryState)
+ }
+ }
+ }
+ }
+ }
+ deepLinkHandled = getBooleanOrElse(KEY_DEEP_LINK_HANDLED) { false }
}
- deepLinkHandled = navState.getBoolean(KEY_DEEP_LINK_HANDLED)
}
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
diff --git a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavControllerViewModel.kt b/navigation/navigation-runtime/src/androidMain/kotlin/androidx/navigation/NavControllerViewModel.android.kt
similarity index 100%
rename from navigation/navigation-runtime/src/main/java/androidx/navigation/NavControllerViewModel.kt
rename to navigation/navigation-runtime/src/androidMain/kotlin/androidx/navigation/NavControllerViewModel.android.kt
diff --git a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavDeepLinkBuilder.kt b/navigation/navigation-runtime/src/androidMain/kotlin/androidx/navigation/NavDeepLinkBuilder.android.kt
similarity index 91%
rename from navigation/navigation-runtime/src/main/java/androidx/navigation/NavDeepLinkBuilder.kt
rename to navigation/navigation-runtime/src/androidMain/kotlin/androidx/navigation/NavDeepLinkBuilder.android.kt
index 3e65714..649ba64 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavDeepLinkBuilder.kt
+++ b/navigation/navigation-runtime/src/androidMain/kotlin/androidx/navigation/NavDeepLinkBuilder.android.kt
@@ -21,11 +21,12 @@
import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
-import android.os.Bundle
import androidx.annotation.IdRes
import androidx.annotation.NavigationRes
import androidx.core.app.TaskStackBuilder
import androidx.navigation.NavDestination.Companion.createRoute
+import androidx.savedstate.SavedState
+import androidx.savedstate.read
/**
* Class used to construct deep links to a particular destination in a [NavGraph].
@@ -50,7 +51,8 @@
* @see NavDeepLinkBuilder.setComponentName
*/
public class NavDeepLinkBuilder(private val context: Context) {
- private class DeepLinkDestination constructor(val destinationId: Int, val arguments: Bundle?)
+ private class DeepLinkDestination
+ constructor(val destinationId: Int, val arguments: SavedState?)
private val activity: Activity? =
generateSequence(context) { (it as? ContextWrapper)?.baseContext }
@@ -67,7 +69,7 @@
.also { it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) }
private var graph: NavGraph? = null
private val destinations = mutableListOf<DeepLinkDestination>()
- private var globalArgs: Bundle? = null
+ private var globalArgs: SavedState? = null
/** @see NavController.createDeepLink */
internal constructor(navController: NavController) : this(navController.context) {
@@ -132,7 +134,7 @@
* @return this object for chaining
*/
@JvmOverloads
- public fun setDestination(@IdRes destId: Int, args: Bundle? = null): NavDeepLinkBuilder {
+ public fun setDestination(@IdRes destId: Int, args: SavedState? = null): NavDeepLinkBuilder {
destinations.clear()
destinations.add(DeepLinkDestination(destId, args))
if (graph != null) {
@@ -152,7 +154,7 @@
* @return this object for chaining
*/
@JvmOverloads
- public fun setDestination(destRoute: String, args: Bundle? = null): NavDeepLinkBuilder {
+ public fun setDestination(destRoute: String, args: SavedState? = null): NavDeepLinkBuilder {
destinations.clear()
destinations.add(DeepLinkDestination(createRoute(destRoute).hashCode(), args))
if (graph != null) {
@@ -190,7 +192,7 @@
* @return this object for chaining
*/
@JvmOverloads
- public fun addDestination(@IdRes destId: Int, args: Bundle? = null): NavDeepLinkBuilder {
+ public fun addDestination(@IdRes destId: Int, args: SavedState? = null): NavDeepLinkBuilder {
destinations.add(DeepLinkDestination(destId, args))
if (graph != null) {
verifyAllDestinations()
@@ -210,7 +212,7 @@
* @return this object for chaining
*/
@JvmOverloads
- public fun addDestination(route: String, args: Bundle? = null): NavDeepLinkBuilder {
+ public fun addDestination(route: String, args: SavedState? = null): NavDeepLinkBuilder {
destinations.add(DeepLinkDestination(createRoute(route).hashCode(), args))
if (graph != null) {
verifyAllDestinations()
@@ -249,7 +251,7 @@
private fun fillInIntent() {
val deepLinkIds = mutableListOf<Int>()
- val deepLinkArgs = ArrayList<Bundle?>()
+ val deepLinkArgs = ArrayList<SavedState?>()
var previousDestination: NavDestination? = null
for (destination in destinations) {
val destId = destination.destinationId
@@ -278,7 +280,7 @@
* @param args arguments to pass to each destination
* @return this object for chaining
*/
- public fun setArguments(args: Bundle?): NavDeepLinkBuilder {
+ public fun setArguments(args: SavedState?): NavDeepLinkBuilder {
globalArgs = args
intent.putExtra(NavController.KEY_DEEP_LINK_EXTRAS, args)
return this
@@ -330,22 +332,13 @@
*/
@Suppress("DEPRECATION")
public fun createPendingIntent(): PendingIntent {
- var requestCode = 0
- globalArgs?.let { globalArgs ->
- for (key in globalArgs.keySet()) {
- val value = globalArgs[key]
- requestCode = 31 * requestCode + (value?.hashCode() ?: 0)
- }
- }
+ var requestCode = globalArgs?.read { contentDeepHashCode() } ?: 0
for (destination in destinations) {
val destId = destination.destinationId
requestCode = 31 * requestCode + destId
- val arguments = destination.arguments
- if (arguments != null) {
- for (key in arguments.keySet()) {
- val value = arguments[key]
- requestCode = 31 * requestCode + (value?.hashCode() ?: 0)
- }
+ val argumentsHashCode = destination.arguments?.read { contentDeepHashCode() }
+ if (argumentsHashCode != null) {
+ requestCode = 31 * requestCode + argumentsHashCode
}
}
return createTaskStackBuilder()
@@ -369,7 +362,7 @@
override fun navigate(
destination: NavDestination,
- args: Bundle?,
+ args: SavedState?,
navOptions: NavOptions?,
navigatorExtras: Extras?
): NavDestination? {
diff --git a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavHost.kt b/navigation/navigation-runtime/src/androidMain/kotlin/androidx/navigation/NavHost.android.kt
similarity index 98%
rename from navigation/navigation-runtime/src/main/java/androidx/navigation/NavHost.kt
rename to navigation/navigation-runtime/src/androidMain/kotlin/androidx/navigation/NavHost.android.kt
index 30024a2..b21b710 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavHost.kt
+++ b/navigation/navigation-runtime/src/androidMain/kotlin/androidx/navigation/NavHost.android.kt
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+@file:JvmName("NavHostKt")
+@file:JvmMultifileClass
+
package androidx.navigation
import androidx.annotation.IdRes
diff --git a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavHostController.kt b/navigation/navigation-runtime/src/androidMain/kotlin/androidx/navigation/NavHostController.android.kt
similarity index 100%
rename from navigation/navigation-runtime/src/main/java/androidx/navigation/NavHostController.kt
rename to navigation/navigation-runtime/src/androidMain/kotlin/androidx/navigation/NavHostController.android.kt
diff --git a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavInflater.kt b/navigation/navigation-runtime/src/androidMain/kotlin/androidx/navigation/NavInflater.android.kt
similarity index 97%
rename from navigation/navigation-runtime/src/main/java/androidx/navigation/NavInflater.kt
rename to navigation/navigation-runtime/src/androidMain/kotlin/androidx/navigation/NavInflater.android.kt
index f8e46da..a3e0c58 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavInflater.kt
+++ b/navigation/navigation-runtime/src/androidMain/kotlin/androidx/navigation/NavInflater.android.kt
@@ -20,7 +20,6 @@
import android.content.res.Resources
import android.content.res.TypedArray
import android.content.res.XmlResourceParser
-import android.os.Bundle
import android.util.AttributeSet
import android.util.TypedValue
import android.util.Xml
@@ -29,6 +28,9 @@
import androidx.core.content.res.use
import androidx.core.content.withStyledAttributes
import androidx.navigation.common.R
+import androidx.savedstate.SavedState
+import androidx.savedstate.read
+import androidx.savedstate.savedState
import java.io.IOException
import org.xmlpull.v1.XmlPullParser
import org.xmlpull.v1.XmlPullParserException
@@ -137,7 +139,7 @@
@Throws(XmlPullParserException::class)
private fun inflateArgumentForBundle(
res: Resources,
- bundle: Bundle,
+ savedState: SavedState,
attrs: AttributeSet,
graphResId: Int
) {
@@ -147,7 +149,7 @@
?: throw XmlPullParserException("Arguments must have a name")
val argument = inflateArgument(array, res, graphResId)
if (argument.isDefaultValuePresent) {
- argument.putDefaultValue(name, bundle)
+ argument.putDefaultValue(name, savedState)
}
}
}
@@ -309,7 +311,7 @@
builder.setPopEnterAnim(getResourceId(R.styleable.NavAction_popEnterAnim, -1))
builder.setPopExitAnim(getResourceId(R.styleable.NavAction_popExitAnim, -1))
action.navOptions = builder.build()
- val args = Bundle()
+ val args = savedState()
val innerDepth = parser.depth + 1
var type: Int
var depth = 0
@@ -329,7 +331,7 @@
inflateArgumentForBundle(res, args, attrs, graphResId)
}
}
- if (!args.isEmpty) {
+ if (!args.read { isEmpty() }) {
action.defaultArguments = args
}
dest.putAction(id, action)
diff --git a/navigation/navigation-runtime/src/main/java/androidx/navigation/Navigation.kt b/navigation/navigation-runtime/src/androidMain/kotlin/androidx/navigation/Navigation.android.kt
similarity index 98%
rename from navigation/navigation-runtime/src/main/java/androidx/navigation/Navigation.kt
rename to navigation/navigation-runtime/src/androidMain/kotlin/androidx/navigation/Navigation.android.kt
index 4597770..4cf9a03 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/Navigation.kt
+++ b/navigation/navigation-runtime/src/androidMain/kotlin/androidx/navigation/Navigation.android.kt
@@ -16,10 +16,10 @@
package androidx.navigation
import android.app.Activity
-import android.os.Bundle
import android.view.View
import androidx.annotation.IdRes
import androidx.core.app.ActivityCompat
+import androidx.savedstate.SavedState
import java.lang.ref.WeakReference
/**
@@ -83,7 +83,7 @@
@JvmOverloads
public fun createNavigateOnClickListener(
@IdRes resId: Int,
- args: Bundle? = null
+ args: SavedState? = null
): View.OnClickListener {
return View.OnClickListener { view -> findNavController(view).navigate(resId, args) }
}
diff --git a/navigation/navigation-runtime/src/main/java/androidx/navigation/View.kt b/navigation/navigation-runtime/src/androidMain/kotlin/androidx/navigation/View.android.kt
similarity index 94%
rename from navigation/navigation-runtime/src/main/java/androidx/navigation/View.kt
rename to navigation/navigation-runtime/src/androidMain/kotlin/androidx/navigation/View.android.kt
index 55db610..c5db4bc 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/View.kt
+++ b/navigation/navigation-runtime/src/androidMain/kotlin/androidx/navigation/View.android.kt
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+@file:JvmName("ViewKt")
+@file:JvmMultifileClass
+
package androidx.navigation
import android.view.View
diff --git a/navigation/navigation-runtime/src/main/res-public/values/public_attrs.xml b/navigation/navigation-runtime/src/androidMain/res-public/values/public_attrs.xml
similarity index 100%
rename from navigation/navigation-runtime/src/main/res-public/values/public_attrs.xml
rename to navigation/navigation-runtime/src/androidMain/res-public/values/public_attrs.xml
diff --git a/navigation/navigation-runtime/src/main/res/values/attrs.xml b/navigation/navigation-runtime/src/androidMain/res/values/attrs.xml
similarity index 100%
rename from navigation/navigation-runtime/src/main/res/values/attrs.xml
rename to navigation/navigation-runtime/src/androidMain/res/values/attrs.xml
diff --git a/navigation/navigation-runtime/src/main/res/values/ids.xml b/navigation/navigation-runtime/src/androidMain/res/values/ids.xml
similarity index 100%
rename from navigation/navigation-runtime/src/main/res/values/ids.xml
rename to navigation/navigation-runtime/src/androidMain/res/values/ids.xml
diff --git a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavDeepLinkSaveStateControl.kt b/navigation/navigation-runtime/src/commonMain/kotlin/androidx/navigation/NavDeepLinkSaveStateControl.kt
similarity index 94%
rename from navigation/navigation-runtime/src/main/java/androidx/navigation/NavDeepLinkSaveStateControl.kt
rename to navigation/navigation-runtime/src/commonMain/kotlin/androidx/navigation/NavDeepLinkSaveStateControl.kt
index 469b038..5e7015f 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavDeepLinkSaveStateControl.kt
+++ b/navigation/navigation-runtime/src/commonMain/kotlin/androidx/navigation/NavDeepLinkSaveStateControl.kt
@@ -16,7 +16,6 @@
package androidx.navigation
-/** @see NavController.enableDeepLinkSaveState */
@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.FUNCTION)
@RequiresOptIn(level = RequiresOptIn.Level.WARNING)
diff --git a/navigation3/navigation3/api/current.txt b/navigation3/navigation3/api/current.txt
index d8d07e0..36708c9 100644
--- a/navigation3/navigation3/api/current.txt
+++ b/navigation3/navigation3/api/current.txt
@@ -46,20 +46,20 @@
}
public final class NavDisplay {
- method public java.util.Map<java.lang.String,java.lang.Object> isDialog(boolean boolean);
+ method public java.util.Map<java.lang.String,java.lang.Object> popTransition(androidx.compose.animation.EnterTransition? enter, androidx.compose.animation.ExitTransition? exit);
method public java.util.Map<java.lang.String,java.lang.Object> transition(androidx.compose.animation.EnterTransition? enter, androidx.compose.animation.ExitTransition? exit);
field public static final androidx.navigation3.NavDisplay INSTANCE;
}
public final class NavDisplay_androidKt {
- method @androidx.compose.runtime.Composable public static <T> void NavDisplay(java.util.List<? extends T> backstack, optional androidx.compose.ui.Modifier modifier, optional java.util.List<? extends androidx.navigation3.NavLocalProvider> localProviders, optional androidx.compose.ui.Alignment contentAlignment, optional androidx.compose.animation.SizeTransform? sizeTransform, optional androidx.compose.animation.EnterTransition enterTransition, optional androidx.compose.animation.ExitTransition exitTransition, optional kotlin.jvm.functions.Function0<kotlin.Unit> onBack, kotlin.jvm.functions.Function1<? super T,? extends androidx.navigation3.NavEntry<? extends T>> entryProvider);
+ method @androidx.compose.runtime.Composable public static <T> void NavDisplay(java.util.List<? extends T> backstack, optional androidx.compose.ui.Modifier modifier, optional java.util.List<? extends androidx.navigation3.NavLocalProvider> localProviders, optional androidx.compose.ui.Alignment contentAlignment, optional androidx.compose.animation.SizeTransform? sizeTransform, optional androidx.compose.animation.EnterTransition enterTransition, optional androidx.compose.animation.ExitTransition exitTransition, optional androidx.compose.animation.EnterTransition popEnterTransition, optional androidx.compose.animation.ExitTransition popExitTransition, optional kotlin.jvm.functions.Function0<kotlin.Unit> onBack, kotlin.jvm.functions.Function1<? super T,? extends androidx.navigation3.NavEntry<? extends T>> entryProvider);
}
- public final class NavEntry<T> {
+ public class NavEntry<T> {
ctor public NavEntry(T key, optional java.util.Map<java.lang.String,?> featureMap, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> content);
- method public kotlin.jvm.functions.Function1<T,kotlin.Unit> getContent();
- method public java.util.Map<java.lang.String,java.lang.Object> getFeatureMap();
- method public T getKey();
+ method public final kotlin.jvm.functions.Function1<T,kotlin.Unit> getContent();
+ method public final java.util.Map<java.lang.String,java.lang.Object> getFeatureMap();
+ method public final T getKey();
property public final kotlin.jvm.functions.Function1<T,kotlin.Unit> content;
property public final java.util.Map<java.lang.String,java.lang.Object> featureMap;
property public final T key;
diff --git a/navigation3/navigation3/api/restricted_current.txt b/navigation3/navigation3/api/restricted_current.txt
index d8d07e0..36708c9 100644
--- a/navigation3/navigation3/api/restricted_current.txt
+++ b/navigation3/navigation3/api/restricted_current.txt
@@ -46,20 +46,20 @@
}
public final class NavDisplay {
- method public java.util.Map<java.lang.String,java.lang.Object> isDialog(boolean boolean);
+ method public java.util.Map<java.lang.String,java.lang.Object> popTransition(androidx.compose.animation.EnterTransition? enter, androidx.compose.animation.ExitTransition? exit);
method public java.util.Map<java.lang.String,java.lang.Object> transition(androidx.compose.animation.EnterTransition? enter, androidx.compose.animation.ExitTransition? exit);
field public static final androidx.navigation3.NavDisplay INSTANCE;
}
public final class NavDisplay_androidKt {
- method @androidx.compose.runtime.Composable public static <T> void NavDisplay(java.util.List<? extends T> backstack, optional androidx.compose.ui.Modifier modifier, optional java.util.List<? extends androidx.navigation3.NavLocalProvider> localProviders, optional androidx.compose.ui.Alignment contentAlignment, optional androidx.compose.animation.SizeTransform? sizeTransform, optional androidx.compose.animation.EnterTransition enterTransition, optional androidx.compose.animation.ExitTransition exitTransition, optional kotlin.jvm.functions.Function0<kotlin.Unit> onBack, kotlin.jvm.functions.Function1<? super T,? extends androidx.navigation3.NavEntry<? extends T>> entryProvider);
+ method @androidx.compose.runtime.Composable public static <T> void NavDisplay(java.util.List<? extends T> backstack, optional androidx.compose.ui.Modifier modifier, optional java.util.List<? extends androidx.navigation3.NavLocalProvider> localProviders, optional androidx.compose.ui.Alignment contentAlignment, optional androidx.compose.animation.SizeTransform? sizeTransform, optional androidx.compose.animation.EnterTransition enterTransition, optional androidx.compose.animation.ExitTransition exitTransition, optional androidx.compose.animation.EnterTransition popEnterTransition, optional androidx.compose.animation.ExitTransition popExitTransition, optional kotlin.jvm.functions.Function0<kotlin.Unit> onBack, kotlin.jvm.functions.Function1<? super T,? extends androidx.navigation3.NavEntry<? extends T>> entryProvider);
}
- public final class NavEntry<T> {
+ public class NavEntry<T> {
ctor public NavEntry(T key, optional java.util.Map<java.lang.String,?> featureMap, kotlin.jvm.functions.Function1<? super T,kotlin.Unit> content);
- method public kotlin.jvm.functions.Function1<T,kotlin.Unit> getContent();
- method public java.util.Map<java.lang.String,java.lang.Object> getFeatureMap();
- method public T getKey();
+ method public final kotlin.jvm.functions.Function1<T,kotlin.Unit> getContent();
+ method public final java.util.Map<java.lang.String,java.lang.Object> getFeatureMap();
+ method public final T getKey();
property public final kotlin.jvm.functions.Function1<T,kotlin.Unit> content;
property public final java.util.Map<java.lang.String,java.lang.Object> featureMap;
property public final T key;
diff --git a/navigation3/navigation3/samples/src/main/kotlin/androidx/navigation3/samples/NavDisplaySamples.kt b/navigation3/navigation3/samples/src/main/kotlin/androidx/navigation3/samples/NavDisplaySamples.kt
index d4728e8..64c39b0 100644
--- a/navigation3/navigation3/samples/src/main/kotlin/androidx/navigation3/samples/NavDisplaySamples.kt
+++ b/navigation3/navigation3/samples/src/main/kotlin/androidx/navigation3/samples/NavDisplaySamples.kt
@@ -21,6 +21,8 @@
import androidx.compose.animation.slideOutHorizontally
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.lifecycle.viewmodel.navigation3.ViewModelStoreNavLocalProvider
@@ -38,6 +40,7 @@
@Composable
fun BaseNav() {
val backStack = rememberMutableStateListOf(Profile)
+ val showDialog = remember { mutableStateOf(false) }
NavDisplay(
backstack = backStack,
localProviders = listOf(SavedStateNavLocalProvider, ViewModelStoreNavLocalProvider),
@@ -55,8 +58,8 @@
) {
Scrollable({ backStack.add(it) }) { backStack.removeLast() }
}
- entry<Dialog>(featureMap = NavDisplay.isDialog(true)) {
- DialogContent { backStack.removeLast() }
+ entry<DialogBase> {
+ DialogBase(onClick = { showDialog.value = true }) { backStack.removeLast() }
}
entry<Dashboard>(
NavDisplay.transition(slideInHorizontally { it }, slideOutHorizontally { it })
@@ -66,4 +69,7 @@
}
}
)
+ if (showDialog.value) {
+ DialogContent(onDismissRequest = { showDialog.value = false })
+ }
}
diff --git a/navigation3/navigation3/samples/src/main/kotlin/androidx/navigation3/samples/NavigationSamples.kt b/navigation3/navigation3/samples/src/main/kotlin/androidx/navigation3/samples/NavigationSamples.kt
index 3702017..6a5ec92 100644
--- a/navigation3/navigation3/samples/src/main/kotlin/androidx/navigation3/samples/NavigationSamples.kt
+++ b/navigation3/navigation3/samples/src/main/kotlin/androidx/navigation3/samples/NavigationSamples.kt
@@ -42,6 +42,7 @@
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
+import androidx.compose.ui.window.Dialog
import androidx.savedstate.serialization.decodeFromSavedState
import androidx.savedstate.serialization.encodeToSavedState
import kotlinx.serialization.InternalSerializationApi
@@ -66,7 +67,7 @@
}
@Serializable
-object Dialog {
+object DialogBase {
val resourceId: Int = R.string.dialog
}
@@ -85,7 +86,7 @@
Divider(color = Color.Black)
NavigateButton(stringResource(Scrollable.resourceId)) { navigateTo(Scrollable) }
Divider(color = Color.Black)
- NavigateButton(stringResource(Dialog.resourceId)) { navigateTo(Dialog) }
+ NavigateButton(stringResource(DialogBase.resourceId)) { navigateTo(DialogBase) }
Spacer(Modifier.weight(1f))
NavigateBackButton(onBack)
}
@@ -112,13 +113,24 @@
}
@Composable
-fun DialogContent(onBack: () -> Unit) {
- val dialogWidth = 300.dp
- val dialogHeight = 300.dp
- Column(Modifier.size(dialogWidth, dialogHeight).background(Color.White).padding(8.dp)) {
+fun DialogBase(onClick: () -> Unit, onBack: () -> Unit) {
+ Column(Modifier.fillMaxSize()) {
+ Text(stringResource(R.string.dialog))
+ Button(onClick = onClick) { Text("Show Dialog") }
+ Spacer(Modifier.weight(1f))
NavigateBackButton(onBack)
- LazyColumn(modifier = Modifier.weight(1f)) {
- items(phrases) { phrase -> Text(phrase, fontSize = 16.sp) }
+ }
+}
+
+@Composable
+fun DialogContent(onDismissRequest: () -> Unit) {
+ Dialog(onDismissRequest = onDismissRequest) {
+ val dialogWidth = 300.dp
+ val dialogHeight = 300.dp
+ Column(Modifier.size(dialogWidth, dialogHeight).background(Color.White).padding(8.dp)) {
+ LazyColumn(modifier = Modifier.weight(1f)) {
+ items(phrases) { phrase -> Text(phrase, fontSize = 16.sp) }
+ }
}
}
}
diff --git a/navigation3/navigation3/src/androidInstrumentedTest/kotlin/androidx/navigation3/AnimatedTest.kt b/navigation3/navigation3/src/androidInstrumentedTest/kotlin/androidx/navigation3/AnimatedTest.kt
index 6fb5955..be69875 100644
--- a/navigation3/navigation3/src/androidInstrumentedTest/kotlin/androidx/navigation3/AnimatedTest.kt
+++ b/navigation3/navigation3/src/androidInstrumentedTest/kotlin/androidx/navigation3/AnimatedTest.kt
@@ -138,7 +138,637 @@
composeTestRule.onNodeWithText(first).assertDoesNotExist()
composeTestRule.onNodeWithText(second).assertExists()
}
+
+ @Test
+ fun testPop() {
+ lateinit var backstack: MutableList<Any>
+ val testDuration = DEFAULT_TRANSITION_DURATION_MILLISECOND / 5
+ composeTestRule.setContent {
+ backstack = remember { mutableStateListOf(first, second) }
+ NavDisplay(backstack) {
+ when (it) {
+ first ->
+ NavEntry(
+ first,
+ featureMap =
+ NavDisplay.popTransition(
+ enter = fadeIn(tween(testDuration)),
+ exit = fadeOut(tween(testDuration))
+ )
+ ) {
+ Text(first)
+ }
+ second ->
+ NavEntry(
+ second,
+ ) {
+ Text(second)
+ }
+ else -> error("Invalid key passed")
+ }
+ }
+ }
+
+ composeTestRule.waitForIdle()
+ composeTestRule.onNodeWithText(second).assertIsDisplayed()
+ assertThat(backstack).containsExactly(first, second)
+
+ composeTestRule.mainClock.autoAdvance = false
+ composeTestRule.runOnIdle { backstack.removeAt(1) }
+
+ // advance by a duration that is much shorter than the default duration
+ // to ensure that the custom animation is used and has completed after this
+ composeTestRule.mainClock.advanceTimeBy((testDuration * 1.5).toLong())
+
+ composeTestRule.waitForIdle()
+ // pop to first
+ assertThat(backstack).containsExactly(first)
+ composeTestRule.onNodeWithText(first).assertIsDisplayed()
+ composeTestRule.onNodeWithText(second).assertDoesNotExist()
+ }
+
+ @Test
+ fun testPopMultiple() {
+ lateinit var backstack: MutableList<Any>
+ val testDuration = DEFAULT_TRANSITION_DURATION_MILLISECOND / 5
+ composeTestRule.setContent {
+ backstack = remember { mutableStateListOf(first, second, third) }
+ NavDisplay(backstack) {
+ when (it) {
+ first ->
+ NavEntry(
+ first,
+ featureMap =
+ NavDisplay.popTransition(
+ enter = fadeIn(tween(testDuration)),
+ exit = fadeOut(tween(testDuration))
+ )
+ ) {
+ Text(first)
+ }
+ second ->
+ NavEntry(
+ second,
+ ) {
+ Text(second)
+ }
+ third ->
+ NavEntry(
+ third,
+ ) {
+ Text(third)
+ }
+ else -> error("Invalid key passed")
+ }
+ }
+ }
+
+ composeTestRule.waitForIdle()
+ composeTestRule.onNodeWithText(third).assertIsDisplayed()
+ assertThat(backstack).containsExactly(first, second, third)
+
+ composeTestRule.mainClock.autoAdvance = false
+ composeTestRule.runOnIdle {
+ backstack.removeAt(2)
+ backstack.removeAt(1)
+ }
+
+ // advance by a duration that is much shorter than the default duration
+ // to ensure that the custom animation is used and has completed after this
+ composeTestRule.mainClock.advanceTimeBy((testDuration * 1.5).toLong())
+
+ composeTestRule.waitForIdle()
+ // pop to first
+ assertThat(backstack).containsExactly(first)
+ composeTestRule.onNodeWithText(first).assertIsDisplayed()
+ composeTestRule.onNodeWithText(second).assertDoesNotExist()
+ composeTestRule.onNodeWithText(third).assertDoesNotExist()
+ }
+
+ @Test
+ fun testPopNavigate() {
+ lateinit var backstack: MutableList<Any>
+ val testDuration = DEFAULT_TRANSITION_DURATION_MILLISECOND / 5
+ composeTestRule.setContent {
+ backstack = remember { mutableStateListOf(first, second) }
+ NavDisplay(backstack) {
+ when (it) {
+ first ->
+ NavEntry(
+ first,
+ ) {
+ Text(first)
+ }
+ second ->
+ NavEntry(
+ second,
+ ) {
+ Text(second)
+ }
+ third ->
+ NavEntry(
+ third,
+ featureMap =
+ NavDisplay.transition(
+ enter = fadeIn(tween(testDuration)),
+ exit = fadeOut(tween(testDuration))
+ )
+ ) {
+ Text(third)
+ }
+ else -> error("Invalid key passed")
+ }
+ }
+ }
+
+ composeTestRule.waitForIdle()
+ composeTestRule.onNodeWithText(second).assertIsDisplayed()
+ assertThat(backstack).containsExactly(first, second)
+
+ composeTestRule.mainClock.autoAdvance = false
+ composeTestRule.runOnIdle {
+ backstack.removeAt(1)
+ backstack.add(third)
+ }
+
+ // advance by a duration that is much shorter than the default duration
+ // to ensure that the custom animation is used and has completed after this
+ composeTestRule.mainClock.advanceTimeBy((testDuration * 1.5).toLong())
+ // not pop
+ composeTestRule.waitForIdle()
+ assertThat(backstack).containsExactly(first, third)
+ composeTestRule.onNodeWithText(first).assertDoesNotExist()
+ composeTestRule.onNodeWithText(second).assertDoesNotExist()
+ composeTestRule.onNodeWithText(third).assertIsDisplayed()
+ }
+
+ @Test
+ fun testCentrePop() {
+ lateinit var backstack: MutableList<Any>
+ val testDuration = DEFAULT_TRANSITION_DURATION_MILLISECOND / 5
+ composeTestRule.setContent {
+ backstack = remember { mutableStateListOf(first, second, third) }
+ NavDisplay(backstack) {
+ when (it) {
+ first ->
+ NavEntry(
+ first,
+ ) {
+ Text(first)
+ }
+ second ->
+ NavEntry(
+ second,
+ ) {
+ Text(second)
+ }
+ third ->
+ NavEntry(
+ third,
+ featureMap =
+ NavDisplay.transition(
+ enter = fadeIn(tween(testDuration)),
+ exit = fadeOut(tween(testDuration))
+ )
+ ) {
+ Text(third)
+ }
+ else -> error("Invalid key passed")
+ }
+ }
+ }
+
+ composeTestRule.waitForIdle()
+ composeTestRule.onNodeWithText(third).assertIsDisplayed()
+ assertThat(backstack).containsExactly(first, second, third)
+
+ composeTestRule.mainClock.autoAdvance = false
+ composeTestRule.runOnIdle { backstack.removeAt(1) }
+
+ // advance by a duration that is much shorter than the default duration
+ // to ensure that the custom animation is used and has completed after this
+ composeTestRule.mainClock.advanceTimeBy((testDuration * 1.5).toLong())
+ // not pop
+ composeTestRule.waitForIdle()
+ assertThat(backstack).containsExactly(first, third)
+ composeTestRule.onNodeWithText(first).assertDoesNotExist()
+ composeTestRule.onNodeWithText(second).assertDoesNotExist()
+ composeTestRule.onNodeWithText(third).assertIsDisplayed()
+ }
+
+ @Test
+ fun testCentreNavigate() {
+ lateinit var backstack: MutableList<Any>
+ val testDuration = DEFAULT_TRANSITION_DURATION_MILLISECOND / 5
+ composeTestRule.setContent {
+ backstack = remember { mutableStateListOf(first, third) }
+ NavDisplay(backstack) {
+ when (it) {
+ first ->
+ NavEntry(
+ first,
+ ) {
+ Text(first)
+ }
+ second ->
+ NavEntry(
+ second,
+ ) {
+ Text(second)
+ }
+ third ->
+ NavEntry(
+ third,
+ featureMap =
+ NavDisplay.transition(
+ enter = fadeIn(tween(testDuration)),
+ exit = fadeOut(tween(testDuration))
+ )
+ ) {
+ Text(third)
+ }
+ else -> error("Invalid key passed")
+ }
+ }
+ }
+
+ composeTestRule.waitForIdle()
+ composeTestRule.onNodeWithText(third).assertIsDisplayed()
+ assertThat(backstack).containsExactly(first, third)
+
+ composeTestRule.mainClock.autoAdvance = false
+ composeTestRule.runOnIdle { backstack.add(1, second) }
+
+ // advance by a duration that is much shorter than the default duration
+ // to ensure that the custom animation is used and has completed after this
+ composeTestRule.mainClock.advanceTimeBy((testDuration * 1.5).toLong())
+ // not pop
+ composeTestRule.waitForIdle()
+ assertThat(backstack).containsExactly(first, second, third)
+ composeTestRule.onNodeWithText(first).assertDoesNotExist()
+ composeTestRule.onNodeWithText(second).assertDoesNotExist()
+ composeTestRule.onNodeWithText(third).assertIsDisplayed()
+ }
+
+ @Test
+ fun testCentrePopAndEndPop() {
+ lateinit var backstack: MutableList<Any>
+ val testDuration = DEFAULT_TRANSITION_DURATION_MILLISECOND / 5
+ composeTestRule.setContent {
+ backstack = remember { mutableStateListOf(first, second, third, fourth) }
+ NavDisplay(backstack) {
+ when (it) {
+ first ->
+ NavEntry(
+ first,
+ ) {
+ Text(first)
+ }
+ second ->
+ NavEntry(
+ second,
+ ) {
+ Text(second)
+ }
+ third ->
+ NavEntry(
+ third,
+ NavDisplay.transition(
+ enter = fadeIn(tween(testDuration)),
+ exit = fadeOut(tween(testDuration))
+ )
+ ) {
+ Text(third)
+ }
+ fourth ->
+ NavEntry(
+ fourth,
+ ) {
+ Text(fourth)
+ }
+ else -> error("Invalid key passed")
+ }
+ }
+ }
+
+ composeTestRule.waitForIdle()
+ composeTestRule.onNodeWithText(fourth).assertIsDisplayed()
+ assertThat(backstack).containsExactly(first, second, third, fourth)
+
+ composeTestRule.mainClock.autoAdvance = false
+ composeTestRule.runOnIdle {
+ backstack.removeAt(3)
+ backstack.removeAt(1)
+ }
+
+ // advance by a duration that is much shorter than the default duration
+ // to ensure that the custom animation is used and has completed after this
+ composeTestRule.mainClock.advanceTimeBy((testDuration * 1.5).toLong())
+ // not pop
+ composeTestRule.waitForIdle()
+ assertThat(backstack).containsExactly(first, third)
+ composeTestRule.onNodeWithText(first).assertDoesNotExist()
+ composeTestRule.onNodeWithText(second).assertDoesNotExist()
+ composeTestRule.onNodeWithText(third).assertIsDisplayed()
+ composeTestRule.onNodeWithText(fourth).assertDoesNotExist()
+ }
+
+ @Test
+ fun testCentrePopAndEndNavigate() {
+ lateinit var backstack: MutableList<Any>
+ val testDuration = DEFAULT_TRANSITION_DURATION_MILLISECOND / 5
+ composeTestRule.setContent {
+ backstack = remember { mutableStateListOf(first, second, third) }
+ NavDisplay(backstack) {
+ when (it) {
+ first ->
+ NavEntry(
+ first,
+ ) {
+ Text(first)
+ }
+ second ->
+ NavEntry(
+ second,
+ ) {
+ Text(second)
+ }
+ third ->
+ NavEntry(
+ third,
+ ) {
+ Text(third)
+ }
+ fourth ->
+ NavEntry(
+ fourth,
+ featureMap =
+ NavDisplay.transition(
+ enter = fadeIn(tween(testDuration)),
+ exit = fadeOut(tween(testDuration))
+ )
+ ) {
+ Text(fourth)
+ }
+ else -> error("Invalid key passed")
+ }
+ }
+ }
+
+ composeTestRule.waitForIdle()
+ composeTestRule.onNodeWithText(third).assertIsDisplayed()
+ assertThat(backstack).containsExactly(first, second, third)
+
+ composeTestRule.mainClock.autoAdvance = false
+ composeTestRule.runOnIdle {
+ backstack.removeAt(1)
+ backstack.add(fourth)
+ }
+
+ // advance by a duration that is much shorter than the default duration
+ // to ensure that the custom animation is used and has completed after this
+ composeTestRule.mainClock.advanceTimeBy((testDuration * 1.5).toLong())
+
+ composeTestRule.waitForIdle()
+ assertThat(backstack).containsExactly(first, third, fourth)
+ composeTestRule.onNodeWithText(first).assertDoesNotExist()
+ composeTestRule.onNodeWithText(third).assertDoesNotExist()
+ composeTestRule.onNodeWithText(fourth).assertIsDisplayed()
+ }
+
+ @Test
+ fun testCentreNavigateAndEndPop() {
+ lateinit var backstack: MutableList<Any>
+ val testDuration = DEFAULT_TRANSITION_DURATION_MILLISECOND / 5
+ composeTestRule.setContent {
+ backstack = remember { mutableStateListOf(first, third, fourth) }
+ NavDisplay(backstack) {
+ when (it) {
+ first ->
+ NavEntry(
+ first,
+ ) {
+ Text(first)
+ }
+ second ->
+ NavEntry(
+ second,
+ ) {
+ Text(second)
+ }
+ third ->
+ NavEntry(
+ third,
+ featureMap =
+ NavDisplay.transition(
+ enter = fadeIn(tween(testDuration)),
+ exit = fadeOut(tween(testDuration))
+ )
+ ) {
+ Text(third)
+ }
+ fourth ->
+ NavEntry(
+ fourth,
+ ) {
+ Text(fourth)
+ }
+ else -> error("Invalid key passed")
+ }
+ }
+ }
+
+ composeTestRule.waitForIdle()
+ composeTestRule.onNodeWithText(fourth).assertIsDisplayed()
+ assertThat(backstack).containsExactly(first, third, fourth)
+ composeTestRule.mainClock.autoAdvance = false
+ composeTestRule.runOnIdle {
+ backstack.removeAt(2)
+ backstack.add(1, second)
+ }
+
+ // advance by a duration that is much shorter than the default duration
+ // to ensure that the custom animation is used and has completed after this
+ composeTestRule.mainClock.advanceTimeBy((testDuration * 1.5).toLong())
+ // not pop
+ composeTestRule.waitForIdle()
+ assertThat(backstack).containsExactly(first, second, third)
+ composeTestRule.onNodeWithText(first).assertDoesNotExist()
+ composeTestRule.onNodeWithText(second).assertDoesNotExist()
+ composeTestRule.onNodeWithText(third).assertIsDisplayed()
+ composeTestRule.onNodeWithText(fourth).assertDoesNotExist()
+ }
+
+ @Test
+ fun testCentreNavigateAndEndNavigate() {
+ lateinit var backstack: MutableList<Any>
+ val testDuration = DEFAULT_TRANSITION_DURATION_MILLISECOND / 5
+ composeTestRule.setContent {
+ backstack = remember { mutableStateListOf(first, third) }
+ NavDisplay(backstack) {
+ when (it) {
+ first ->
+ NavEntry(
+ first,
+ ) {
+ Text(first)
+ }
+ second ->
+ NavEntry(
+ second,
+ ) {
+ Text(second)
+ }
+ third ->
+ NavEntry(
+ third,
+ ) {
+ Text(third)
+ }
+ fourth ->
+ NavEntry(
+ fourth,
+ NavDisplay.transition(
+ enter = fadeIn(tween(testDuration)),
+ exit = fadeOut(tween(testDuration))
+ )
+ ) {
+ Text(fourth)
+ }
+ else -> error("Invalid key passed")
+ }
+ }
+ }
+
+ composeTestRule.waitForIdle()
+ composeTestRule.onNodeWithText(third).assertIsDisplayed()
+ assertThat(backstack).containsExactly(first, third)
+
+ composeTestRule.mainClock.autoAdvance = false
+ composeTestRule.runOnIdle {
+ backstack.add(1, second)
+ backstack.add(fourth)
+ }
+
+ // advance by a duration that is much shorter than the default duration
+ // to ensure that the custom animation is used and has completed after this
+ composeTestRule.mainClock.advanceTimeBy((testDuration * 1.5).toLong())
+ // not pop
+ composeTestRule.waitForIdle()
+ assertThat(backstack).containsExactly(first, second, third, fourth)
+ composeTestRule.onNodeWithText(first).assertDoesNotExist()
+ composeTestRule.onNodeWithText(second).assertDoesNotExist()
+ composeTestRule.onNodeWithText(third).assertDoesNotExist()
+ composeTestRule.onNodeWithText(fourth).assertIsDisplayed()
+ }
+
+ @Test
+ fun testSameStack() {
+ lateinit var backstack: MutableList<Any>
+ val testDuration = DEFAULT_TRANSITION_DURATION_MILLISECOND / 5
+ composeTestRule.setContent {
+ backstack = remember { mutableStateListOf(first, second) }
+ NavDisplay(backstack) {
+ when (it) {
+ first ->
+ NavEntry(
+ first,
+ ) {
+ Text(first)
+ }
+ second ->
+ NavEntry(
+ second,
+ NavDisplay.transition(
+ enter = fadeIn(tween(testDuration)),
+ exit = fadeOut(tween(testDuration))
+ )
+ ) {
+ Text(second)
+ }
+ else -> error("Invalid key passed")
+ }
+ }
+ }
+
+ composeTestRule.waitForIdle()
+ composeTestRule.onNodeWithText(second).assertIsDisplayed()
+ assertThat(backstack).containsExactly(first, second)
+
+ composeTestRule.mainClock.autoAdvance = false
+ composeTestRule.runOnIdle {
+ backstack.removeAt(1)
+ backstack.add(second)
+ }
+
+ // advance by a duration that is much shorter than the default duration
+ // to ensure that the custom animation is used and has completed after this
+ composeTestRule.mainClock.advanceTimeBy((testDuration * 1.5).toLong())
+ // not pop
+ composeTestRule.waitForIdle()
+ assertThat(backstack).containsExactly(first, second)
+ composeTestRule.onNodeWithText(first).assertDoesNotExist()
+ composeTestRule.onNodeWithText(second).assertIsDisplayed()
+ }
+
+ @Test
+ fun testDuplicateLastEntry() {
+ lateinit var backstack: MutableList<Any>
+ val testDuration = DEFAULT_TRANSITION_DURATION_MILLISECOND / 5
+ composeTestRule.setContent {
+ backstack = remember { mutableStateListOf(first, second, third) }
+ NavDisplay(backstack) {
+ when (it) {
+ first ->
+ NavEntry(
+ first,
+ ) {
+ Text(first)
+ }
+ second ->
+ NavEntry(
+ second,
+ featureMap =
+ NavDisplay.transition(
+ enter = fadeIn(tween(testDuration)),
+ exit = fadeOut(tween(testDuration))
+ )
+ ) {
+ Text(second)
+ }
+ third ->
+ NavEntry(
+ third,
+ ) {
+ Text(third)
+ }
+ else -> error("Invalid key passed")
+ }
+ }
+ }
+
+ composeTestRule.waitForIdle()
+ composeTestRule.onNodeWithText(first).assertDoesNotExist()
+ composeTestRule.onNodeWithText(second).assertDoesNotExist()
+ composeTestRule.onNodeWithText(third).assertIsDisplayed()
+ assertThat(backstack).containsExactly(first, second, third)
+
+ composeTestRule.mainClock.autoAdvance = false
+ composeTestRule.runOnIdle { backstack.add(second) }
+
+ // advance by a duration that is much shorter than the default duration
+ // to ensure that the custom animation is used and has completed after this
+ composeTestRule.mainClock.advanceTimeBy((testDuration * 1.5).toLong())
+ // not pop
+ composeTestRule.waitForIdle()
+ assertThat(backstack).containsExactly(first, second, third, second)
+ composeTestRule.onNodeWithText(first).assertDoesNotExist()
+ composeTestRule.onNodeWithText(third).assertDoesNotExist()
+ composeTestRule.onNodeWithText(second).assertIsDisplayed()
+ }
}
private const val first = "first"
private const val second = "second"
+private const val third = "third"
+private const val fourth = "fourth"
diff --git a/navigation3/navigation3/src/androidInstrumentedTest/kotlin/androidx/navigation3/NavDisplayTest.kt b/navigation3/navigation3/src/androidInstrumentedTest/kotlin/androidx/navigation3/NavDisplayTest.kt
index 614eb88..3342fbf 100644
--- a/navigation3/navigation3/src/androidInstrumentedTest/kotlin/androidx/navigation3/NavDisplayTest.kt
+++ b/navigation3/navigation3/src/androidInstrumentedTest/kotlin/androidx/navigation3/NavDisplayTest.kt
@@ -18,6 +18,7 @@
import androidx.activity.OnBackPressedDispatcher
import androidx.activity.compose.LocalOnBackPressedDispatcherOwner
+import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateListOf
@@ -28,6 +29,8 @@
import androidx.compose.ui.test.isDisplayed
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
+import androidx.compose.ui.window.Dialog
import androidx.kruth.assertThat
import androidx.savedstate.SavedStateRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -78,20 +81,28 @@
fun testDialog() {
lateinit var backstack: MutableList<Any>
composeTestRule.setContent {
+ var showDialog = remember { mutableStateOf(false) }
backstack = remember { mutableStateListOf(first) }
NavDisplay(backstack = backstack) {
when (it) {
- first -> NavEntry(first) { Text(first) }
- second -> NavEntry(second, NavDisplay.isDialog(true)) { Text(second) }
+ first ->
+ NavEntry(first) {
+ Button(onClick = { showDialog.value = true }) { Text(first) }
+ }
else -> error("Invalid key passed")
}
}
+ if (showDialog.value) {
+ Dialog(onDismissRequest = {}) { Text(second) }
+ }
}
assertThat(composeTestRule.onNodeWithText(first).isDisplayed()).isTrue()
- composeTestRule.runOnIdle { backstack.add(second) }
+ composeTestRule.waitForIdle()
+ composeTestRule.onNodeWithText(first).performClick()
+ composeTestRule.waitForIdle()
// Both first and second should be showing if we are on a dialog.
assertThat(composeTestRule.onNodeWithText(first).isDisplayed()).isTrue()
assertThat(composeTestRule.onNodeWithText(second).isDisplayed()).isTrue()
diff --git a/navigation3/navigation3/src/androidMain/kotlin/androidx/navigation3/NavDisplay.android.kt b/navigation3/navigation3/src/androidMain/kotlin/androidx/navigation3/NavDisplay.android.kt
index fdc1e24..fbfa0d7 100644
--- a/navigation3/navigation3/src/androidMain/kotlin/androidx/navigation3/NavDisplay.android.kt
+++ b/navigation3/navigation3/src/androidMain/kotlin/androidx/navigation3/NavDisplay.android.kt
@@ -26,10 +26,14 @@
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.window.Dialog
import androidx.navigation3.NavDisplay.DEFAULT_TRANSITION_DURATION_MILLISECOND
+import androidx.navigation3.NavDisplay.ENTER_TRANSITION_KEY
+import androidx.navigation3.NavDisplay.EXIT_TRANSITION_KEY
+import androidx.navigation3.NavDisplay.POP_ENTER_TRANSITION_KEY
+import androidx.navigation3.NavDisplay.POP_EXIT_TRANSITION_KEY
/** Object that indicates the features that can be handled by the [NavDisplay] */
public object NavDisplay {
@@ -42,15 +46,17 @@
else mapOf(ENTER_TRANSITION_KEY to enter, EXIT_TRANSITION_KEY to exit)
/**
- * Function to be called on the [NavEntry.featureMap] to notify the [NavDisplay] that the
- * content should be displayed inside of a [Dialog]
+ * Function to be called on the [NavEntry.featureMap] to notify the [NavDisplay] that, when
+ * popping from backstack, the content should be animated using the provided transitions.
*/
- public fun isDialog(boolean: Boolean): Map<String, Any> =
- if (!boolean) emptyMap() else mapOf(DIALOG_KEY to true)
+ public fun popTransition(enter: EnterTransition?, exit: ExitTransition?): Map<String, Any> =
+ if (enter == null || exit == null) emptyMap()
+ else mapOf(POP_ENTER_TRANSITION_KEY to enter, POP_EXIT_TRANSITION_KEY to exit)
internal const val ENTER_TRANSITION_KEY = "enterTransition"
internal const val EXIT_TRANSITION_KEY = "exitTransition"
- internal const val DIALOG_KEY = "dialog"
+ internal const val POP_ENTER_TRANSITION_KEY = "popEnterTransition"
+ internal const val POP_EXIT_TRANSITION_KEY = "popExitTransition"
internal const val DEFAULT_TRANSITION_DURATION_MILLISECOND = 700
}
@@ -58,19 +64,22 @@
* Display for Composable content that displays a single pane of content at a time, but can move
* that content in and out with customized transitions.
*
- * The NavDisplay displays the content associated with the last key on the back stack in most
- * circumstances. If that content wants to be displayed as a dialog, as communicated by adding
- * [NavDisplay.isDialog] to a [NavEntry.featureMap], then the last key's content is a dialog and the
- * second to last key is a displayed in the background.
+ * The NavDisplay displays the content associated with the last key on the back stack.
*
* @param backstack the collection of keys that represents the state that needs to be handled
* @param localProviders list of [NavLocalProvider] to add information to the provided entriess
* @param modifier the modifier to be applied to the layout.
* @param contentAlignment The [Alignment] of the [AnimatedContent]
- * @param enterTransition Default [EnterTransition] for all [NavEntry]s. Can be overridden
+ * @param enterTransition Default [EnterTransition] when navigating to [NavEntry]s. Can be
+ * overridden individually for each [NavEntry] by passing in the entry's transitions through
+ * [NavEntry.featureMap].
+ * @param exitTransition Default [ExitTransition] when navigating to [NavEntry]s. Can be overridden
* individually for each [NavEntry] by passing in the entry's transitions through
* [NavEntry.featureMap].
- * @param exitTransition Default [ExitTransition] for all [NavEntry]s. Can be overridden
+ * @param popEnterTransition Default [EnterTransition] when popping [NavEntry]s. Can be overridden
+ * individually for each [NavEntry] by passing in the entry's transitions through
+ * [NavEntry.featureMap].
+ * @param popExitTransition Default [ExitTransition] when popping [NavEntry]s. Can be overridden
* individually for each [NavEntry] by passing in the entry's transitions through
* [NavEntry.featureMap].
* @param onBack a callback for handling system back presses
@@ -98,6 +107,20 @@
DEFAULT_TRANSITION_DURATION_MILLISECOND,
)
),
+ popEnterTransition: EnterTransition =
+ fadeIn(
+ animationSpec =
+ tween(
+ DEFAULT_TRANSITION_DURATION_MILLISECOND,
+ )
+ ),
+ popExitTransition: ExitTransition =
+ fadeOut(
+ animationSpec =
+ tween(
+ DEFAULT_TRANSITION_DURATION_MILLISECOND,
+ )
+ ),
onBack: () -> Unit = { if (backstack is MutableList) backstack.removeAt(backstack.size - 1) },
entryProvider: (key: T) -> NavEntry<out T>
) {
@@ -106,32 +129,30 @@
val wrapperManager: NavWrapperManager = rememberNavWrapperManager(localProviders)
BackHandler(backstack.size > 1, onBack)
wrapperManager.PrepareBackStack(backStack = backstack)
- val key = backstack.last()
- val entry = entryProvider.invoke(key)
- // Incoming entry defines transitions, otherwise it uses default transitions from NavDisplay
+ // Make a copy shallow copy so that transition.currentState and transition.targetState are
+ // different backstack instances. This ensures currentState reflects the old backstack when
+ // the backstack (targetState) is updated.
+ val newStack = backstack.toList()
+ val entry = entryProvider.invoke(newStack.last())
+
+ val transition = updateTransition(targetState = newStack, label = newStack.toString())
+ val isPop = isPop(transition.currentState, newStack)
+ // Incoming entry defines transitions, otherwise it uses default transitions from
+ // NavDisplay
val finalEnterTransition =
- entry.featureMap[NavDisplay.ENTER_TRANSITION_KEY] as? EnterTransition ?: enterTransition
- val finalExitTransition =
- entry.featureMap[NavDisplay.EXIT_TRANSITION_KEY] as? ExitTransition ?: exitTransition
-
- val isDialog = entry.featureMap[NavDisplay.DIALOG_KEY] == true
-
- // if there is a dialog, we should create a transition with the next to last entry instead.
- val transition =
- if (isDialog) {
- if (backstack.size > 1) {
- val previousKey = backstack[backstack.size - 2]
- val previousEntry = entryProvider.invoke(previousKey)
- updateTransition(targetState = previousEntry, label = previousKey.toString())
- } else {
- null
- }
+ if (isPop) {
+ entry.featureMap[POP_ENTER_TRANSITION_KEY] as? EnterTransition ?: popEnterTransition
} else {
- updateTransition(targetState = entry, label = key.toString())
+ entry.featureMap[ENTER_TRANSITION_KEY] as? EnterTransition ?: enterTransition
}
-
- transition?.AnimatedContent(
+ val finalExitTransition =
+ if (isPop) {
+ entry.featureMap[POP_EXIT_TRANSITION_KEY] as? ExitTransition ?: popExitTransition
+ } else {
+ entry.featureMap[EXIT_TRANSITION_KEY] as? ExitTransition ?: exitTransition
+ }
+ transition.AnimatedContent(
modifier = modifier,
transitionSpec = {
ContentTransform(
@@ -141,12 +162,25 @@
)
},
contentAlignment = contentAlignment,
- contentKey = { it.key }
- ) { innerEntry ->
- wrapperManager.ContentForEntry(innerEntry)
+ contentKey = { it.lastOrNull() }
+ ) { innerStack ->
+ // innerStack is not modified but List is still considered unstable in the world of
+ // compose - compose doesn't know for sure the key is the stable so it recomposes
+ // the content, unless we remember the key
+ val record = remember(innerStack.last()) { entryProvider.invoke(innerStack.last()) }
+ wrapperManager.ContentForEntry(record)
}
+}
- if (isDialog) {
- Dialog(onBack) { wrapperManager.ContentForEntry(entry) }
- }
+private fun <T : Any> isPop(oldBackStack: List<T>, newBackStack: List<T>): Boolean {
+ // entire stack replaced
+ if (oldBackStack.first() != newBackStack.first()) return false
+ // navigated
+ if (newBackStack.size > oldBackStack.size) return false
+
+ val divergingIndex =
+ newBackStack.indices.firstOrNull { index -> newBackStack[index] != oldBackStack[index] }
+ // if newBackStack never diverged from oldBackStack, then it is a clean subset of the oldStack
+ // and is a pop
+ return divergingIndex == null
}
diff --git a/navigation3/navigation3/src/commonMain/kotlin/androidx/navigation3/NavEntry.kt b/navigation3/navigation3/src/commonMain/kotlin/androidx/navigation3/NavEntry.kt
index e5d19d9..e8fc674 100644
--- a/navigation3/navigation3/src/commonMain/kotlin/androidx/navigation3/NavEntry.kt
+++ b/navigation3/navigation3/src/commonMain/kotlin/androidx/navigation3/NavEntry.kt
@@ -26,7 +26,7 @@
* @param featureMap map of the available features from a display
* @param content content for this entry to be displayed when this entry is active
*/
-public class NavEntry<T : Any>(
+public open class NavEntry<T : Any>(
public val key: T,
public val featureMap: Map<String, Any> = emptyMap(),
public val content: @Composable (T) -> Unit,
diff --git a/paging/paging-compose/src/androidInstrumentedTest/kotlin/androidx/paging/compose/LazyPagingItemsTest.kt b/paging/paging-compose/src/androidInstrumentedTest/kotlin/androidx/paging/compose/LazyPagingItemsTest.kt
index dc64222..edf4bf1 100644
--- a/paging/paging-compose/src/androidInstrumentedTest/kotlin/androidx/paging/compose/LazyPagingItemsTest.kt
+++ b/paging/paging-compose/src/androidInstrumentedTest/kotlin/androidx/paging/compose/LazyPagingItemsTest.kt
@@ -263,8 +263,8 @@
}
}
- val idMinus1 = rule.onNodeWithTag("-1").semanticsId()
- val id0 = rule.onNodeWithTag("0").semanticsId()
+ val idMinus1 = rule.onNodeWithTag("-1").fetchSemanticsNode().id
+ val id0 = rule.onNodeWithTag("0").fetchSemanticsNode().id
rule.runOnIdle {
runBlocking {
state.scrollToItem(2)
@@ -327,8 +327,8 @@
rule.waitUntil { loadedItem6 }
- val idMinus1 = rule.onNodeWithTag("-1").semanticsId()
- val id0 = rule.onNodeWithTag("0").semanticsId()
+ val idMinus1 = rule.onNodeWithTag("-1").fetchSemanticsNode().id
+ val id0 = rule.onNodeWithTag("0").fetchSemanticsNode().id
rule.runOnIdle {
runBlocking {
@@ -381,8 +381,8 @@
}
}
- val idMinus1 = rule.onNodeWithTag("-1").semanticsId()
- val id0 = rule.onNodeWithTag("0").semanticsId()
+ val idMinus1 = rule.onNodeWithTag("-1").fetchSemanticsNode().id
+ val id0 = rule.onNodeWithTag("0").fetchSemanticsNode().id
rule.runOnIdle {
runBlocking {
diff --git a/pdf/integration-tests/testapp/src/main/AndroidManifest.xml b/pdf/integration-tests/testapp/src/main/AndroidManifest.xml
index fe5943bc..d0228a0 100644
--- a/pdf/integration-tests/testapp/src/main/AndroidManifest.xml
+++ b/pdf/integration-tests/testapp/src/main/AndroidManifest.xml
@@ -27,7 +27,6 @@
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
- android:configChanges="orientation|screenSize"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
diff --git a/pdf/pdf-viewer/src/androidTest/kotlin/androidx/pdf/view/PdfViewZoomStateTest.kt b/pdf/pdf-viewer/src/androidTest/kotlin/androidx/pdf/view/PdfViewZoomStateTest.kt
new file mode 100644
index 0000000..c04af8b
--- /dev/null
+++ b/pdf/pdf-viewer/src/androidTest/kotlin/androidx/pdf/view/PdfViewZoomStateTest.kt
@@ -0,0 +1,145 @@
+/*
+ * 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.pdf.view
+
+import android.graphics.Point
+import android.graphics.PointF
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import androidx.test.core.app.ActivityScenario
+import androidx.test.espresso.Espresso.onView
+import androidx.test.espresso.matcher.ViewMatchers.withId
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@LargeTest
+class PdfViewZoomStateTest {
+
+ private lateinit var activityScenario: ActivityScenario<PdfViewTestActivity>
+
+ @Before
+ fun setup() {
+ activityScenario = ActivityScenario.launch(PdfViewTestActivity::class.java)
+ }
+
+ @After
+ fun tearDown() {
+ PdfViewTestActivity.onCreateCallback = {}
+ activityScenario.close()
+ }
+
+ private fun setupPdfView(fakePdfDocument: FakePdfDocument?) {
+ PdfViewTestActivity.onCreateCallback = { activity ->
+ val container = FrameLayout(activity)
+ val pdfView =
+ PdfView(activity).apply {
+ pdfDocument = fakePdfDocument
+ id = PDF_VIEW_ID
+ minZoom = 0.5f
+ maxZoom = 5.0f
+ }
+ container.addView(pdfView, ViewGroup.LayoutParams(PAGE_WIDTH, PAGE_HEIGHT * 2))
+ activity.setContentView(container)
+ }
+ }
+
+ @Test
+ fun testInitialZoom_fitWidth() = runTest {
+ val fakePdfDocument = FakePdfDocument(List(20) { Point(PAGE_WIDTH, PAGE_HEIGHT) })
+
+ setupPdfView(fakePdfDocument)
+
+ with(ActivityScenario.launch(PdfViewTestActivity::class.java)) {
+ fakePdfDocument.waitForLayout(untilPage = 3)
+ onView(withId(PDF_VIEW_ID)).check { view, noViewFoundException ->
+ view ?: throw noViewFoundException
+ val pdfView = view as PdfView
+ assertThat(pdfView.isInitialZoomDone).isTrue()
+ assertThat(pdfView.zoom).isWithin(0.01f).of(1.0f)
+ }
+ }
+ }
+
+ @Test
+ fun testGetDefaultZoom_fitWidth() = runTest {
+ val fakePdfDocument = FakePdfDocument(List(20) { Point(PAGE_WIDTH, PAGE_HEIGHT) })
+
+ setupPdfView(fakePdfDocument)
+ activityScenario.recreate()
+
+ activityScenario.onActivity { activity ->
+ val pdfView = activity.findViewById<PdfView>(PDF_VIEW_ID)
+ pdfView.zoom = 2.0f
+ val expectedZoom = 1.0f
+ val actualZoom = pdfView.getDefaultZoom()
+ assertThat(actualZoom).isWithin(0.01f).of(expectedZoom)
+ }
+ }
+
+ @Test
+ fun testRestoreUserZoomAndScrollPosition() = runTest {
+ val fakePdfDocument = FakePdfDocument(List(20) { Point(PAGE_WIDTH, PAGE_HEIGHT) })
+ val savedZoom = 2.5f
+ val savedScrollPosition = PointF(100f, PAGE_HEIGHT * 1f / savedZoom)
+
+ setupPdfView(fakePdfDocument)
+ activityScenario.recreate()
+
+ activityScenario.onActivity { activity ->
+ val pdfView = activity.findViewById<PdfView>(PDF_VIEW_ID)
+ pdfView.zoom = savedZoom
+ pdfView.scrollTo(
+ (savedScrollPosition.x * savedZoom - pdfView.viewportWidth / 2f).toInt(),
+ (savedScrollPosition.y * savedZoom - pdfView.viewportHeight / 2f).toInt()
+ )
+ pdfView.isInitialZoomDone = true
+ }
+
+ activityScenario.recreate()
+
+ onView(withId(PDF_VIEW_ID)).check { view, _ ->
+ view as PdfView
+ assertThat(view.zoom).isWithin(0.01f).of(savedZoom)
+ val expectedScrollX =
+ (savedScrollPosition.x * savedZoom - view.viewportWidth / 2f).toInt()
+ val expectedScrollY =
+ (savedScrollPosition.y * savedZoom - view.viewportHeight / 2f).toInt()
+ assertThat(view.scrollX).isEqualTo(expectedScrollX)
+ assertThat(view.scrollY).isEqualTo(expectedScrollY)
+ }
+ }
+}
+
+/** Arbitrary fixed ID for PdfView */
+private const val PDF_VIEW_ID = 123456789
+private const val PAGE_WIDTH = 500
+private const val PAGE_HEIGHT = 800
+
+/** The height of the viewport, minus padding */
+val PdfView.viewportHeight: Int
+ get() = bottom - top - paddingBottom - paddingTop
+
+/** The width of the viewport, minus padding */
+val PdfView.viewportWidth: Int
+ get() = right - left - paddingRight - paddingLeft
diff --git a/pdf/pdf-viewer/src/main/kotlin/androidx/pdf/view/AccessibilityPageHelper.kt b/pdf/pdf-viewer/src/main/kotlin/androidx/pdf/view/AccessibilityPageHelper.kt
index 4f23256..1850e02 100644
--- a/pdf/pdf-viewer/src/main/kotlin/androidx/pdf/view/AccessibilityPageHelper.kt
+++ b/pdf/pdf-viewer/src/main/kotlin/androidx/pdf/view/AccessibilityPageHelper.kt
@@ -41,8 +41,8 @@
private val pageManager: PageManager
) : ExploreByTouchHelper(pdfView) {
- private var gotoLinks: MutableList<PdfPageGotoLinkContent> = mutableListOf()
- private var urlLinks: MutableList<PdfPageLinkContent> = mutableListOf()
+ private val gotoLinks: MutableMap<Int, LinkWrapper<PdfPageGotoLinkContent>> = mutableMapOf()
+ private val urlLinks: MutableMap<Int, LinkWrapper<PdfPageLinkContent>> = mutableMapOf()
private val totalPages = pdfView.pdfDocument?.pageCount ?: 0
private var isLinksLoaded = false
@@ -56,19 +56,21 @@
loadPageLinks()
}
- // Check if the coordinates fall within any of the gotoLinks bounds
- gotoLinks.forEachIndexed { index, gotoLink ->
- if (gotoLink.bounds.any { it.contains(contentX.toFloat(), contentY.toFloat()) }) {
- return index + totalPages
+ gotoLinks.entries
+ .find { (_, wrapper) ->
+ wrapper.linkBounds.contains(contentX.toFloat(), contentY.toFloat())
}
- }
+ ?.let {
+ return it.key
+ }
- // Check if the coordinates fall within any of the urlLinks bounds
- urlLinks.forEachIndexed { index, urlLink ->
- if (urlLink.bounds.any { it.contains(contentX.toFloat(), contentY.toFloat()) }) {
- return index + totalPages + gotoLinks.size
+ urlLinks.entries
+ .find { (_, wrapper) ->
+ wrapper.linkBounds.contains(contentX.toFloat(), contentY.toFloat())
}
- }
+ ?.let {
+ return it.key
+ }
// Check if the coordinates fall within the visible page bounds
return (visiblePages.lower..visiblePages.upper).firstOrNull { page ->
@@ -80,14 +82,12 @@
public override fun getVisibleVirtualViews(virtualViewIds: MutableList<Int>) {
val visiblePages = pageLayoutManager.visiblePages.value
- virtualViewIds.addAll(visiblePages.lower..visiblePages.upper)
-
loadPageLinks()
- gotoLinks.forEachIndexed { index, _ -> virtualViewIds.add(totalPages + index) }
-
- urlLinks.forEachIndexed { index, _ ->
- virtualViewIds.add(totalPages + gotoLinks.size + index)
+ virtualViewIds.apply {
+ addAll(visiblePages.lower..visiblePages.upper)
+ addAll(gotoLinks.keys)
+ addAll(urlLinks.keys)
}
}
@@ -97,10 +97,13 @@
) {
if (!isLinksLoaded) loadPageLinks()
- if (virtualViewId < totalPages) {
- populateNodeForPage(virtualViewId, node)
- } else {
- populateNodeForLink(virtualViewId, node)
+ when {
+ virtualViewId < totalPages -> populateNodeForPage(virtualViewId, node)
+ else -> {
+ // Populate node for GoTo links and URL links
+ gotoLinks[virtualViewId]?.let { populateGotoLinkNode(it, node) }
+ urlLinks[virtualViewId]?.let { populateUrlLinkNode(it, node) }
+ }
}
}
@@ -134,40 +137,61 @@
}
}
- private fun populateNodeForLink(virtualViewId: Int, node: AccessibilityNodeInfoCompat) {
- val adjustedId = virtualViewId - totalPages
- if (adjustedId < gotoLinks.size) {
- populateGotoLinkNode(adjustedId, node)
- } else {
- populateUrlLinkNode(adjustedId - gotoLinks.size, node)
- }
- }
+ private fun populateGotoLinkNode(
+ linkWrapper: LinkWrapper<PdfPageGotoLinkContent>,
+ node: AccessibilityNodeInfoCompat
+ ) {
+ val bounds = scalePageBounds(linkWrapper.linkBounds, pdfView.zoom)
- private fun populateGotoLinkNode(linkIndex: Int, node: AccessibilityNodeInfoCompat) {
- val gotoLink = gotoLinks[linkIndex]
- val bounds = scalePageBounds(gotoLink.bounds.first(), pdfView.zoom)
node.apply {
contentDescription =
pdfView.context.getString(
R.string.desc_goto_link,
- gotoLink.destination.pageNumber + 1
+ linkWrapper.content.destination.pageNumber + 1
)
- setBoundsInScreenFromBoundsInParent(node, bounds)
+ setBoundsInScreenFromBoundsInParent(this, bounds)
isFocusable = true
}
}
- private fun populateUrlLinkNode(linkIndex: Int, node: AccessibilityNodeInfoCompat) {
- val urlLink = urlLinks[linkIndex]
- val bounds = scalePageBounds(urlLink.bounds.first(), pdfView.zoom)
+ private fun populateUrlLinkNode(
+ linkWrapper: LinkWrapper<PdfPageLinkContent>,
+ node: AccessibilityNodeInfoCompat
+ ) {
+ val bounds = scalePageBounds(linkWrapper.linkBounds, pdfView.zoom)
node.apply {
contentDescription =
- ExternalLinks.getDescription(urlLink.uri.toString(), pdfView.context)
+ ExternalLinks.getDescription(linkWrapper.content.uri.toString(), pdfView.context)
setBoundsInScreenFromBoundsInParent(node, bounds)
isFocusable = true
}
}
+ /**
+ * Calculates the adjusted bounds of a link relative to the full content of the PDF.
+ *
+ * @param pageNumber The 0-indexed page number.
+ * @param linkBounds The bounds of the link on the page.
+ * @return The adjusted bounds in the content coordinate system.
+ */
+ fun getLinkBounds(pageNumber: Int, linkBounds: RectF): RectF {
+ val pageBounds =
+ pageLayoutManager.getPageLocation(pageNumber, pdfView.getVisibleAreaInContentCoords())
+ return RectF(
+ linkBounds.left + pageBounds.left,
+ linkBounds.top + pageBounds.top,
+ linkBounds.right + pageBounds.left,
+ linkBounds.bottom + pageBounds.top
+ )
+ }
+
+ /**
+ * Scales the bounds of a page based on the current zoom level.
+ *
+ * @param bounds The original bounds to scale.
+ * @param zoom The zoom level.
+ * @return The scaled bounds as a Rect.
+ */
@VisibleForTesting
fun scalePageBounds(bounds: RectF, zoom: Float): Rect {
return Rect(
@@ -178,6 +202,12 @@
)
}
+ /**
+ * Loads the links for the visible pages.
+ *
+ * This method fetches the GoTo links and URL links for the currently visible pages and stores
+ * them in the corresponding maps.
+ */
fun loadPageLinks() {
val visiblePages = pageLayoutManager.visiblePages.value
@@ -185,10 +215,20 @@
gotoLinks.clear()
urlLinks.clear()
+ var cumulativeId = totalPages
+
(visiblePages.lower..visiblePages.upper).forEach { pageIndex ->
pageManager.pages[pageIndex]?.links?.let { links ->
- links.gotoLinks.let { gotoLinks.addAll(it) }
- links.externalLinks.let { urlLinks.addAll(it) }
+ links.gotoLinks.forEach { link ->
+ gotoLinks[cumulativeId] =
+ LinkWrapper(pageIndex, link, getLinkBounds(pageIndex, link.bounds.first()))
+ cumulativeId++
+ }
+ links.externalLinks.forEach { link ->
+ urlLinks[cumulativeId] =
+ LinkWrapper(pageIndex, link, getLinkBounds(pageIndex, link.bounds.first()))
+ cumulativeId++
+ }
}
}
isLinksLoaded = true
@@ -243,3 +283,13 @@
}
}
}
+
+/**
+ * A wrapper class for links in the PDF document.
+ *
+ * @param T The type of link content (GotoLink or URL link).
+ * @param pageNumber The 0-indexed page number where the link is located.
+ * @param content The link's content (GotoLink or URL link).
+ * @param linkBounds The link's bounds in the full PDF's content coordinates.
+ */
+private data class LinkWrapper<T>(val pageNumber: Int, val content: T, val linkBounds: RectF)
diff --git a/pdf/pdf-viewer/src/main/kotlin/androidx/pdf/view/BitmapFetcher.kt b/pdf/pdf-viewer/src/main/kotlin/androidx/pdf/view/BitmapFetcher.kt
index 8b223f0..f8c0f61 100644
--- a/pdf/pdf-viewer/src/main/kotlin/androidx/pdf/view/BitmapFetcher.kt
+++ b/pdf/pdf-viewer/src/main/kotlin/androidx/pdf/view/BitmapFetcher.kt
@@ -24,12 +24,11 @@
import androidx.annotation.MainThread
import androidx.annotation.VisibleForTesting
import androidx.pdf.PdfDocument
+import androidx.pdf.util.RectUtils
import kotlin.math.roundToInt
-import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.ensureActive
-import kotlinx.coroutines.job
import kotlinx.coroutines.launch
/**
@@ -56,6 +55,7 @@
*/
private val maxTileBackgroundSizePx = Point(maxBitmapSizePx.x / 2, maxBitmapSizePx.y / 2)
+ /** True if this fetcher is ready to fetch bitmaps for the page */
var isActive: Boolean = false
set(value) {
// Debounce setting the field to the same value
@@ -64,41 +64,102 @@
if (field) onActive() else onInactive()
}
+ /** The bitmaps to draw for this page, as [PageContents] */
@get:MainThread var pageContents: PageContents? = null
+ /** The [PdfDocument.BitmapSource] from which to obtain [Bitmap]s, only used while [isActive] */
private var bitmapSource: PdfDocument.BitmapSource? = null
- @VisibleForTesting var currentRenderingScale: Float? = null
- @VisibleForTesting var renderingJob: Job? = null
- /**
- * Notify this fetcher that the zoom level / scale factor of the UI has changed, and that it
- * ought to consider fetching new bitmaps
- */
- fun onScaleChanged(scale: Float) {
- if (!shouldRenderNewBitmaps(scale)) return
+ /** The scale, i.e. zoom level for which we're actively fetching [Bitmap]s */
+ @VisibleForTesting var currentFetchingScale: Float? = null
- currentRenderingScale = scale
- renderingJob?.cancel()
- renderingJob =
- if (needsTiling(scale)) {
- fetchTiles(scale)
- } else {
- fetchNewBitmap(scale)
- }
- renderingJob?.invokeOnCompletion { cause ->
- // We only want to reset these states when we completed naturally
- if (cause is CancellationException) return@invokeOnCompletion
- renderingJob = null
- currentRenderingScale = null
+ /** The [BitmapRequestHandle] for any ongoing fetch */
+ @VisibleForTesting var fetchingWorkHandle: BitmapRequestHandle? = null
+
+ /** Update the view area and scale for which we should be fetching bitmaps */
+ fun updateViewProperties(scale: Float, viewArea: Rect) {
+ // Scale the provided viewArea, and clip it to the scaled bounds of the page
+ // Carefully avoid mutating the provided Rect
+ val scaledViewArea = Rect(viewArea)
+ RectUtils.scale(scaledViewArea, scale)
+ scaledViewArea.intersect(0, 0, (pageSize.x * scale).toInt(), (pageSize.y * scale).toInt())
+ if (shouldFetchNewContents(scale)) {
+ // Scale has changed, fetch entirely new PageContents
+ fetchNewContents(scale, scaledViewArea)
+ } else {
+ // View area has changed, fetch new tiles and discard obsolete ones IFF we're tiling
+ maybeUpdateTiling(scale, scaledViewArea)
}
}
- private fun shouldRenderNewBitmaps(scale: Float): Boolean {
- val renderingAtCurrentScale =
- currentRenderingScale == scale && renderingJob?.isActive == true
- val renderedAtCurrentScale = pageContents?.let { it.renderedScale == scale } ?: false
+ /** Discard all bitmaps in the current tiling */
+ fun discardTileBitmaps() {
+ (pageContents as? TileBoard)?.let { for (tile in it.tiles) tile.bitmap = null }
+ }
- return !renderedAtCurrentScale && !renderingAtCurrentScale
+ private fun maybeUpdateTiling(scale: Float, scaledViewArea: Rect) {
+ // Exit early if we're not tiling
+ val currentTileBoard = pageContents as? TileBoard ?: return
+ val currentTilingWork = fetchingWorkHandle as? TileBoardRequestHandle
+ val tileRequests = mutableMapOf<Int, SingleBitmapRequestHandle>()
+ var tileJob: Job? = null
+ for (tile in currentTileBoard.tiles) {
+ val ongoingRequest = currentTilingWork?.tileRequestHandles?.get(tile.index)
+ if (
+ tile.rectPx.intersects(
+ scaledViewArea.left,
+ scaledViewArea.top,
+ scaledViewArea.right,
+ scaledViewArea.bottom
+ )
+ ) {
+ // Tile is visible, make sure we have, or have requested, a Bitmap for it
+ if (ongoingRequest?.isActive == true) {
+ // Continue tracking the active request for this tile
+ tileRequests[tile.index] = ongoingRequest
+ } else if (tile.bitmap == null) {
+ // Make a new request for this tile
+ tileJob = fetchBitmap(tile, scale, tileJob)
+ tileRequests[tile.index] = SingleBitmapRequestHandle(tileJob)
+ }
+ } else {
+ // Tile is no longer visible, cancel any active request and clean up the Bitmap
+ ongoingRequest?.cancel()
+ tile.bitmap = null
+ }
+ }
+ if (tileRequests.isNotEmpty()) {
+ fetchingWorkHandle =
+ TileBoardRequestHandle(tileRequests, currentTilingWork?.backgroundRequestHandle)
+ currentFetchingScale = scale
+ }
+ }
+
+ /**
+ * Notify this fetcher that the zoom level / scale factor of the UI has changed, and that it
+ * ought to fetch new bitmaps
+ */
+ private fun fetchNewContents(scale: Float, scaledViewArea: Rect) {
+ fetchingWorkHandle?.cancel()
+ fetchingWorkHandle =
+ if (needsTiling(scale)) {
+ fetchTiles(scale, scaledViewArea)
+ } else {
+ fetchNewBitmap(scale)
+ }
+ currentFetchingScale = scale
+ }
+
+ /**
+ * Returns true if this fetcher should start fetching a net-new [PageContents], i.e. if the
+ * scaled has changed since we started or finished fetching the previous set of Bitmaps
+ */
+ private fun shouldFetchNewContents(scale: Float): Boolean {
+ val fetchingAtCurrentScale =
+ currentFetchingScale == scale && fetchingWorkHandle?.isActive == true
+ val fetchedAtCurrentScale = pageContents?.let { it.bitmapScale == scale } == true
+
+ return !fetchedAtCurrentScale && !fetchingAtCurrentScale
}
/** Prepare to start fetching bitmaps */
@@ -111,73 +172,102 @@
* this fetcher
*/
private fun onInactive() {
- currentRenderingScale = null
+ currentFetchingScale = null
pageContents = null
- renderingJob?.cancel()
- renderingJob = null
+ fetchingWorkHandle?.cancel()
+ fetchingWorkHandle = null
bitmapSource?.close()
bitmapSource = null
}
/** Fetch a [FullPageBitmap] */
- private fun fetchNewBitmap(scale: Float): Job {
- return backgroundScope.launch {
- val size = limitBitmapSize(scale, maxBitmapSizePx)
- // If our BitmapSource is null that means this fetcher is inactive and we should
- // stop what we're doing
- val bitmap = bitmapSource?.getBitmap(size) ?: return@launch
- ensureActive()
- pageContents = FullPageBitmap(bitmap, scale)
- onPageUpdate()
- }
+ private fun fetchNewBitmap(scale: Float): SingleBitmapRequestHandle {
+ val job =
+ backgroundScope.launch {
+ val size = limitBitmapSize(scale, maxBitmapSizePx)
+ // If our BitmapSource is null that means this fetcher is inactive and we should
+ // stop what we're doing
+ val bitmap = bitmapSource?.getBitmap(size) ?: return@launch
+ ensureActive()
+ pageContents = FullPageBitmap(bitmap, scale)
+ onPageUpdate()
+ }
+ return SingleBitmapRequestHandle(job)
}
/** Fetch a [TileBoard] */
- private fun fetchTiles(scale: Float): Job {
+ private fun fetchTiles(scale: Float, scaledViewArea: Rect): TileBoardRequestHandle {
val pageSizePx = Point((pageSize.x * scale).roundToInt(), (pageSize.y * scale).roundToInt())
val tileBoard = TileBoard(tileSizePx, pageSizePx, scale)
- // Re-use an existing background bitmap if we have one to avoid unnecessary re-rendering
+ // Re-use an existing background bitmap if we have one to avoid unnecessary re-fetching
// and jank
- val prevBackground = (tileBoard as? TileBoard)?.backgroundBitmap
+ val prevBackground = tileBoard.backgroundBitmap
if (prevBackground != null) {
tileBoard.backgroundBitmap = prevBackground
pageContents = tileBoard
onPageUpdate()
}
- return backgroundScope.launch {
- // Render a new background bitmap if we must
+ val backgroundRequest =
if (prevBackground == null) {
- // If our BitmapSource is null that means this fetcher is inactive and we should
- // stop what we're doing
- val backgroundSize = limitBitmapSize(scale, maxTileBackgroundSizePx)
- val bitmap = bitmapSource?.getBitmap(backgroundSize) ?: return@launch
- pageContents = tileBoard
- ensureActive()
- tileBoard.backgroundBitmap = bitmap
- onPageUpdate()
+ val job =
+ backgroundScope.launch {
+ ensureActive()
+ val backgroundSize = limitBitmapSize(scale, maxTileBackgroundSizePx)
+ val bitmap = bitmapSource?.getBitmap(backgroundSize) ?: return@launch
+ pageContents = tileBoard
+ ensureActive()
+ tileBoard.backgroundBitmap = bitmap
+ onPageUpdate()
+ }
+ SingleBitmapRequestHandle(job)
+ } else {
+ null
}
- for (tile in tileBoard.tiles) {
- renderBitmap(tile, coroutineContext.job, scale)
+ val tileRequests = mutableMapOf<Int, SingleBitmapRequestHandle>()
+ // Used to sequence requests so tiles are loaded left-to-right and top-to-bottom
+ var tileJob: Job? = null
+ for (tile in tileBoard.tiles) {
+ val tileRect = tile.rectPx
+ if (
+ scaledViewArea.intersects(
+ tileRect.left,
+ tileRect.top,
+ tileRect.right,
+ tileRect.bottom
+ )
+ ) {
+ tileJob = fetchBitmap(tile, scale, tileJob)
+ tileRequests[tile.index] = SingleBitmapRequestHandle(tileJob)
}
}
+ return TileBoardRequestHandle(tileRequests.toMap(), backgroundRequest)
}
- /** Render a [Bitmap] for this [TileBoard.Tile] */
- private suspend fun renderBitmap(tile: TileBoard.Tile, thisJob: Job, scale: Float) {
- thisJob.ensureActive()
- val left = tile.offsetPx.x
- val top = tile.offsetPx.y
- val tileRect = Rect(left, top, left + tile.exactSizePx.x, top + tile.exactSizePx.y)
- // If our BitmapSource is null that means this fetcher is inactive and we should
- // stop what we're doing
- val bitmap =
- bitmapSource?.getBitmap(
- Size((pageSize.x * scale).roundToInt(), (pageSize.y * scale).roundToInt()),
- tileRect
- ) ?: return
- thisJob.ensureActive()
- tile.bitmap = bitmap
- onPageUpdate()
+ /**
+ * Fetch a [Bitmap] for this [TileBoard.Tile]
+ *
+ * @param tile the [TileBoard.Tile] to fetch a bitmap for
+ * @param scale the scale factor of the bitmap
+ * @param prevJob the [Job] that is fetching a bitmap for the tile left or above [tile], i.e. to
+ * guarantee tiles are loaded left-to-right and top-to-bottom
+ */
+ private fun fetchBitmap(tile: TileBoard.Tile, scale: Float, prevJob: Job?): Job {
+ val job =
+ backgroundScope.launch {
+ prevJob?.join()
+ ensureActive()
+ // If our BitmapSource is null that means this fetcher is inactive and we should
+ // stop what we're doing
+ val bitmap =
+ bitmapSource?.getBitmap(
+ Size((pageSize.x * scale).roundToInt(), (pageSize.y * scale).roundToInt()),
+ tile.rectPx
+ ) ?: return@launch
+ ensureActive()
+ tile.bitmap = bitmap
+ onPageUpdate()
+ }
+ return job
}
/** True if the [pageSize] * [scale] exceeds [maxBitmapSizePx] */
@@ -206,13 +296,56 @@
}
}
+/** Represents a cancellable handle to a request for one or more [Bitmap]s */
+internal sealed interface BitmapRequestHandle {
+ /** True if this request is active */
+ val isActive: Boolean
+
+ /** Cancel this request completely */
+ fun cancel()
+}
+
+/** Cancellable [BitmapRequestHandle] for a single [Bitmap] */
+internal class SingleBitmapRequestHandle(private val job: Job) : BitmapRequestHandle {
+ override val isActive: Boolean
+ get() = job.isActive
+
+ override fun cancel() {
+ job.cancel()
+ }
+}
+
+/**
+ * Cancellable [BitmapRequestHandle] for a full [TileBoard], composing multiple
+ * [SingleBitmapRequestHandle] for the low-res background and each high-res tile
+ */
+internal class TileBoardRequestHandle(
+ /** Map of [TileBoard.Tile.index] to a [BitmapRequestHandle] to fetch that tile's bitmap */
+ val tileRequestHandles: Map<Int, SingleBitmapRequestHandle>,
+ /**
+ * [SingleBitmapRequestHandle] to fetch a low-res background for this tiling, or null if we
+ * re-used the background from a previous tiling
+ */
+ val backgroundRequestHandle: SingleBitmapRequestHandle? = null
+) : BitmapRequestHandle {
+ override val isActive: Boolean
+ get() =
+ tileRequestHandles.values.any { it.isActive } ||
+ backgroundRequestHandle?.isActive == true
+
+ override fun cancel() {
+ tileRequestHandles.values.forEach { it.cancel() }
+ backgroundRequestHandle?.cancel()
+ }
+}
+
/** Represents the [Bitmap] or [Bitmap]s used to render this page */
internal sealed interface PageContents {
- val renderedScale: Float
+ val bitmapScale: Float
}
/** A singular [Bitmap] depicting the full page, when full page rendering is used */
-internal class FullPageBitmap(val bitmap: Bitmap, override val renderedScale: Float) : PageContents
+internal class FullPageBitmap(val bitmap: Bitmap, override val bitmapScale: Float) : PageContents
/**
* A set of [Bitmap]s that depict the full page as a rectangular grid of individual bitmap tiles.
@@ -221,7 +354,7 @@
internal class TileBoard(
val tileSizePx: Point,
val pageSizePx: Point,
- override val renderedScale: Float
+ override val bitmapScale: Float
) : PageContents {
/** The low res background [Bitmap] for this [TileBoard] */
@@ -239,7 +372,7 @@
val tiles = Array(numRows * numCols) { index -> Tile(index) }
/** An individual [Tile] in this [TileBoard] */
- inner class Tile(index: Int) {
+ inner class Tile(val index: Int) {
/** The x position of this tile in the tile board */
private val rowIdx = index / numCols
@@ -248,7 +381,7 @@
/**
* The offset of this [Tile] from the origin of the page in pixels, used in computations
- * where an exact pixel size is expected, e.g. rendering bitmaps
+ * where an exact pixel size is expected, e.g. fetching bitmaps
*/
val offsetPx = Point(colIdx * tileSizePx.x, rowIdx * tileSizePx.y)
@@ -259,6 +392,10 @@
minOf(tileSizePx.y, pageSizePx.y - offsetPx.y),
)
+ /** The exact pixel location of this tile in the scaled page */
+ val rectPx =
+ Rect(offsetPx.x, offsetPx.y, offsetPx.x + exactSizePx.x, offsetPx.y + exactSizePx.y)
+
/** The high res [Bitmap] for this [Tile] */
var bitmap: Bitmap? = null
}
diff --git a/pdf/pdf-viewer/src/main/kotlin/androidx/pdf/view/Page.kt b/pdf/pdf-viewer/src/main/kotlin/androidx/pdf/view/Page.kt
index 48d9f6f..5cfabbc 100644
--- a/pdf/pdf-viewer/src/main/kotlin/androidx/pdf/view/Page.kt
+++ b/pdf/pdf-viewer/src/main/kotlin/androidx/pdf/view/Page.kt
@@ -39,7 +39,7 @@
/** The 0-based index of this page in the PDF */
private val pageNum: Int,
/** The size of this PDF page, in content coordinates */
- pageSizePx: Point,
+ pageSize: Point,
/** The [PdfDocument] this [Page] belongs to */
private val pdfDocument: PdfDocument,
/** The [CoroutineScope] to use for background work */
@@ -64,7 +64,7 @@
private val bitmapFetcher =
BitmapFetcher(
pageNum,
- pageSizePx,
+ pageSize,
pdfDocument,
backgroundScope,
maxBitmapSizePx,
@@ -91,17 +91,34 @@
internal var links: PdfDocument.PdfPageLinks? = null
private set
- fun updateState(zoom: Float, isFlinging: Boolean = false) {
+ /**
+ * Puts this page into a "visible" state, and / or updates various properties related to the
+ * page's visible state
+ *
+ * @param zoom the current scale
+ * @param viewArea the portion of the page that's visible, in content coordinates
+ * @param stablePosition true if position is not actively changing, e.g. during a fling
+ */
+ fun setVisible(zoom: Float, viewArea: Rect, stablePosition: Boolean = true) {
bitmapFetcher.isActive = true
- bitmapFetcher.onScaleChanged(zoom)
- if (!isFlinging) {
+ bitmapFetcher.updateViewProperties(zoom, viewArea)
+ if (stablePosition) {
maybeFetchLinks()
if (isTouchExplorationEnabled) {
- fetchPageText()
+ maybeFetchPageText()
}
}
}
+ /**
+ * Puts this page into a "nearly visible" state, discarding only high res bitmaps and retaining
+ * lighter weight data in case the page becomes visible again
+ */
+ fun setNearlyVisible() {
+ bitmapFetcher.discardTileBitmaps()
+ }
+
+ /** Puts this page into an "invisible" state, i.e. retaining only the minimum data required */
fun setInvisible() {
bitmapFetcher.isActive = false
pageText = null
@@ -112,7 +129,7 @@
fetchLinksJob = null
}
- private fun fetchPageText() {
+ private fun maybeFetchPageText() {
if (fetchPageTextJob?.isActive == true || pageText != null) return
fetchPageTextJob =
@@ -171,7 +188,7 @@
canvas.drawBitmap(
bitmap, /* src */
null,
- locationForTile(tile, tileBoard.renderedScale, locationInView),
+ locationForTile(tile, tileBoard.bitmapScale, locationInView),
BMP_PAINT
)
}
diff --git a/pdf/pdf-viewer/src/main/kotlin/androidx/pdf/view/PageLayoutManager.kt b/pdf/pdf-viewer/src/main/kotlin/androidx/pdf/view/PageLayoutManager.kt
index 2844056..90156e5 100644
--- a/pdf/pdf-viewer/src/main/kotlin/androidx/pdf/view/PageLayoutManager.kt
+++ b/pdf/pdf-viewer/src/main/kotlin/androidx/pdf/view/PageLayoutManager.kt
@@ -21,6 +21,7 @@
import android.graphics.Rect
import android.graphics.RectF
import android.util.Range
+import android.util.SparseArray
import androidx.pdf.PdfDocument
import kotlin.math.ceil
import kotlin.math.floor
@@ -107,6 +108,30 @@
}
/**
+ * Returns a [SparseArray] containing [Rect]s indicating the visible region of each visible
+ * page, in page coordinates.
+ */
+ fun getVisiblePageAreas(pages: Range<Int>, viewport: Rect): SparseArray<Rect> {
+ val ret = SparseArray<Rect>(pages.upper - pages.lower + 1)
+ for (i in pages.lower..pages.upper) {
+ ret.put(i, getPageVisibleArea(i, viewport))
+ }
+ return ret
+ }
+
+ private fun getPageVisibleArea(pageNum: Int, viewport: Rect): Rect {
+ val pageLocation = getPageLocation(pageNum, viewport)
+ val pageWidth = pageLocation.right - pageLocation.left
+ val pageHeight = pageLocation.bottom - pageLocation.top
+ return Rect(
+ maxOf(viewport.left - pageLocation.left, 0),
+ maxOf(viewport.top - pageLocation.top, 0),
+ minOf(viewport.right - pageLocation.left, pageWidth),
+ minOf(viewport.bottom - pageLocation.top, pageHeight),
+ )
+ }
+
+ /**
* Returns the current View-coordinate location of a 0-indexed [pageNum] given the [viewport]
*/
fun getPageLocation(pageNum: Int, viewport: Rect): Rect {
diff --git a/pdf/pdf-viewer/src/main/kotlin/androidx/pdf/view/PageManager.kt b/pdf/pdf-viewer/src/main/kotlin/androidx/pdf/view/PageManager.kt
index 322ee54..e32dbae 100644
--- a/pdf/pdf-viewer/src/main/kotlin/androidx/pdf/view/PageManager.kt
+++ b/pdf/pdf-viewer/src/main/kotlin/androidx/pdf/view/PageManager.kt
@@ -78,29 +78,41 @@
private val highlights: MutableMap<Int, MutableList<Highlight>> = mutableMapOf()
/**
- * Updates the internal state of [Page]s owned by this manager in response to a viewport change
+ * Updates the visibility state of [Page]s owned by this manager.
+ *
+ * @param visiblePageAreas the visible area of each visible page, in page coordinates
+ * @param currentZoomLevel the current zoom level
+ * @param stablePosition true if we don't believe our position is actively changing
*/
- fun maybeUpdatePageState(
- visiblePages: Range<Int>,
+ fun updatePageVisibilities(
+ visiblePageAreas: SparseArray<Rect>,
currentZoomLevel: Float,
- isFlinging: Boolean
+ stablePosition: Boolean
) {
// Start preparing UI for visible pages
- for (i in visiblePages.lower..visiblePages.upper) {
- pages[i]?.updateState(currentZoomLevel, isFlinging)
+ visiblePageAreas.keyIterator().forEach { pageNum ->
+ pages[pageNum]?.setVisible(
+ currentZoomLevel,
+ visiblePageAreas.get(pageNum),
+ stablePosition
+ )
}
- // Hide pages that are well outside the viewport. We deliberately don't set pages that
- // are within nearPages, but outside visible pages to invisible to avoid rendering churn
- // for pages likely to return to the viewport.
+ // We put pages that are near the viewport in a "nearly visible" state where some data is
+ // retained. We release all data from pages well outside the viewport
val nearPages =
Range(
- maxOf(0, visiblePages.lower - pagePrefetchRadius),
- minOf(visiblePages.upper + pagePrefetchRadius, pdfDocument.pageCount - 1),
+ maxOf(0, visiblePageAreas.keyAt(0) - pagePrefetchRadius),
+ minOf(
+ visiblePageAreas.keyAt(visiblePageAreas.size() - 1) + pagePrefetchRadius,
+ pdfDocument.pageCount - 1
+ ),
)
for (pageNum in pages.keyIterator()) {
if (pageNum < nearPages.lower || pageNum > nearPages.upper) {
pages[pageNum]?.setInvisible()
+ } else if (!visiblePageAreas.contains(pageNum)) {
+ pages[pageNum]?.setNearlyVisible()
}
}
}
@@ -109,12 +121,12 @@
* Updates the set of [Page]s owned by this manager when a new Page's dimensions are loaded.
* Dimensions are the minimum data required to instantiate a page.
*/
- fun onPageSizeReceived(
+ fun addPage(
pageNum: Int,
size: Point,
- isVisible: Boolean,
currentZoomLevel: Float,
- isFlinging: Boolean
+ stablePosition: Boolean,
+ viewArea: Rect? = null
) {
if (pages.contains(pageNum)) return
val page =
@@ -128,7 +140,12 @@
onPageUpdate = { _invalidationSignalFlow.tryEmit(Unit) },
onPageTextReady = { pageNumber -> _pageTextReadyFlow.tryEmit(pageNumber) }
)
- .apply { if (isVisible) updateState(currentZoomLevel, isFlinging) }
+ .apply {
+ // If the page is visible, let it know
+ if (viewArea != null) {
+ setVisible(currentZoomLevel, viewArea, stablePosition)
+ }
+ }
pages.put(pageNum, page)
}
@@ -151,7 +168,7 @@
* Sets all [Page]s owned by this manager to invisible, i.e. to reduce memory when the host
* [PdfView] is not in an interactive state.
*/
- fun onDetached() {
+ fun cleanup() {
for (page in pages.valueIterator()) {
page.setInvisible()
}
diff --git a/pdf/pdf-viewer/src/main/kotlin/androidx/pdf/view/PdfView.kt b/pdf/pdf-viewer/src/main/kotlin/androidx/pdf/view/PdfView.kt
index 0c1973b..8885cb3 100644
--- a/pdf/pdf-viewer/src/main/kotlin/androidx/pdf/view/PdfView.kt
+++ b/pdf/pdf-viewer/src/main/kotlin/androidx/pdf/view/PdfView.kt
@@ -40,6 +40,7 @@
import android.view.ScaleGestureDetector
import android.view.View
import androidx.annotation.CallSuper
+import androidx.annotation.MainThread
import androidx.annotation.RestrictTo
import androidx.annotation.VisibleForTesting
import androidx.core.graphics.toRectF
@@ -105,9 +106,9 @@
set(value) {
checkMainThread()
value?.let {
- val reset = field != null && field?.uri != value.uri
+ if (field == value) return
field = it
- if (reset) reset()
+ reset()
onDocumentSet()
}
}
@@ -215,6 +216,7 @@
private var awaitingFirstLayout: Boolean = true
private var scrollPositionToRestore: PointF? = null
private var zoomToRestore: Float? = null
+ @VisibleForTesting internal var isInitialZoomDone: Boolean = false
/**
* The width of the PdfView before the last layout change (e.g., before rotation). Used to
* preserve the zoom level when the device is rotated.
@@ -231,6 +233,22 @@
/** Whether we are in a fling movement. This is used to detect the end of that movement */
private var isFling = false
+ /**
+ * Returns true if neither zoom nor scroll are actively changing. Does not account for
+ * externally-driven changes in position (e.g. a animating scrollY or zoom)
+ */
+ private val positionIsStable: Boolean
+ get() {
+ val zoomIsChanging = gestureTracker.matches(GestureTracker.Gesture.ZOOM)
+ val scrollIsChanging =
+ gestureTracker.matches(
+ GestureTracker.Gesture.DRAG,
+ GestureTracker.Gesture.DRAG_X,
+ GestureTracker.Gesture.DRAG_Y
+ ) || isFling
+ return !zoomIsChanging && !scrollIsChanging
+ }
+
// To avoid allocations during drawing
private val visibleAreaRect = Rect()
@@ -250,9 +268,6 @@
@VisibleForTesting
internal var isTouchExplorationEnabled: Boolean =
Accessibility.get().isTouchExplorationEnabled(context)
- set(value) {
- field = value
- }
private var selectionStateManager: SelectionStateManager? = null
private val selectionRenderer = SelectionRenderer(context)
@@ -514,13 +529,14 @@
super.onDetachedFromWindow()
stopCollectingData()
awaitingFirstLayout = true
- pageManager?.onDetached()
+ pageManager?.cleanup()
}
override fun onSaveInstanceState(): Parcelable? {
val superState = super.onSaveInstanceState()
val state = PdfViewSavedState(superState)
state.zoom = zoom
+ state.isInitialZoomDone = isInitialZoomDone
state.viewWidth = width
state.contentCenterX = toContentX(viewportWidth.toFloat() / 2f)
state.contentCenterY = toContentY(viewportHeight.toFloat() / 2f)
@@ -553,11 +569,11 @@
postInvalidateOnAnimation()
} else if (isFling) {
isFling = false
- // Once the fling has ended, prompt the page manager to start fetching data for pages
- // that we don't fetch during a fling
- pageManager?.maybeUpdatePageState(visiblePages, zoom, isFling)
// We hide the action mode during a fling, so reveal it when the fling is over
updateSelectionActionModeVisibility()
+ // Once the fling has ended, prompt the page manager to start fetching data for pages
+ // that we don't fetch during a fling
+ maybeUpdatePageVisibility()
}
}
@@ -605,7 +621,8 @@
}
}
- private fun getDefaultZoom(): Float {
+ @VisibleForTesting
+ internal fun getDefaultZoom(): Float {
if (contentWidth == 0 || viewportWidth == 0) return DEFAULT_INIT_ZOOM
val widthZoom = viewportWidth.toFloat() / contentWidth
return MathUtils.clamp(widthZoom, minZoom, maxZoom)
@@ -649,6 +666,7 @@
scrollPositionToRestore = positionToRestore
zoomToRestore = localStateToRestore.zoom
oldWidth = localStateToRestore.viewWidth
+ isInitialZoomDone = localStateToRestore.isInitialZoomDone
} else {
scrollToRestoredPosition(positionToRestore, localStateToRestore.zoom)
}
@@ -671,6 +689,7 @@
* Launches a tree of coroutines to collect data from helper classes while we're attached to a
* visible window
*/
+ @MainThread
private fun startCollectingData() {
val mainScope =
CoroutineScope(HandlerCompat.createAsync(handler.looper).asCoroutineDispatcher())
@@ -684,7 +703,17 @@
launch {
manager.dimensions.collect { onPageDimensionsReceived(it.first, it.second) }
}
- launch { manager.visiblePages.collect { onVisiblePagesChanged() } }
+ launch { manager.visiblePages.collect { maybeUpdatePageVisibility() } }
+ }
+ // Don't let two copies of this run concurrently
+ val visiblePagesToJoin = visiblePagesCollector?.apply { cancel() }
+ visiblePagesCollector =
+ mainScope.launch(start = CoroutineStart.UNDISPATCHED) {
+ manager.visiblePages.collect {
+ // Prevent 2 copies from running concurrently
+ visiblePagesToJoin?.join()
+ maybeUpdatePageVisibility()
+ }
}
}
pageManager?.let { manager ->
@@ -821,13 +850,12 @@
onViewportChanged()
// Don't fetch new Bitmaps while the user is actively zooming, to avoid jank and rendering
// churn
- if (!gestureTracker.matches(GestureTracker.Gesture.ZOOM)) {
- pageManager?.maybeUpdatePageState(visiblePages, zoom, isFling)
- }
+ if (positionIsStable) maybeUpdatePageVisibility()
}
private fun onViewportChanged() {
pageLayoutManager?.onViewportChanged(scrollY, height, zoom)
+ if (positionIsStable) maybeUpdatePageVisibility()
accessibilityPageHelper?.invalidateRoot()
updateSelectionActionModeVisibility()
}
@@ -886,18 +914,11 @@
return RectF(viewport).intersects(leftEdge, topEdge, rightEdge, bottomEdge)
}
- /**
- * Invoked by gesture handlers to let this view know that its position has stabilized, i.e. it's
- * not actively changing due to user input
- */
- internal fun onStableZoom() {
- pageManager?.maybeUpdatePageState(visiblePages, zoom, isFling)
- }
-
private fun reset() {
// Stop any in progress fling when we open a new document
scroller.forceFinished(true)
scrollTo(0, 0)
+ pageManager?.cleanup()
zoom = DEFAULT_INIT_ZOOM
pageManager = null
pageLayoutManager = null
@@ -905,20 +926,22 @@
stopCollectingData()
}
- /** React to a change in visible pages (load new pages and clean up old ones) */
- private fun onVisiblePagesChanged() {
- pageManager?.maybeUpdatePageState(visiblePages, zoom, isFling)
+ private fun maybeUpdatePageVisibility() {
+ val visiblePageAreas =
+ pageLayoutManager?.getVisiblePageAreas(visiblePages, getVisibleAreaInContentCoords())
+ ?: return
+ pageManager?.updatePageVisibilities(visiblePageAreas, zoom, positionIsStable)
}
/** React to a page's dimensions being made available */
private fun onPageDimensionsReceived(pageNum: Int, size: Point) {
- pageManager?.onPageSizeReceived(
- pageNum,
- size,
- visiblePages.contains(pageNum),
- zoom,
- isFling
- )
+ val pageLocation =
+ if (visiblePages.contains(pageNum)) {
+ pageLayoutManager?.getPageLocation(pageNum, getVisibleAreaInContentCoords())
+ } else {
+ null
+ }
+ pageManager?.addPage(pageNum, size, zoom, isFling, pageLocation)
// Learning the dimensions of a page can change our understanding of the content that's in
// the viewport
pageLayoutManager?.onViewportChanged(scrollY, height, zoom)
@@ -927,7 +950,10 @@
// centering if it's needed. It doesn't override any restored state because we're scrolling
// to the current scroll position.
if (pageNum == 0) {
- this.zoom = getDefaultZoom()
+ if (!isInitialZoomDone) {
+ this.zoom = getDefaultZoom()
+ isInitialZoomDone = true
+ }
scrollTo(scrollX, scrollY)
}
@@ -1175,7 +1201,9 @@
}
override fun onGestureEnd(gesture: GestureTracker.Gesture?) {
- if (gesture == GestureTracker.Gesture.ZOOM) onStableZoom()
+ // Update page visibility after scroll / zoom gestures end, because we avoid fetching
+ // certain data while those gestures are in progress
+ if (gesture in ZOOM_OR_SCROLL_GESTURES) maybeUpdatePageVisibility()
totalX = 0f
totalY = 0f
straightenCurrentVerticalScroll = true
@@ -1383,6 +1411,14 @@
private const val DEFAULT_PAGE_PREFETCH_RADIUS: Int = 2
+ private val ZOOM_OR_SCROLL_GESTURES =
+ setOf(
+ GestureTracker.Gesture.ZOOM,
+ GestureTracker.Gesture.DRAG,
+ GestureTracker.Gesture.DRAG_X,
+ GestureTracker.Gesture.DRAG_Y
+ )
+
private fun checkMainThread() {
check(Looper.myLooper() == Looper.getMainLooper()) {
"Property must be set on the main thread"
diff --git a/pdf/pdf-viewer/src/main/kotlin/androidx/pdf/view/PdfViewSavedState.kt b/pdf/pdf-viewer/src/main/kotlin/androidx/pdf/view/PdfViewSavedState.kt
index c0c5047..dd0ab91 100644
--- a/pdf/pdf-viewer/src/main/kotlin/androidx/pdf/view/PdfViewSavedState.kt
+++ b/pdf/pdf-viewer/src/main/kotlin/androidx/pdf/view/PdfViewSavedState.kt
@@ -30,6 +30,7 @@
var zoom: Float = 1F
var documentUri: Uri? = null
var paginationModel: PaginationModel? = null
+ var isInitialZoomDone: Boolean = false
/**
* The width of the PdfView before the last layout change (e.g., before rotation). Used to
* preserve the zoom level when the device is rotated.
diff --git a/pdf/pdf-viewer/src/main/res/values-af/strings.xml b/pdf/pdf-viewer/src/main/res/values-af/strings.xml
index faf7bd3..907338a 100644
--- a/pdf/pdf-viewer/src/main/res/values-af/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-af/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"Bladsy is vir die PDF-dokument gebreek"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Onvoldoende data om die PDF-dokument te verwerk"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"Kan nie PDF-lêer oopmaak nie"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Inhoud gekopieer vanaf PDF"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-am/strings.xml b/pdf/pdf-viewer/src/main/res/values-am/strings.xml
index 2c973fa..d518661 100644
--- a/pdf/pdf-viewer/src/main/res/values-am/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-am/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"ለPDF ሰነዱ ገፅ ተበላሽቷል"</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF ሰነዱን ለማሰናዳት በቂ ያልሆነ ውሂብ"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"PDF ፋይል መክፈት አይቻልም"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"ይዘት ከPDF ተቀድቷል"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-ar/strings.xml b/pdf/pdf-viewer/src/main/res/values-ar/strings.xml
index 1958e32..c2b3ec6 100644
--- a/pdf/pdf-viewer/src/main/res/values-ar/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ar/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"تعذّر تحميل صفحة من مستند PDF"</string>
<string name="needs_more_data" msgid="3520133467908240802">"البيانات غير كافية لمعالجة مستند PDF"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"يتعذّر فتح ملف PDF"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"تم نسخ المحتوى من ملف PDF"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-az/strings.xml b/pdf/pdf-viewer/src/main/res/values-az/strings.xml
index 4f4045e..65132fe 100644
--- a/pdf/pdf-viewer/src/main/res/values-az/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-az/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"PDF sənədi üçün səhifədə xəta var"</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF sənədini emal etmək üçün kifayət qədər data yoxdur"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"PDF faylını açmaq olmur"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Kontent PDF-dən kopiyalanıb"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-b+sr+Latn/strings.xml b/pdf/pdf-viewer/src/main/res/values-b+sr+Latn/strings.xml
index 156e778..24b8a40 100644
--- a/pdf/pdf-viewer/src/main/res/values-b+sr+Latn/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-b+sr+Latn/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"Neispravna stranica za PDF dokument"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Nedovoljno podataka za obradu PDF dokumenta"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"Otvaranje PDF fajla nije uspelo"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Sadržaj je kopiran iz PDF-a"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-be/strings.xml b/pdf/pdf-viewer/src/main/res/values-be/strings.xml
index e1a2a77..5f6ad1d 100644
--- a/pdf/pdf-viewer/src/main/res/values-be/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-be/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"Старонка дакумента PDF пашкоджана"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Не хапае даных для апрацоўкі дакумента PDF"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"Не ўдаецца адкрыць файл PDF"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Змесціва скапіравана з PDF"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-bg/strings.xml b/pdf/pdf-viewer/src/main/res/values-bg/strings.xml
index ba05b00..614d295 100644
--- a/pdf/pdf-viewer/src/main/res/values-bg/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-bg/strings.xml
@@ -25,7 +25,7 @@
<string name="desc_password" msgid="6636473611443746739">"поле за парола"</string>
<string name="desc_password_incorrect" msgid="4718823067483963845">"неправилна парола"</string>
<string name="desc_password_incorrect_message" msgid="4676206571020946870">"Грешна парола. Опитайте отново."</string>
- <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"страница <xliff:g id="PAGE">%1$d</xliff:g> от <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"Процент на промяна на мащаба: <xliff:g id="FIRST">%1$d</xliff:g>"</string>
<string name="desc_goto_link" msgid="2461368384824849714">"Към страница <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
@@ -40,7 +40,7 @@
<string name="desc_phone_link" msgid="4986414958429253135">"Телефон: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"начало на избраното"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"край на избраното"</string>
- <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g> – <xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
+ <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g> – <xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_page_range" msgid="5286496438609641577">"страници <xliff:g id="FIRST">%1$d</xliff:g> до <xliff:g id="LAST">%2$d</xliff:g> от <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_image_alt_text" msgid="7700601988820586333">"Изображение: <xliff:g id="ALT_TEXT">%1$s</xliff:g>"</string>
<string name="hint_find" msgid="5385388836603550565">"Търсете във файла"</string>
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"Невалидна страница в PDF документа"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Няма достатъчно данни за обработването на PDF документа"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"PDF файлът не може да се отвори"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Съдържанието е копирано от PDF файла"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-bn/strings.xml b/pdf/pdf-viewer/src/main/res/values-bn/strings.xml
index e617105..81012b0 100644
--- a/pdf/pdf-viewer/src/main/res/values-bn/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-bn/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"পিডিএফ ডকুমেন্টের ক্ষেত্রে পৃষ্ঠা ভেঙে গেছে"</string>
<string name="needs_more_data" msgid="3520133467908240802">"পিডিএফ ডকুমেন্ট প্রসেস করার জন্য যথেষ্ট ডেটা নেই"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"PDF ফাইল খোলা যাচ্ছে না"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"পিডিএফ থেকে কন্টেন্ট কপি করা হয়েছে"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-bs/strings.xml b/pdf/pdf-viewer/src/main/res/values-bs/strings.xml
index a90e957..80927bf 100644
--- a/pdf/pdf-viewer/src/main/res/values-bs/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-bs/strings.xml
@@ -25,7 +25,7 @@
<string name="desc_password" msgid="6636473611443746739">"polje za lozinku"</string>
<string name="desc_password_incorrect" msgid="4718823067483963845">"pogrešna lozinka"</string>
<string name="desc_password_incorrect_message" msgid="4676206571020946870">"Pogrešna lozinka Ponovni pokušaj."</string>
- <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"<xliff:g id="PAGE">%1$d</xliff:g>. stranica od <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"zumiranje <xliff:g id="FIRST">%1$d</xliff:g> posto"</string>
<string name="desc_goto_link" msgid="2461368384824849714">"Odlazak na <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>. stranicu"</string>
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"Stranica je prelomljena za PDF dokument"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Nema dovoljno podataka za obradu PDF dokumenta"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"Nije moguće otvoriti PDF fajl"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Sadržaj je kopiran iz PDF-a"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-ca/strings.xml b/pdf/pdf-viewer/src/main/res/values-ca/strings.xml
index 5f52251..a4f2ed0 100644
--- a/pdf/pdf-viewer/src/main/res/values-ca/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ca/strings.xml
@@ -25,7 +25,7 @@
<string name="desc_password" msgid="6636473611443746739">"camp de contrasenya"</string>
<string name="desc_password_incorrect" msgid="4718823067483963845">"contrasenya incorrecta"</string>
<string name="desc_password_incorrect_message" msgid="4676206571020946870">"Contrasenya incorrecta. Torna-ho a provar."</string>
- <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"pàgina <xliff:g id="PAGE">%1$d</xliff:g> de <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"zoom <xliff:g id="FIRST">%1$d</xliff:g> per cent"</string>
<string name="desc_goto_link" msgid="2461368384824849714">"Ves a la pàgina <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"La pàgina no funciona per al document PDF"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Les dades són insuficients per processar el document PDF"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"No es pot obrir el fitxer PDF"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Contingut copiat del PDF"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-cs/strings.xml b/pdf/pdf-viewer/src/main/res/values-cs/strings.xml
index d521d11..1d94a04 100644
--- a/pdf/pdf-viewer/src/main/res/values-cs/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-cs/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"Dokument PDF obsahuje poškozenou stránku"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Nedostatek dat ke zpracování dokumentu PDF"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"Soubor PDF se nepodařilo otevřít"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Obsah byl zkopírován ze souboru PDF"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-da/strings.xml b/pdf/pdf-viewer/src/main/res/values-da/strings.xml
index 012a1a5..16677bf 100644
--- a/pdf/pdf-viewer/src/main/res/values-da/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-da/strings.xml
@@ -25,7 +25,7 @@
<string name="desc_password" msgid="6636473611443746739">"felt til adgangskode"</string>
<string name="desc_password_incorrect" msgid="4718823067483963845">"adgangskoden er forkert"</string>
<string name="desc_password_incorrect_message" msgid="4676206571020946870">"Forkert adgangskode. Prøv igen."</string>
- <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"side <xliff:g id="PAGE">%1$d</xliff:g> af <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"zoom <xliff:g id="FIRST">%1$d</xliff:g> %%"</string>
<string name="desc_goto_link" msgid="2461368384824849714">"Gå til side <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
@@ -40,7 +40,7 @@
<string name="desc_phone_link" msgid="4986414958429253135">"Telefonnummer: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"start på markering"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"slut på markering"</string>
- <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
+ <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_page_range" msgid="5286496438609641577">"side <xliff:g id="FIRST">%1$d</xliff:g> til <xliff:g id="LAST">%2$d</xliff:g> af <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_image_alt_text" msgid="7700601988820586333">"Billede: <xliff:g id="ALT_TEXT">%1$s</xliff:g>"</string>
<string name="hint_find" msgid="5385388836603550565">"Søg i fil"</string>
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"Siden er ødelagt for PDF-dokumentet"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Der er ikke nok data til at behandle PDF-dokumentet"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"PDF-filen kan ikke åbnes"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Indholdet blev kopieret fra PDF-filen"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-de/strings.xml b/pdf/pdf-viewer/src/main/res/values-de/strings.xml
index 3732805..bc94866 100644
--- a/pdf/pdf-viewer/src/main/res/values-de/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-de/strings.xml
@@ -25,7 +25,7 @@
<string name="desc_password" msgid="6636473611443746739">"Passwortfeld"</string>
<string name="desc_password_incorrect" msgid="4718823067483963845">"Falsches Passwort"</string>
<string name="desc_password_incorrect_message" msgid="4676206571020946870">"Falsches Passwort. Versuch es nochmal."</string>
- <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"Seite <xliff:g id="PAGE">%1$d</xliff:g> von <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"Zoom: <xliff:g id="FIRST">%1$d</xliff:g> %%"</string>
<string name="desc_goto_link" msgid="2461368384824849714">"Gehe zu Seite <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
@@ -40,7 +40,7 @@
<string name="desc_phone_link" msgid="4986414958429253135">"Telefonnummer: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"Beginn der Auswahl"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"Ende der Auswahl"</string>
- <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>–<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
+ <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>–<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_page_range" msgid="5286496438609641577">"Seiten <xliff:g id="FIRST">%1$d</xliff:g> bis <xliff:g id="LAST">%2$d</xliff:g> von <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_image_alt_text" msgid="7700601988820586333">"Bild: <xliff:g id="ALT_TEXT">%1$s</xliff:g>"</string>
<string name="hint_find" msgid="5385388836603550565">"In Datei suchen"</string>
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"Seite für PDF-Dokument ist fehlerhaft"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Keine ausreichenden Daten, um das PDF-Dokument zu verarbeiten"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"PDF‑Datei kann nicht geöffnet werden"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Inhalt aus PDF kopiert"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-el/strings.xml b/pdf/pdf-viewer/src/main/res/values-el/strings.xml
index bfef9c9..d34708d5 100644
--- a/pdf/pdf-viewer/src/main/res/values-el/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-el/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"Δεν ήταν δυνατή η φόρτωση του εγγράφου PDF από τη σελίδα"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Μη επαρκή δεδομένα για την επεξεργασία του εγγράφου PDF"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"Δεν είναι δυνατό το άνοιγμα του αρχείου PDF"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Το περιεχόμενο αντιγράφηκε από το PDF"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-en-rAU/strings.xml b/pdf/pdf-viewer/src/main/res/values-en-rAU/strings.xml
index 37aa01f..5b49160 100644
--- a/pdf/pdf-viewer/src/main/res/values-en-rAU/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-en-rAU/strings.xml
@@ -25,7 +25,7 @@
<string name="desc_password" msgid="6636473611443746739">"password field"</string>
<string name="desc_password_incorrect" msgid="4718823067483963845">"password incorrect"</string>
<string name="desc_password_incorrect_message" msgid="4676206571020946870">"Wrong password. Try again."</string>
- <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"page <xliff:g id="PAGE">%1$d</xliff:g> of <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"zoom <xliff:g id="FIRST">%1$d</xliff:g> per cent"</string>
<string name="desc_goto_link" msgid="2461368384824849714">"Go to page <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
@@ -40,7 +40,7 @@
<string name="desc_phone_link" msgid="4986414958429253135">"Phone: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"selection start"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"selection end"</string>
- <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>–<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
+ <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>–<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_page_range" msgid="5286496438609641577">"pages <xliff:g id="FIRST">%1$d</xliff:g> to <xliff:g id="LAST">%2$d</xliff:g> of <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_image_alt_text" msgid="7700601988820586333">"Image: <xliff:g id="ALT_TEXT">%1$s</xliff:g>"</string>
<string name="hint_find" msgid="5385388836603550565">"Find in file"</string>
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"Page broken for the PDF document"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Insufficient data for processing the PDF document"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"Can\'t open PDF file"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Content copied from PDF"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-en-rGB/strings.xml b/pdf/pdf-viewer/src/main/res/values-en-rGB/strings.xml
index 37aa01f..5b49160 100644
--- a/pdf/pdf-viewer/src/main/res/values-en-rGB/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-en-rGB/strings.xml
@@ -25,7 +25,7 @@
<string name="desc_password" msgid="6636473611443746739">"password field"</string>
<string name="desc_password_incorrect" msgid="4718823067483963845">"password incorrect"</string>
<string name="desc_password_incorrect_message" msgid="4676206571020946870">"Wrong password. Try again."</string>
- <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"page <xliff:g id="PAGE">%1$d</xliff:g> of <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"zoom <xliff:g id="FIRST">%1$d</xliff:g> per cent"</string>
<string name="desc_goto_link" msgid="2461368384824849714">"Go to page <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
@@ -40,7 +40,7 @@
<string name="desc_phone_link" msgid="4986414958429253135">"Phone: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"selection start"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"selection end"</string>
- <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>–<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
+ <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>–<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_page_range" msgid="5286496438609641577">"pages <xliff:g id="FIRST">%1$d</xliff:g> to <xliff:g id="LAST">%2$d</xliff:g> of <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_image_alt_text" msgid="7700601988820586333">"Image: <xliff:g id="ALT_TEXT">%1$s</xliff:g>"</string>
<string name="hint_find" msgid="5385388836603550565">"Find in file"</string>
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"Page broken for the PDF document"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Insufficient data for processing the PDF document"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"Can\'t open PDF file"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Content copied from PDF"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-en-rIN/strings.xml b/pdf/pdf-viewer/src/main/res/values-en-rIN/strings.xml
index 37aa01f..5b49160 100644
--- a/pdf/pdf-viewer/src/main/res/values-en-rIN/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-en-rIN/strings.xml
@@ -25,7 +25,7 @@
<string name="desc_password" msgid="6636473611443746739">"password field"</string>
<string name="desc_password_incorrect" msgid="4718823067483963845">"password incorrect"</string>
<string name="desc_password_incorrect_message" msgid="4676206571020946870">"Wrong password. Try again."</string>
- <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"page <xliff:g id="PAGE">%1$d</xliff:g> of <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"zoom <xliff:g id="FIRST">%1$d</xliff:g> per cent"</string>
<string name="desc_goto_link" msgid="2461368384824849714">"Go to page <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
@@ -40,7 +40,7 @@
<string name="desc_phone_link" msgid="4986414958429253135">"Phone: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"selection start"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"selection end"</string>
- <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>–<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
+ <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>–<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_page_range" msgid="5286496438609641577">"pages <xliff:g id="FIRST">%1$d</xliff:g> to <xliff:g id="LAST">%2$d</xliff:g> of <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_image_alt_text" msgid="7700601988820586333">"Image: <xliff:g id="ALT_TEXT">%1$s</xliff:g>"</string>
<string name="hint_find" msgid="5385388836603550565">"Find in file"</string>
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"Page broken for the PDF document"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Insufficient data for processing the PDF document"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"Can\'t open PDF file"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Content copied from PDF"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-es-rUS/strings.xml b/pdf/pdf-viewer/src/main/res/values-es-rUS/strings.xml
index 38a7d86f..1fbf379 100644
--- a/pdf/pdf-viewer/src/main/res/values-es-rUS/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-es-rUS/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"La página no funciona para el documento PDF"</string>
<string name="needs_more_data" msgid="3520133467908240802">"No hay datos suficientes para procesar el documento PDF"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"No se puede abrir el archivo PDF"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Se copió el contenido del PDF"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-es/strings.xml b/pdf/pdf-viewer/src/main/res/values-es/strings.xml
index 54e909e..9d335dd 100644
--- a/pdf/pdf-viewer/src/main/res/values-es/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-es/strings.xml
@@ -25,7 +25,7 @@
<string name="desc_password" msgid="6636473611443746739">"campo de contraseña"</string>
<string name="desc_password_incorrect" msgid="4718823067483963845">"contraseña incorrecta"</string>
<string name="desc_password_incorrect_message" msgid="4676206571020946870">"Contraseña incorrecta. Inténtalo de nuevo."</string>
- <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"página <xliff:g id="PAGE">%1$d</xliff:g> de <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"zoom <xliff:g id="FIRST">%1$d</xliff:g> por ciento"</string>
<string name="desc_goto_link" msgid="2461368384824849714">"Ir a la página <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
@@ -40,7 +40,7 @@
<string name="desc_phone_link" msgid="4986414958429253135">"Teléfono: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"inicio de la selección"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"final de la selección"</string>
- <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
+ <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_page_range" msgid="5286496438609641577">"páginas <xliff:g id="FIRST">%1$d</xliff:g> a <xliff:g id="LAST">%2$d</xliff:g> de <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_image_alt_text" msgid="7700601988820586333">"Imagen: <xliff:g id="ALT_TEXT">%1$s</xliff:g>"</string>
<string name="hint_find" msgid="5385388836603550565">"Buscar en el archivo"</string>
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"La página no funciona para el documento PDF"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Datos insuficientes para procesar el documento PDF"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"No se puede abrir el archivo PDF"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Contenido copiado del PDF"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-et/strings.xml b/pdf/pdf-viewer/src/main/res/values-et/strings.xml
index 7bdf417..4d52a6d 100644
--- a/pdf/pdf-viewer/src/main/res/values-et/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-et/strings.xml
@@ -25,7 +25,7 @@
<string name="desc_password" msgid="6636473611443746739">"parooliväli"</string>
<string name="desc_password_incorrect" msgid="4718823067483963845">"parool on vale"</string>
<string name="desc_password_incorrect_message" msgid="4676206571020946870">"Vale parool. Proovige uuesti."</string>
- <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"lk <xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"suum <xliff:g id="FIRST">%1$d</xliff:g> protsenti"</string>
<string name="desc_goto_link" msgid="2461368384824849714">"Mine lehele <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
@@ -40,7 +40,7 @@
<string name="desc_phone_link" msgid="4986414958429253135">"Telefon: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"valiku algus"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"valiku lõpp"</string>
- <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>–<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
+ <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>–<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_page_range" msgid="5286496438609641577">"lk <xliff:g id="FIRST">%1$d</xliff:g>–<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>-st"</string>
<string name="desc_image_alt_text" msgid="7700601988820586333">"Pilt: <xliff:g id="ALT_TEXT">%1$s</xliff:g>"</string>
<string name="hint_find" msgid="5385388836603550565">"Otsige failist"</string>
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"Rikutud leht PDF-dokumendis"</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF-dokumendi töötlemiseks pole piisavalt andmeid"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"PDF-faili ei saa avada"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Sisu kopeeritud PDF-ist"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-eu/strings.xml b/pdf/pdf-viewer/src/main/res/values-eu/strings.xml
index 48c22ae..163a362 100644
--- a/pdf/pdf-viewer/src/main/res/values-eu/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-eu/strings.xml
@@ -25,7 +25,7 @@
<string name="desc_password" msgid="6636473611443746739">"pasahitzaren eremua"</string>
<string name="desc_password_incorrect" msgid="4718823067483963845">"pasahitza okerra da"</string>
<string name="desc_password_incorrect_message" msgid="4676206571020946870">"Pasahitza ez da zuzena. Saiatu berriro."</string>
- <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"<xliff:g id="TOTAL">%2$d</xliff:g> orritatik <xliff:g id="PAGE">%1$d</xliff:g>garrena"</string>
<string name="desc_zoom" msgid="7318480946145947242">"zooma ehuneko <xliff:g id="FIRST">%1$d</xliff:g>"</string>
<string name="desc_goto_link" msgid="2461368384824849714">"Joan <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>. orrira"</string>
@@ -40,7 +40,7 @@
<string name="desc_phone_link" msgid="4986414958429253135">"Telefono-zenbakia: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"hautapenaren hasiera"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"hautapenaren amaiera"</string>
- <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
+ <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_page_range" msgid="5286496438609641577">"<xliff:g id="FIRST">%1$d</xliff:g> eta <xliff:g id="LAST">%2$d</xliff:g> bitarteko orriak, guztira <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_image_alt_text" msgid="7700601988820586333">"Irudia: <xliff:g id="ALT_TEXT">%1$s</xliff:g>"</string>
<string name="hint_find" msgid="5385388836603550565">"Bilatu fitxategia"</string>
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"PDF dokumentuaren orria hondatuta dago"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Ez dago behar adina daturik PDF dokumentua prozesatzeko"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"Ezin da ireki PDF fitxategia"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"PDFtik kopiatutako edukia"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-fa/strings.xml b/pdf/pdf-viewer/src/main/res/values-fa/strings.xml
index c741e2c..e957a49 100644
--- a/pdf/pdf-viewer/src/main/res/values-fa/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-fa/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"صفحه سند PDF خراب است"</string>
<string name="needs_more_data" msgid="3520133467908240802">"دادهها برای پردازش سند PDF کافی نیست"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"فایل PDF باز نشد"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"محتوای کپیشده از PDF"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-fi/strings.xml b/pdf/pdf-viewer/src/main/res/values-fi/strings.xml
index 53cc957..1be8815 100644
--- a/pdf/pdf-viewer/src/main/res/values-fi/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-fi/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"PDF-dokumenttiin liittyvä sivu on rikki"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Riittämätön data PDF-dokumentin käsittelyyn"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"PDF-tiedostoa ei voi avata"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Sisältö kopioitu PDF-tiedostosta"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-fr-rCA/strings.xml b/pdf/pdf-viewer/src/main/res/values-fr-rCA/strings.xml
index ccd0b38..cf9eeb2 100644
--- a/pdf/pdf-viewer/src/main/res/values-fr-rCA/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-fr-rCA/strings.xml
@@ -25,7 +25,7 @@
<string name="desc_password" msgid="6636473611443746739">"champ du mot de passe"</string>
<string name="desc_password_incorrect" msgid="4718823067483963845">"mot de passe incorrect"</string>
<string name="desc_password_incorrect_message" msgid="4676206571020946870">"Mot de passe incorrect. Réessayez."</string>
- <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"page <xliff:g id="PAGE">%1$d</xliff:g> sur <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"zoom <xliff:g id="FIRST">%1$d</xliff:g> pour cent"</string>
<string name="desc_goto_link" msgid="2461368384824849714">"Accéder à la page <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
@@ -40,7 +40,7 @@
<string name="desc_phone_link" msgid="4986414958429253135">"Numéro de téléphone : <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"début de la sélection"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"fin de la sélection"</string>
- <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>–<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
+ <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>–<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_page_range" msgid="5286496438609641577">"pages <xliff:g id="FIRST">%1$d</xliff:g> à <xliff:g id="LAST">%2$d</xliff:g> sur <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_image_alt_text" msgid="7700601988820586333">"Image : <xliff:g id="ALT_TEXT">%1$s</xliff:g>"</string>
<string name="hint_find" msgid="5385388836603550565">"Trouver dans fichier"</string>
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"Page brisée pour le document PDF"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Données insuffisantes pour le traitement du document PDF"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"Impossible d\'ouvrir le fichier PDF"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Contenu copié à partir d\'un PDF"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-fr/strings.xml b/pdf/pdf-viewer/src/main/res/values-fr/strings.xml
index 05c1ad3..4c61f48f 100644
--- a/pdf/pdf-viewer/src/main/res/values-fr/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-fr/strings.xml
@@ -25,7 +25,7 @@
<string name="desc_password" msgid="6636473611443746739">"champ de mot de passe"</string>
<string name="desc_password_incorrect" msgid="4718823067483963845">"mot de passe incorrect"</string>
<string name="desc_password_incorrect_message" msgid="4676206571020946870">"Mot de passe incorrect. Réessayez."</string>
- <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"page <xliff:g id="PAGE">%1$d</xliff:g> sur <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"zoom : <xliff:g id="FIRST">%1$d</xliff:g> pour cent"</string>
<string name="desc_goto_link" msgid="2461368384824849714">"Accéder à la page <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
@@ -40,7 +40,7 @@
<string name="desc_phone_link" msgid="4986414958429253135">"Téléphone : <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"début de la sélection"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"fin de la sélection"</string>
- <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
+ <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_page_range" msgid="5286496438609641577">"pages <xliff:g id="FIRST">%1$d</xliff:g> à <xliff:g id="LAST">%2$d</xliff:g> sur <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_image_alt_text" msgid="7700601988820586333">"Image : <xliff:g id="ALT_TEXT">%1$s</xliff:g>"</string>
<string name="hint_find" msgid="5385388836603550565">"Rechercher dans fichier"</string>
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"Page non fonctionnelle pour le document PDF"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Données insuffisantes pour le traitement du document PDF"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"Impossible d\'ouvrir le fichier PDF"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Contenu copié depuis un PDF"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-gl/strings.xml b/pdf/pdf-viewer/src/main/res/values-gl/strings.xml
index b85aea9..7cbf622 100644
--- a/pdf/pdf-viewer/src/main/res/values-gl/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-gl/strings.xml
@@ -25,7 +25,7 @@
<string name="desc_password" msgid="6636473611443746739">"campo do contrasinal"</string>
<string name="desc_password_incorrect" msgid="4718823067483963845">"contrasinal incorrecto"</string>
<string name="desc_password_incorrect_message" msgid="4676206571020946870">"O contrasinal é incorrecto. Téntao de novo."</string>
- <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"páxina <xliff:g id="PAGE">%1$d</xliff:g> de <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"zoom ao <xliff:g id="FIRST">%1$d</xliff:g> por cento"</string>
<string name="desc_goto_link" msgid="2461368384824849714">"Vai á páxina <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
@@ -40,7 +40,7 @@
<string name="desc_phone_link" msgid="4986414958429253135">"Teléfono: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"inicio da selección"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"fin da selección"</string>
- <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
+ <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_page_range" msgid="5286496438609641577">"páxinas da <xliff:g id="FIRST">%1$d</xliff:g> á <xliff:g id="LAST">%2$d</xliff:g> dun total de <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_image_alt_text" msgid="7700601988820586333">"Imaxe: <xliff:g id="ALT_TEXT">%1$s</xliff:g>"</string>
<string name="hint_find" msgid="5385388836603550565">"Busca no ficheiro"</string>
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"Non funciona a páxina para o documento PDF"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Os datos non son suficientes para procesar o documento PDF"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"Non se puido abrir o PDF"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Copiouse o contido desde o PDF"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-gu/strings.xml b/pdf/pdf-viewer/src/main/res/values-gu/strings.xml
index a06a2b3..ade2e21 100644
--- a/pdf/pdf-viewer/src/main/res/values-gu/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-gu/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"PDF દસ્તાવેજ માટે પેજ લોડ થઈ રહ્યું નથી"</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF દસ્તાવેજ પર પ્રક્રિયા કરવા માટે પર્યાપ્ત ડેટા નથી"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"PDF ફાઇલ ખોલી શકાતી નથી"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"PDFમાંથી કન્ટેન્ટ કૉપિ કર્યું"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-hr/strings.xml b/pdf/pdf-viewer/src/main/res/values-hr/strings.xml
index 0b612cc..00660b7 100644
--- a/pdf/pdf-viewer/src/main/res/values-hr/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-hr/strings.xml
@@ -25,7 +25,7 @@
<string name="desc_password" msgid="6636473611443746739">"polje zaporke"</string>
<string name="desc_password_incorrect" msgid="4718823067483963845">"zaporka nije točna"</string>
<string name="desc_password_incorrect_message" msgid="4676206571020946870">"Pogrešna zaporka. Pokušajte ponovo."</string>
- <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"stranica <xliff:g id="PAGE">%1$d</xliff:g> od <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"zumiranje <xliff:g id="FIRST">%1$d</xliff:g> posto"</string>
<string name="desc_goto_link" msgid="2461368384824849714">"Idite na stranicu <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
@@ -40,7 +40,7 @@
<string name="desc_phone_link" msgid="4986414958429253135">"Telefonski broj: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"početak odabira"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"završetak odabira"</string>
- <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g> – <xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
+ <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g> – <xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_page_range" msgid="5286496438609641577">"stranice od <xliff:g id="FIRST">%1$d</xliff:g> do <xliff:g id="LAST">%2$d</xliff:g> od ukupno <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_image_alt_text" msgid="7700601988820586333">"Slika: <xliff:g id="ALT_TEXT">%1$s</xliff:g>"</string>
<string name="hint_find" msgid="5385388836603550565">"Pronađi u datoteci"</string>
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"Stranica je raščlanjena za PDF dokument"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Nema dovoljno podataka za obradu PDF dokumenta"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"PDF datoteka ne može se otvoriti"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Sadržaj kopiran iz PDF-a"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-hu/strings.xml b/pdf/pdf-viewer/src/main/res/values-hu/strings.xml
index 39d0f1e..71164e3 100644
--- a/pdf/pdf-viewer/src/main/res/values-hu/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-hu/strings.xml
@@ -25,7 +25,7 @@
<string name="desc_password" msgid="6636473611443746739">"jelszómező"</string>
<string name="desc_password_incorrect" msgid="4718823067483963845">"helytelen jelszó"</string>
<string name="desc_password_incorrect_message" msgid="4676206571020946870">"Helytelen jelszó. Próbálkozzon újra."</string>
- <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="TOTAL">%2$d</xliff:g> / <xliff:g id="PAGE">%1$d</xliff:g>."</string>
+ <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="TOTAL">%2$d</xliff:g>/<xliff:g id="PAGE">%1$d</xliff:g>."</string>
<string name="desc_page_single" msgid="8459583146661044094">"<xliff:g id="TOTAL">%2$d</xliff:g>/<xliff:g id="PAGE">%1$d</xliff:g>. oldal"</string>
<string name="desc_zoom" msgid="7318480946145947242">"<xliff:g id="FIRST">%1$d</xliff:g> százalékos nagyítás"</string>
<string name="desc_goto_link" msgid="2461368384824849714">"Ugrás erre az oldalra: <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
@@ -40,7 +40,7 @@
<string name="desc_phone_link" msgid="4986414958429253135">"Telefon: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"kijelölés kezdete"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"kijelölés vége"</string>
- <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="TOTAL">%3$d</xliff:g> / <xliff:g id="FIRST">%1$d</xliff:g>–<xliff:g id="LAST">%2$d</xliff:g>."</string>
+ <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="TOTAL">%3$d</xliff:g>/<xliff:g id="FIRST">%1$d</xliff:g>–<xliff:g id="LAST">%2$d</xliff:g>."</string>
<string name="desc_page_range" msgid="5286496438609641577">"<xliff:g id="TOTAL">%3$d</xliff:g>/<xliff:g id="FIRST">%1$d</xliff:g>–<xliff:g id="LAST">%2$d</xliff:g>. oldal"</string>
<string name="desc_image_alt_text" msgid="7700601988820586333">"Kép: <xliff:g id="ALT_TEXT">%1$s</xliff:g>"</string>
<string name="hint_find" msgid="5385388836603550565">"Keresés a fájlban"</string>
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"Az oldal nem tölt be a PDF-dokumentumban"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Nem áll rendelkezésre elegendő adat a PDF-dokumentum feldolgozásához"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"Nem sikerült megnyitni a PDF-fájlt"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Tartalom másolva PDF-ből"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-hy/strings.xml b/pdf/pdf-viewer/src/main/res/values-hy/strings.xml
index 2b64b83..be6da93 100644
--- a/pdf/pdf-viewer/src/main/res/values-hy/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-hy/strings.xml
@@ -25,7 +25,7 @@
<string name="desc_password" msgid="6636473611443746739">"գաղտնաբառի դաշտ"</string>
<string name="desc_password_incorrect" msgid="4718823067483963845">"գաղտնաբառը սխալ է"</string>
<string name="desc_password_incorrect_message" msgid="4676206571020946870">"Գաղտնաբառը սխալ է։ Նորից փորձեք։"</string>
- <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"Էջ <xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"մասշտաբ՝ <xliff:g id="FIRST">%1$d</xliff:g> տոկոս"</string>
<string name="desc_goto_link" msgid="2461368384824849714">"Անցեք <xliff:g id="PAGE_NUMBER">%1$d</xliff:g> էջ"</string>
@@ -40,7 +40,7 @@
<string name="desc_phone_link" msgid="4986414958429253135">"Հեռախոս՝ <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"ընտրված տեքստի սկիզբ"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"ընտրված տեքստի վերջ"</string>
- <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>–<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
+ <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>–<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_page_range" msgid="5286496438609641577">"էջ <xliff:g id="FIRST">%1$d</xliff:g>–<xliff:g id="LAST">%2$d</xliff:g>՝ <xliff:g id="TOTAL">%3$d</xliff:g>-ից"</string>
<string name="desc_image_alt_text" msgid="7700601988820586333">"Պատկեր՝ <xliff:g id="ALT_TEXT">%1$s</xliff:g>"</string>
<string name="hint_find" msgid="5385388836603550565">"Գտեք ֆայլում"</string>
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"PDF փաստաթղթի էջը վնասված է"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Ոչ բավարար տվյալներ PDF փաստաթղթի մշակման համար"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"Չհաջողվեց բացել PDF ֆայլը"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"PDF ֆայլից պատճենված բովանդակություն"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-in/strings.xml b/pdf/pdf-viewer/src/main/res/values-in/strings.xml
index b10c88d..9c3fa04 100644
--- a/pdf/pdf-viewer/src/main/res/values-in/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-in/strings.xml
@@ -25,7 +25,7 @@
<string name="desc_password" msgid="6636473611443746739">"kolom sandi"</string>
<string name="desc_password_incorrect" msgid="4718823067483963845">"sandi salah"</string>
<string name="desc_password_incorrect_message" msgid="4676206571020946870">"Sandi salah. Coba lagi."</string>
- <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"halaman <xliff:g id="PAGE">%1$d</xliff:g> dari <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"zoom <xliff:g id="FIRST">%1$d</xliff:g> persen"</string>
<string name="desc_goto_link" msgid="2461368384824849714">"Buka halaman <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"Halaman dokumen PDF rusak"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Data tidak cukup untuk memproses dokumen PDF"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"Tidak dapat membuka file PDF"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Konten disalin dari PDF"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-is/strings.xml b/pdf/pdf-viewer/src/main/res/values-is/strings.xml
index 3a9d53d3..0ab21f3 100644
--- a/pdf/pdf-viewer/src/main/res/values-is/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-is/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"Síða í PDF-skjali er gölluð"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Ekki næg gögn fyrir úrvinnslu á PDF-skjali"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"Ekki tókst að opna PDF-skrá"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Efni afritað úr PDF"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-it/strings.xml b/pdf/pdf-viewer/src/main/res/values-it/strings.xml
index 48da371..7412194 100644
--- a/pdf/pdf-viewer/src/main/res/values-it/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-it/strings.xml
@@ -25,7 +25,7 @@
<string name="desc_password" msgid="6636473611443746739">"campo password"</string>
<string name="desc_password_incorrect" msgid="4718823067483963845">"password errata"</string>
<string name="desc_password_incorrect_message" msgid="4676206571020946870">"Password errata. Riprova."</string>
- <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"pagina <xliff:g id="PAGE">%1$d</xliff:g> di <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"zoom <xliff:g id="FIRST">%1$d</xliff:g>%%"</string>
<string name="desc_goto_link" msgid="2461368384824849714">"Vai alla pagina <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
@@ -40,7 +40,7 @@
<string name="desc_phone_link" msgid="4986414958429253135">"Telefono: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"inizio selezione"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"fine selezione"</string>
- <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
+ <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_page_range" msgid="5286496438609641577">"pagine da <xliff:g id="FIRST">%1$d</xliff:g> a <xliff:g id="LAST">%2$d</xliff:g> di <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_image_alt_text" msgid="7700601988820586333">"Immagine: <xliff:g id="ALT_TEXT">%1$s</xliff:g>"</string>
<string name="hint_find" msgid="5385388836603550565">"Trova nel file"</string>
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"Pagina inaccessibile per il documento PDF"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Dati insufficienti per l\'elaborazione del documento PDF"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"Impossibile aprire il file PDF"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Contenuti copiati dal PDF"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-iw/strings.xml b/pdf/pdf-viewer/src/main/res/values-iw/strings.xml
index 294e58c..1383134 100644
--- a/pdf/pdf-viewer/src/main/res/values-iw/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-iw/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"קישור מנותק בדף למסמך ה-PDF"</string>
<string name="needs_more_data" msgid="3520133467908240802">"אין מספיק נתונים כדי לעבד את מסמך ה-PDF"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"לא ניתן לפתוח את קובץ ה-PDF"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"התוכן הועתק מקובץ PDF"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-ja/strings.xml b/pdf/pdf-viewer/src/main/res/values-ja/strings.xml
index 1684664..76ee961 100644
--- a/pdf/pdf-viewer/src/main/res/values-ja/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ja/strings.xml
@@ -25,7 +25,7 @@
<string name="desc_password" msgid="6636473611443746739">"パスワードを入力する項目です"</string>
<string name="desc_password_incorrect" msgid="4718823067483963845">"パスワードが正しくありません"</string>
<string name="desc_password_incorrect_message" msgid="4676206571020946870">"パスワードが正しくありません。もう一度お試しください。"</string>
- <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g> ページ"</string>
<string name="desc_zoom" msgid="7318480946145947242">"ズーム <xliff:g id="FIRST">%1$d</xliff:g> %%"</string>
<string name="desc_goto_link" msgid="2461368384824849714">"<xliff:g id="PAGE_NUMBER">%1$d</xliff:g> ページに移動します"</string>
@@ -40,7 +40,7 @@
<string name="desc_phone_link" msgid="4986414958429253135">"電話番号: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"選択範囲の最初"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"選択範囲の最後"</string>
- <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>~<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
+ <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>~<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_page_range" msgid="5286496438609641577">"<xliff:g id="FIRST">%1$d</xliff:g>~<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g> ページ"</string>
<string name="desc_image_alt_text" msgid="7700601988820586333">"画像: <xliff:g id="ALT_TEXT">%1$s</xliff:g>"</string>
<string name="hint_find" msgid="5385388836603550565">"ファイル内を検索"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-kk/strings.xml b/pdf/pdf-viewer/src/main/res/values-kk/strings.xml
index 4047aae..a2c32ee 100644
--- a/pdf/pdf-viewer/src/main/res/values-kk/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-kk/strings.xml
@@ -25,7 +25,7 @@
<string name="desc_password" msgid="6636473611443746739">"құпия сөз өрісі"</string>
<string name="desc_password_incorrect" msgid="4718823067483963845">"құпия сөз қате"</string>
<string name="desc_password_incorrect_message" msgid="4676206571020946870">"Құпия сөз қате. Қайталап көріңіз."</string>
- <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"Бет: <xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"<xliff:g id="FIRST">%1$d</xliff:g> пайызға масштабтау"</string>
<string name="desc_goto_link" msgid="2461368384824849714">"<xliff:g id="PAGE_NUMBER">%1$d</xliff:g>-бетке өтіңіз."</string>
@@ -40,7 +40,7 @@
<string name="desc_phone_link" msgid="4986414958429253135">"Телефон: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"таңдалған мәтіннің басы"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"таңдалған мәтіннің соңы"</string>
- <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
+ <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_page_range" msgid="5286496438609641577">"Бет: <xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_image_alt_text" msgid="7700601988820586333">"Сурет: <xliff:g id="ALT_TEXT">%1$s</xliff:g>."</string>
<string name="hint_find" msgid="5385388836603550565">"Файлдан табу"</string>
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"PDF құжатының беті бұзылған."</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF құжатын өңдеу үшін деректер жеткіліксіз."</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"PDF файлын ашу мүмкін емес."</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"PDF файлынан көшірілген контент"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-km/strings.xml b/pdf/pdf-viewer/src/main/res/values-km/strings.xml
index 8192706..8dc92e4 100644
--- a/pdf/pdf-viewer/src/main/res/values-km/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-km/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"ទំព័រមិនដំណើរការសម្រាប់ឯកសារ PDF"</string>
<string name="needs_more_data" msgid="3520133467908240802">"មានទិន្នន័យមិនគ្រប់គ្រាន់សម្រាប់ដំណើរការឯកសារ PDF"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"មិនអាចបើកឯកសារ PDF បានទេ"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"បានចម្លងខ្លឹមសារពី PDF"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-kn/strings.xml b/pdf/pdf-viewer/src/main/res/values-kn/strings.xml
index a03105f..ccb316d 100644
--- a/pdf/pdf-viewer/src/main/res/values-kn/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-kn/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"PDF ಡಾಕ್ಯುಮೆಂಟ್ಗೆ ಸಂಬಂಧಿಸಿದ ಪುಟ ಮುರಿದಿದೆ"</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF ಡಾಕ್ಯುಮೆಂಟ್ ಅನ್ನು ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲು ಸಾಕಷ್ಟು ಡೇಟಾ ಇಲ್ಲ"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"PDF ಫೈಲ್ ಅನ್ನು ತೆರೆಯಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"ಕಂಟೆಂಟ್ ಅನ್ನು PDF ನಿಂದ ಕಾಪಿ ಮಾಡಲಾಗಿದೆ"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-ko/strings.xml b/pdf/pdf-viewer/src/main/res/values-ko/strings.xml
index 3cfb9f1..b334d32 100644
--- a/pdf/pdf-viewer/src/main/res/values-ko/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ko/strings.xml
@@ -25,7 +25,7 @@
<string name="desc_password" msgid="6636473611443746739">"비밀번호 입력란"</string>
<string name="desc_password_incorrect" msgid="4718823067483963845">"비밀번호가 잘못됨"</string>
<string name="desc_password_incorrect_message" msgid="4676206571020946870">"잘못된 비밀번호입니다. 다시 시도하세요."</string>
- <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"<xliff:g id="TOTAL">%2$d</xliff:g>페이지 중 <xliff:g id="PAGE">%1$d</xliff:g>페이지"</string>
<string name="desc_zoom" msgid="7318480946145947242">"<xliff:g id="FIRST">%1$d</xliff:g>%% 확대/축소"</string>
<string name="desc_goto_link" msgid="2461368384824849714">"<xliff:g id="PAGE_NUMBER">%1$d</xliff:g> 페이지로 이동"</string>
@@ -40,7 +40,7 @@
<string name="desc_phone_link" msgid="4986414958429253135">"전화번호: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"선택 영역 시작"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"선택 영역 끝"</string>
- <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>~<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
+ <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>~<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_page_range" msgid="5286496438609641577">"<xliff:g id="TOTAL">%3$d</xliff:g>페이지 중 <xliff:g id="FIRST">%1$d</xliff:g>~<xliff:g id="LAST">%2$d</xliff:g>페이지"</string>
<string name="desc_image_alt_text" msgid="7700601988820586333">"이미지: <xliff:g id="ALT_TEXT">%1$s</xliff:g>"</string>
<string name="hint_find" msgid="5385388836603550565">"파일에서 찾기"</string>
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"PDF 문서의 페이지가 손상되었습니다."</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF 문서 처리를 위한 데이터가 부족합니다."</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"PDF 파일을 열 수 없음"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"PDF에서 복사된 콘텐츠"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-ky/strings.xml b/pdf/pdf-viewer/src/main/res/values-ky/strings.xml
index c6efa08..9b96c97 100644
--- a/pdf/pdf-viewer/src/main/res/values-ky/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ky/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"PDF документинин барагы бузук"</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF документин иштетүү үчүн маалымат жетишсиз"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"PDF файл ачылбай жатат"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Контент PDF\'тен көчүрүлдү"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-lo/strings.xml b/pdf/pdf-viewer/src/main/res/values-lo/strings.xml
index dacf6fa..b737461 100644
--- a/pdf/pdf-viewer/src/main/res/values-lo/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-lo/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"ໜ້າເສຍຫາຍສໍາລັບເອກະສານ PDF"</string>
<string name="needs_more_data" msgid="3520133467908240802">"ຂໍ້ມູນບໍ່ພຽງພໍສໍາລັບການປະມວນຜົນເອກະສານ PDF"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"ບໍ່ສາມາດເປີດໄຟລ໌ PDF"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"ສໍາເນົາເນື້ອຫາຈາກ PDF ແລ້ວ"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-lt/strings.xml b/pdf/pdf-viewer/src/main/res/values-lt/strings.xml
index 9a284fe..e260cf5 100644
--- a/pdf/pdf-viewer/src/main/res/values-lt/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-lt/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"Sugadintas PDF dokumento puslapis"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Nepakanka duomenų PDF dokumentui apdoroti"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"Nepavyksta atidaryti PDF failo"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Turinys nukopijuotas iš PDF"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-lv/strings.xml b/pdf/pdf-viewer/src/main/res/values-lv/strings.xml
index 3dce52e..7d455ea 100644
--- a/pdf/pdf-viewer/src/main/res/values-lv/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-lv/strings.xml
@@ -25,7 +25,7 @@
<string name="desc_password" msgid="6636473611443746739">"paroles lauks"</string>
<string name="desc_password_incorrect" msgid="4718823067483963845">"parole nav pareiza"</string>
<string name="desc_password_incorrect_message" msgid="4676206571020946870">"Nepareiza parole. Mēģiniet vēlreiz."</string>
- <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"<xliff:g id="PAGE">%1$d</xliff:g>. lapa no <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"tālummaiņa procentos ir <xliff:g id="FIRST">%1$d</xliff:g>"</string>
<string name="desc_goto_link" msgid="2461368384824849714">"Doties uz <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>. lapu"</string>
@@ -40,7 +40,7 @@
<string name="desc_phone_link" msgid="4986414958429253135">"Tālruņa numurs: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"atlases sākums"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"atlases beigas"</string>
- <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>–<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
+ <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>–<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_page_range" msgid="5286496438609641577">"<xliff:g id="FIRST">%1$d</xliff:g>.–<xliff:g id="LAST">%2$d</xliff:g>. lapa no <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_image_alt_text" msgid="7700601988820586333">"Attēls: <xliff:g id="ALT_TEXT">%1$s</xliff:g>"</string>
<string name="hint_find" msgid="5385388836603550565">"Meklēt failā"</string>
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"PDF dokumenta lapa ir bojāta"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Nepietiekams datu apjoms, lai apstrādātu PDF dokumentu"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"Nevar atvērt PDF failu."</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"No PDF faila kopēts saturs"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-mk/strings.xml b/pdf/pdf-viewer/src/main/res/values-mk/strings.xml
index 1d569ea..5735451 100644
--- a/pdf/pdf-viewer/src/main/res/values-mk/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-mk/strings.xml
@@ -25,7 +25,7 @@
<string name="desc_password" msgid="6636473611443746739">"поле за лозинка"</string>
<string name="desc_password_incorrect" msgid="4718823067483963845">"лозинката е неточна"</string>
<string name="desc_password_incorrect_message" msgid="4676206571020946870">"Погрешна лозинка. Обидете се повторно."</string>
- <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"страница <xliff:g id="PAGE">%1$d</xliff:g> од <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"зумирајте <xliff:g id="FIRST">%1$d</xliff:g> проценти"</string>
<string name="desc_goto_link" msgid="2461368384824849714">"Одете на страницата <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
@@ -40,7 +40,7 @@
<string name="desc_phone_link" msgid="4986414958429253135">"Телефон: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"почеток на изборот"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"крај на изборот"</string>
- <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g> – <xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
+ <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g> – <xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_page_range" msgid="5286496438609641577">"од страница <xliff:g id="FIRST">%1$d</xliff:g> до <xliff:g id="LAST">%2$d</xliff:g> од <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_image_alt_text" msgid="7700601988820586333">"Слика: <xliff:g id="ALT_TEXT">%1$s</xliff:g>"</string>
<string name="hint_find" msgid="5385388836603550565">"Најдете во датотека"</string>
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"Страницата не може да го вчита PDF-документот"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Недоволно податоци за обработка на PDF-документот"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"Не може да се отвори PDF-датотеката"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Содржините се копирани од PDF"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-mn/strings.xml b/pdf/pdf-viewer/src/main/res/values-mn/strings.xml
index 930c2b7..4198e49 100644
--- a/pdf/pdf-viewer/src/main/res/values-mn/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-mn/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"PDF баримт бичгийн хуудас эвдэрсэн"</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF баримт бичгийг боловсруулахад өгөгдөл хангалтгүй байна"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"PDF файлыг нээх боломжгүй"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"PDF-с контент хуулсан"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-mr/strings.xml b/pdf/pdf-viewer/src/main/res/values-mr/strings.xml
index 41b2446..01afbd7 100644
--- a/pdf/pdf-viewer/src/main/res/values-mr/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-mr/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"पीडीएफ दस्तऐवजासाठी पेज खंडित झाले आहे"</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF दस्तऐवजावर प्रक्रिया करण्यासाठी डेटा पुरेसा नाही"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"PDF फाइल उघडू शकत नाही"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"आशय PDF वरून कॉपी केला आहे"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-my/strings.xml b/pdf/pdf-viewer/src/main/res/values-my/strings.xml
index f7705fe..4a00bad 100644
--- a/pdf/pdf-viewer/src/main/res/values-my/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-my/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"PDF မှတ်တမ်းအတွက် စာမျက်နှာ ပျက်နေသည်"</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF မှတ်တမ်း လုပ်ဆောင်ရန်အတွက် ဒေတာ မလုံလောက်ပါ"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"PDF ဖိုင်ကို ဖွင့်၍မရပါ"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"PDF မှ မိတ္တူကူးထားသည့် အကြောင်းအရာ"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-nb/strings.xml b/pdf/pdf-viewer/src/main/res/values-nb/strings.xml
index fd76435..bd09342 100644
--- a/pdf/pdf-viewer/src/main/res/values-nb/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-nb/strings.xml
@@ -25,7 +25,7 @@
<string name="desc_password" msgid="6636473611443746739">"passordfelt"</string>
<string name="desc_password_incorrect" msgid="4718823067483963845">"feil passord"</string>
<string name="desc_password_incorrect_message" msgid="4676206571020946870">"Feil passord. Prøv på nytt."</string>
- <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"side <xliff:g id="PAGE">%1$d</xliff:g> av <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"zoom <xliff:g id="FIRST">%1$d</xliff:g> prosent"</string>
<string name="desc_goto_link" msgid="2461368384824849714">"Gå til side <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
@@ -40,7 +40,7 @@
<string name="desc_phone_link" msgid="4986414958429253135">"Telefonnummer: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"starten av utvalget"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"slutten av utvalget"</string>
- <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>–<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
+ <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>–<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_page_range" msgid="5286496438609641577">"side <xliff:g id="FIRST">%1$d</xliff:g> til <xliff:g id="LAST">%2$d</xliff:g> av <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_image_alt_text" msgid="7700601988820586333">"Bilde: <xliff:g id="ALT_TEXT">%1$s</xliff:g>"</string>
<string name="hint_find" msgid="5385388836603550565">"Finn i filen"</string>
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"Siden er ødelagt for PDF-dokumentet"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Det er utilstrekkelige data for behandling av PDF-dokumentet"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"Kan ikke åpne PDF-filen"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Innholdet er kopiert fra en PDF"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-ne/strings.xml b/pdf/pdf-viewer/src/main/res/values-ne/strings.xml
index ca0437a..4d6929b 100644
--- a/pdf/pdf-viewer/src/main/res/values-ne/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ne/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"PDF डकुमेन्टको पेज लोड गर्न सकिएन"</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF डकुमेन्ट प्रोसेस गर्न पर्याप्त जानकारी छैन"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"PDF फाइल खोल्न सकिएन"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"PDF बाट कपी गरिएको सामग्री"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-nl/strings.xml b/pdf/pdf-viewer/src/main/res/values-nl/strings.xml
index 2f98f70..0b6fbe0 100644
--- a/pdf/pdf-viewer/src/main/res/values-nl/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-nl/strings.xml
@@ -25,7 +25,7 @@
<string name="desc_password" msgid="6636473611443746739">"wachtwoordveld"</string>
<string name="desc_password_incorrect" msgid="4718823067483963845">"wachtwoord is onjuist"</string>
<string name="desc_password_incorrect_message" msgid="4676206571020946870">"Onjuist wachtwoord. Probeer het opnieuw."</string>
- <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"pagina <xliff:g id="PAGE">%1$d</xliff:g> van <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"zoom <xliff:g id="FIRST">%1$d</xliff:g> procent"</string>
<string name="desc_goto_link" msgid="2461368384824849714">"Ga naar pagina <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"Pagina van het pdf-document kan niet worden geladen"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Onvoldoende gegevens om het pdf-document te verwerken"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"Kan pdf-bestand niet openen"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Content gekopieerd uit pdf"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-or/strings.xml b/pdf/pdf-viewer/src/main/res/values-or/strings.xml
index 8a0582a..e6808dc 100644
--- a/pdf/pdf-viewer/src/main/res/values-or/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-or/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"PDF ଡକ୍ୟୁମେଣ୍ଟ ପାଇଁ ପୃଷ୍ଠା ବିଭାଜିତ ହୋଇଛି"</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF ଡକ୍ୟୁମେଣ୍ଟ ପ୍ରକ୍ରିୟାକରଣ ପାଇଁ ପର୍ଯ୍ୟାପ୍ତ ଡାଟା ନାହିଁ"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"PDF ଫାଇଲକୁ ଖୋଲାଯାଇପାରିବ ନାହିଁ"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"PDFରୁ ବିଷୟବସ୍ତୁ କପି କରାଯାଇଛି"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-pa/strings.xml b/pdf/pdf-viewer/src/main/res/values-pa/strings.xml
index ff925a3..46bfbf6 100644
--- a/pdf/pdf-viewer/src/main/res/values-pa/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-pa/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"PDF ਦਸਤਾਵੇਜ਼ ਲਈ ਪੰਨਾ ਲੋਡ ਨਹੀਂ ਹੋ ਰਿਹਾ"</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF ਦਸਤਾਵੇਜ਼ \'ਤੇ ਪ੍ਰਕਿਰਿਆ ਕਰਨ ਲਈ ਲੋੜੀਂਦਾ ਡਾਟਾ ਨਹੀਂ ਹੈ"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"PDF ਫ਼ਾਈਲ ਖੋਲ੍ਹੀ ਨਹੀਂ ਜਾ ਸਕਦੀ"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"PDF ਤੋਂ ਕਾਪੀ ਕੀਤੀ ਗਈ ਸਮੱਗਰੀ"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-pl/strings.xml b/pdf/pdf-viewer/src/main/res/values-pl/strings.xml
index c06a100..bcd1878 100644
--- a/pdf/pdf-viewer/src/main/res/values-pl/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-pl/strings.xml
@@ -25,7 +25,7 @@
<string name="desc_password" msgid="6636473611443746739">"pole hasła"</string>
<string name="desc_password_incorrect" msgid="4718823067483963845">"nieprawidłowe hasło"</string>
<string name="desc_password_incorrect_message" msgid="4676206571020946870">"Błędne hasło. Spróbuj ponownie."</string>
- <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"strona <xliff:g id="PAGE">%1$d</xliff:g> z <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"powiększenie <xliff:g id="FIRST">%1$d</xliff:g> procent"</string>
<string name="desc_goto_link" msgid="2461368384824849714">"Idź do strony <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-pt-rBR/strings.xml b/pdf/pdf-viewer/src/main/res/values-pt-rBR/strings.xml
index 87937e2..69988f4 100644
--- a/pdf/pdf-viewer/src/main/res/values-pt-rBR/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-pt-rBR/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"Página do documento PDF corrompida"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Dados insuficientes para processamento do documento PDF"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"Não é possível abrir o arquivo PDF"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Conteúdo copiado do PDF"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-pt-rPT/strings.xml b/pdf/pdf-viewer/src/main/res/values-pt-rPT/strings.xml
index d9db7c2..9b603b9 100644
--- a/pdf/pdf-viewer/src/main/res/values-pt-rPT/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-pt-rPT/strings.xml
@@ -25,7 +25,7 @@
<string name="desc_password" msgid="6636473611443746739">"campo palavra-passe"</string>
<string name="desc_password_incorrect" msgid="4718823067483963845">"palavra-passe incorreta"</string>
<string name="desc_password_incorrect_message" msgid="4676206571020946870">"Palavra-passe incorreta. Tente novamente."</string>
- <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"página <xliff:g id="PAGE">%1$d</xliff:g> de <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"zoom a <xliff:g id="FIRST">%1$d</xliff:g> por cento"</string>
<string name="desc_goto_link" msgid="2461368384824849714">"Ir para a página <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
@@ -40,7 +40,7 @@
<string name="desc_phone_link" msgid="4986414958429253135">"Telefone: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"início da seleção"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"fim da seleção"</string>
- <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
+ <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_page_range" msgid="5286496438609641577">"páginas <xliff:g id="FIRST">%1$d</xliff:g> a <xliff:g id="LAST">%2$d</xliff:g> de <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_image_alt_text" msgid="7700601988820586333">"Imagem: <xliff:g id="ALT_TEXT">%1$s</xliff:g>"</string>
<string name="hint_find" msgid="5385388836603550565">"Procure no ficheiro"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-pt/strings.xml b/pdf/pdf-viewer/src/main/res/values-pt/strings.xml
index 87937e2..69988f4 100644
--- a/pdf/pdf-viewer/src/main/res/values-pt/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-pt/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"Página do documento PDF corrompida"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Dados insuficientes para processamento do documento PDF"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"Não é possível abrir o arquivo PDF"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Conteúdo copiado do PDF"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-ro/strings.xml b/pdf/pdf-viewer/src/main/res/values-ro/strings.xml
index 1d04a93..96bb18b 100644
--- a/pdf/pdf-viewer/src/main/res/values-ro/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ro/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"Pagină deteriorată pentru documentul PDF"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Date insuficiente pentru procesarea documentului PDF"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"Nu se poate deschide fișierul PDF"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Conținutul a fost copiat din PDF"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-ru/strings.xml b/pdf/pdf-viewer/src/main/res/values-ru/strings.xml
index c4670b90..7776686 100644
--- a/pdf/pdf-viewer/src/main/res/values-ru/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ru/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"Страница документа PDF повреждена"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Недостаточно данных для обработки документа PDF"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"Не удается открыть PDF-файл."</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Скопировано из PDF-файла"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-si/strings.xml b/pdf/pdf-viewer/src/main/res/values-si/strings.xml
index d92f11a..247104e 100644
--- a/pdf/pdf-viewer/src/main/res/values-si/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-si/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"PDF ලේඛනය සඳහා පිටුව හානි වී ඇත"</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF ලේඛනය සැකසීම සඳහා ප්රමාණවත් දත්ත නොමැත"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"PDF ගොනුව විවෘත කළ නොහැක"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"PDF වෙතින් පිටපත් කළ අන්තර්ගතය"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-sk/strings.xml b/pdf/pdf-viewer/src/main/res/values-sk/strings.xml
index dcf8c81..f0fa238 100644
--- a/pdf/pdf-viewer/src/main/res/values-sk/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-sk/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"Stránka sa v dokumente vo formáte PDF nedá načítať"</string>
<string name="needs_more_data" msgid="3520133467908240802">"V dokumente vo formáte PDF nie je dostatok údajov na spracovanie"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"Súbor PDF sa nepodarilo otvoriť"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Obsah bol skopírovaný zo súboru PDF"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-sl/strings.xml b/pdf/pdf-viewer/src/main/res/values-sl/strings.xml
index b57be5b..9c2488a 100644
--- a/pdf/pdf-viewer/src/main/res/values-sl/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-sl/strings.xml
@@ -25,7 +25,7 @@
<string name="desc_password" msgid="6636473611443746739">"polje za geslo"</string>
<string name="desc_password_incorrect" msgid="4718823067483963845">"napačno geslo"</string>
<string name="desc_password_incorrect_message" msgid="4676206571020946870">"Napačno geslo. Poskusite znova."</string>
- <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"stran <xliff:g id="PAGE">%1$d</xliff:g> od <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"povečava <xliff:g id="FIRST">%1$d</xliff:g> %%"</string>
<string name="desc_goto_link" msgid="2461368384824849714">"Pojdi na stran <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
@@ -40,7 +40,7 @@
<string name="desc_phone_link" msgid="4986414958429253135">"Telefon: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"začetek izbire"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"konec izbire"</string>
- <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>–<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
+ <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>–<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_page_range" msgid="5286496438609641577">"strani <xliff:g id="FIRST">%1$d</xliff:g> do <xliff:g id="LAST">%2$d</xliff:g> od <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_image_alt_text" msgid="7700601988820586333">"Slika: <xliff:g id="ALT_TEXT">%1$s</xliff:g>"</string>
<string name="hint_find" msgid="5385388836603550565">"Iskanje v datoteki"</string>
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"Strani iz dokumenta PDF ni mogoče prikazati"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Nezadostni podatki za obdelavo dokumenta PDF"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"Datoteke PDF ni mogoče odpreti"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Vsebina je kopirana iz datoteke PDF"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-sq/strings.xml b/pdf/pdf-viewer/src/main/res/values-sq/strings.xml
index 517bdae..8a47505 100644
--- a/pdf/pdf-viewer/src/main/res/values-sq/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-sq/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"Faqe e dëmtuar për dokumentin PDF"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Të dhëna të pamjaftueshme për përpunimin e dokumentit PDF"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"Skedari PDF nuk mund të hapet"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Përmbajtja e kopjuar nga PDF-ja"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-sr/strings.xml b/pdf/pdf-viewer/src/main/res/values-sr/strings.xml
index 305c103..0d69d5e 100644
--- a/pdf/pdf-viewer/src/main/res/values-sr/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-sr/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"Неисправна страница за PDF документ"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Недовољно података за обраду PDF документа"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"Отварање PDF фајла није успело"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Садржај је копиран из PDF-а"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-sv/strings.xml b/pdf/pdf-viewer/src/main/res/values-sv/strings.xml
index 53838ff..abcca45 100644
--- a/pdf/pdf-viewer/src/main/res/values-sv/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-sv/strings.xml
@@ -25,7 +25,7 @@
<string name="desc_password" msgid="6636473611443746739">"lösenordsfält"</string>
<string name="desc_password_incorrect" msgid="4718823067483963845">"felaktigt lösenord"</string>
<string name="desc_password_incorrect_message" msgid="4676206571020946870">"Fel lösenord. Försök igen."</string>
- <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"sida <xliff:g id="PAGE">%1$d</xliff:g> av <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"zooma <xliff:g id="FIRST">%1$d</xliff:g> procent"</string>
<string name="desc_goto_link" msgid="2461368384824849714">"Öppna sidan <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
@@ -40,7 +40,7 @@
<string name="desc_phone_link" msgid="4986414958429253135">"Telefonnummer: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"textmarkeringens början"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"textmarkeringens slut"</string>
- <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>–<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
+ <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>–<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_page_range" msgid="5286496438609641577">"sidorna <xliff:g id="FIRST">%1$d</xliff:g> till <xliff:g id="LAST">%2$d</xliff:g> av <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_image_alt_text" msgid="7700601988820586333">"Bild: <xliff:g id="ALT_TEXT">%1$s</xliff:g>"</string>
<string name="hint_find" msgid="5385388836603550565">"Hitta i filen"</string>
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"Det gick inte att läsa in en sida i PDF-dokumentet"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Otillräcklig data för att behandla PDF-dokumentet"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"Det går inte att öppna PDF-filen"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Innehåll har kopierats från PDF"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-sw/strings.xml b/pdf/pdf-viewer/src/main/res/values-sw/strings.xml
index 19d3afe..1d60764 100644
--- a/pdf/pdf-viewer/src/main/res/values-sw/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-sw/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"Ukurasa wa hati ya PDF una tatizo"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Hamna data ya kutosha kuchakata hati ya PDF"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"Imeshindwa kufungua faili ya PDF"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Imenakili maudhui kutoka PDF"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-ta/strings.xml b/pdf/pdf-viewer/src/main/res/values-ta/strings.xml
index 3a526a2..0ac2405 100644
--- a/pdf/pdf-viewer/src/main/res/values-ta/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ta/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"PDF ஆவணத்தை ஏற்ற முடியவில்லை"</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF ஆவணத்தைச் செயலாக்குவதற்குப் போதுமான தரவு இல்லை"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"PDF ஃபைலைத் திறக்க முடியவில்லை"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"PDFல் இருந்து நகலெடுக்கப்பட்ட உள்ளடக்கம்"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-te/strings.xml b/pdf/pdf-viewer/src/main/res/values-te/strings.xml
index 2c91b5c..ad559a5 100644
--- a/pdf/pdf-viewer/src/main/res/values-te/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-te/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"PDF డాక్యుమెంట్కు సంబంధించి పేజీ బ్రేక్ అయింది"</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF డాక్యుమెంట్ను ప్రాసెస్ చేయడానికి డేటా తగినంత లేదు"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"PDF ఫైల్ను తెరవడం సాధ్యపడదు"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"కంటెంట్ PDF నుండి కాపీ అయింది"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-th/strings.xml b/pdf/pdf-viewer/src/main/res/values-th/strings.xml
index 2f73d7c..d930711 100644
--- a/pdf/pdf-viewer/src/main/res/values-th/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-th/strings.xml
@@ -25,7 +25,7 @@
<string name="desc_password" msgid="6636473611443746739">"ช่องรหัสผ่าน"</string>
<string name="desc_password_incorrect" msgid="4718823067483963845">"รหัสผ่านไม่ถูกต้อง"</string>
<string name="desc_password_incorrect_message" msgid="4676206571020946870">"รหัสผ่านไม่ถูกต้อง โปรดลองอีกครั้ง"</string>
- <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"หน้า <xliff:g id="PAGE">%1$d</xliff:g> จาก <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"ซูม <xliff:g id="FIRST">%1$d</xliff:g> เปอร์เซ็นต์"</string>
<string name="desc_goto_link" msgid="2461368384824849714">"ไปที่หน้า <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
@@ -40,7 +40,7 @@
<string name="desc_phone_link" msgid="4986414958429253135">"หมายเลขโทรศัพท์: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"เริ่มส่วนที่เลือก"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"สิ้นสุดส่วนที่เลือก"</string>
- <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
+ <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_page_range" msgid="5286496438609641577">"หน้า <xliff:g id="FIRST">%1$d</xliff:g> ถึง <xliff:g id="LAST">%2$d</xliff:g> จาก <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_image_alt_text" msgid="7700601988820586333">"รูปภาพ: <xliff:g id="ALT_TEXT">%1$s</xliff:g>"</string>
<string name="hint_find" msgid="5385388836603550565">"ค้นหาในไฟล์"</string>
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"หน้าในเอกสาร PDF เสียหาย"</string>
<string name="needs_more_data" msgid="3520133467908240802">"ข้อมูลไม่เพียงพอสำหรับการประมวลผลเอกสาร PDF"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"เปิดไฟล์ PDF ไม่ได้"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"เนื้อหาที่คัดลอกมาจาก PDF"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-tl/strings.xml b/pdf/pdf-viewer/src/main/res/values-tl/strings.xml
index 1d51aaa..de78569 100644
--- a/pdf/pdf-viewer/src/main/res/values-tl/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-tl/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"Sira ang page para sa PDF na dokumento"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Kulang ang data para maproseso ang PDF na dokumento"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"Hindi mabuksan ang PDF file"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Nakopya ang content mula sa PDF"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-tr/strings.xml b/pdf/pdf-viewer/src/main/res/values-tr/strings.xml
index 14b1abc..de94f9c 100644
--- a/pdf/pdf-viewer/src/main/res/values-tr/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-tr/strings.xml
@@ -25,7 +25,7 @@
<string name="desc_password" msgid="6636473611443746739">"şifre alanı"</string>
<string name="desc_password_incorrect" msgid="4718823067483963845">"şifre yanlış"</string>
<string name="desc_password_incorrect_message" msgid="4676206571020946870">"Yanlış şifre. Tekrar deneyin."</string>
- <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"sayfa <xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"yakınlaştırma yüzdesi <xliff:g id="FIRST">%1$d</xliff:g>"</string>
<string name="desc_goto_link" msgid="2461368384824849714">"Şu sayfaya git <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
@@ -40,7 +40,7 @@
<string name="desc_phone_link" msgid="4986414958429253135">"Telefon: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"seçim başlangıcı"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"seçim sonu"</string>
- <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
+ <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_page_range" msgid="5286496438609641577">"sayfa <xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_image_alt_text" msgid="7700601988820586333">"Resim: <xliff:g id="ALT_TEXT">%1$s</xliff:g>"</string>
<string name="hint_find" msgid="5385388836603550565">"Dosyada bul"</string>
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"PDF dokümanının sayfası bozuk"</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF dokümanını işleyecek kadar yeterli veri yok"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"PDF dosyası açılamıyor"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"PDF\'den kopyalanan içerik"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-uk/strings.xml b/pdf/pdf-viewer/src/main/res/values-uk/strings.xml
index c622b57..cc835f2 100644
--- a/pdf/pdf-viewer/src/main/res/values-uk/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-uk/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"Сторінку документа PDF пошкоджено"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Недостатньо даних для обробки документа PDF"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"Не вдалося відкрити файл PDF"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Контент скопійовано з PDF-файлу"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-ur/strings.xml b/pdf/pdf-viewer/src/main/res/values-ur/strings.xml
index 6b542e8..14c25d6 100644
--- a/pdf/pdf-viewer/src/main/res/values-ur/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ur/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"PDF دستاویز کیلئے شکستہ صفحہ"</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF دستاویز پر کارروائی کرنے کیلئے ڈیٹا ناکافی ہے"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"PDF فائل کو کھولا نہیں جا سکتا"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"PDF سے کاپی کیا گیا مواد"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-uz/strings.xml b/pdf/pdf-viewer/src/main/res/values-uz/strings.xml
index 0dfea8a..0b7c2cc 100644
--- a/pdf/pdf-viewer/src/main/res/values-uz/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-uz/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"PDF hujjat sahifasi yaroqsiz"</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF hujjatni qayta ishlash uchun kerakli axborotlar yetarli emas"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"PDF fayk ochilmadi"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Kontent PDF fayldan nusxalandi"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-vi/strings.xml b/pdf/pdf-viewer/src/main/res/values-vi/strings.xml
index 76e2782..fc6c2b1 100644
--- a/pdf/pdf-viewer/src/main/res/values-vi/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-vi/strings.xml
@@ -25,7 +25,7 @@
<string name="desc_password" msgid="6636473611443746739">"trường nhập mật khẩu"</string>
<string name="desc_password_incorrect" msgid="4718823067483963845">"mật khẩu không chính xác"</string>
<string name="desc_password_incorrect_message" msgid="4676206571020946870">"Sai mật khẩu. Hãy thử lại."</string>
- <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"trang <xliff:g id="PAGE">%1$d</xliff:g> trong số <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"thu phóng <xliff:g id="FIRST">%1$d</xliff:g> phần trăm"</string>
<string name="desc_goto_link" msgid="2461368384824849714">"Chuyển đến trang <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
@@ -40,7 +40,7 @@
<string name="desc_phone_link" msgid="4986414958429253135">"Số điện thoại: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"đầu văn bản đã chọn"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"cuối văn bản đã chọn"</string>
- <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g> – <xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
+ <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g> – <xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_page_range" msgid="5286496438609641577">"các trang <xliff:g id="FIRST">%1$d</xliff:g> đến <xliff:g id="LAST">%2$d</xliff:g> trong số <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_image_alt_text" msgid="7700601988820586333">"Hình ảnh: <xliff:g id="ALT_TEXT">%1$s</xliff:g>"</string>
<string name="hint_find" msgid="5385388836603550565">"Tìm trong tệp"</string>
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"Tài liệu PDF này bị lỗi trang"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Không đủ dữ liệu để xử lý tài liệu PDF này"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"Không mở được tệp PDF"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Đã sao chép nội dung từ tệp PDF"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-zh-rCN/strings.xml b/pdf/pdf-viewer/src/main/res/values-zh-rCN/strings.xml
index 683dddd..9924fa2 100644
--- a/pdf/pdf-viewer/src/main/res/values-zh-rCN/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-zh-rCN/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"PDF 文档的页面已损坏"</string>
<string name="needs_more_data" msgid="3520133467908240802">"数据不足,无法处理 PDF 文档"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"无法打开 PDF 文件"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"从 PDF 复制的内容"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-zh-rHK/strings.xml b/pdf/pdf-viewer/src/main/res/values-zh-rHK/strings.xml
index 6c6c835..016a712 100644
--- a/pdf/pdf-viewer/src/main/res/values-zh-rHK/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-zh-rHK/strings.xml
@@ -25,7 +25,7 @@
<string name="desc_password" msgid="6636473611443746739">"密碼欄位"</string>
<string name="desc_password_incorrect" msgid="4718823067483963845">"密碼不正確"</string>
<string name="desc_password_incorrect_message" msgid="4676206571020946870">"密碼錯誤,請再試一次。"</string>
- <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"第 <xliff:g id="PAGE">%1$d</xliff:g> 頁,共 <xliff:g id="TOTAL">%2$d</xliff:g> 頁"</string>
<string name="desc_zoom" msgid="7318480946145947242">"縮放 <xliff:g id="FIRST">%1$d</xliff:g>%%"</string>
<string name="desc_goto_link" msgid="2461368384824849714">"前往第 <xliff:g id="PAGE_NUMBER">%1$d</xliff:g> 頁"</string>
@@ -40,7 +40,7 @@
<string name="desc_phone_link" msgid="4986414958429253135">"電話:<xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"選取開頭"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"選取結尾"</string>
- <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g> - <xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
+ <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g> - <xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_page_range" msgid="5286496438609641577">"第 <xliff:g id="FIRST">%1$d</xliff:g> 至 <xliff:g id="LAST">%2$d</xliff:g> 頁 (共 <xliff:g id="TOTAL">%3$d</xliff:g> 頁)"</string>
<string name="desc_image_alt_text" msgid="7700601988820586333">"圖片:<xliff:g id="ALT_TEXT">%1$s</xliff:g>"</string>
<string name="hint_find" msgid="5385388836603550565">"在檔案中搜尋"</string>
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"PDF 文件頁面已損毀"</string>
<string name="needs_more_data" msgid="3520133467908240802">"沒有足夠資料處理 PDF 文件"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"無法開啟 PDF 檔案"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"從 PDF 複製的內容"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-zh-rTW/strings.xml b/pdf/pdf-viewer/src/main/res/values-zh-rTW/strings.xml
index bb54eca..f4337fd 100644
--- a/pdf/pdf-viewer/src/main/res/values-zh-rTW/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-zh-rTW/strings.xml
@@ -25,7 +25,7 @@
<string name="desc_password" msgid="6636473611443746739">"密碼欄位"</string>
<string name="desc_password_incorrect" msgid="4718823067483963845">"密碼不正確"</string>
<string name="desc_password_incorrect_message" msgid="4676206571020946870">"密碼錯誤,請再試一次。"</string>
- <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
+ <string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"第 <xliff:g id="PAGE">%1$d</xliff:g> 頁,共 <xliff:g id="TOTAL">%2$d</xliff:g> 頁"</string>
<string name="desc_zoom" msgid="7318480946145947242">"縮放 <xliff:g id="FIRST">%1$d</xliff:g>%%"</string>
<string name="desc_goto_link" msgid="2461368384824849714">"前往第 <xliff:g id="PAGE_NUMBER">%1$d</xliff:g> 頁"</string>
@@ -40,7 +40,7 @@
<string name="desc_phone_link" msgid="4986414958429253135">"電話號碼:<xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"選取開頭"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"選取結尾"</string>
- <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g> - <xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
+ <string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g> - <xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_page_range" msgid="5286496438609641577">"第 <xliff:g id="FIRST">%1$d</xliff:g> 到 <xliff:g id="LAST">%2$d</xliff:g> 頁,共 <xliff:g id="TOTAL">%3$d</xliff:g> 頁"</string>
<string name="desc_image_alt_text" msgid="7700601988820586333">"圖片:<xliff:g id="ALT_TEXT">%1$s</xliff:g>"</string>
<string name="hint_find" msgid="5385388836603550565">"在檔案中搜尋"</string>
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"PDF 文件的頁面損毀"</string>
<string name="needs_more_data" msgid="3520133467908240802">"資料不足,無法處理 PDF 文件"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"無法開啟 PDF 檔案"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"已從 PDF 複製內容"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-zu/strings.xml b/pdf/pdf-viewer/src/main/res/values-zu/strings.xml
index d5e0c57..59804ef 100644
--- a/pdf/pdf-viewer/src/main/res/values-zu/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-zu/strings.xml
@@ -60,6 +60,5 @@
<string name="page_broken" msgid="2968770793669433462">"Ikhasi eliphuliwe ledokhumenti ye-PDF"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Idatha enganele yokucubungula idokhumenti ye-PDF"</string>
<string name="error_cannot_open_pdf" msgid="2361919778558145071">"Ayikwazi ukuvula ifayela le-PDF"</string>
- <!-- no translation found for clipboard_label (8943155331324944981) -->
- <skip />
+ <string name="clipboard_label" msgid="8943155331324944981">"Okuqukethwe kukopishwe ku-PDF"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/test/kotlin/androidx/pdf/view/BitmapFetcherTest.kt b/pdf/pdf-viewer/src/test/kotlin/androidx/pdf/view/BitmapFetcherTest.kt
index fc60503..048f51b 100644
--- a/pdf/pdf-viewer/src/test/kotlin/androidx/pdf/view/BitmapFetcherTest.kt
+++ b/pdf/pdf-viewer/src/test/kotlin/androidx/pdf/view/BitmapFetcherTest.kt
@@ -16,7 +16,9 @@
package androidx.pdf.view
+import android.graphics.Bitmap
import android.graphics.Point
+import android.graphics.Rect
import androidx.pdf.PdfDocument
import com.google.common.truth.Truth.assertThat
import kotlin.math.roundToInt
@@ -48,6 +50,7 @@
private val maxBitmapSizePx = Point(2048, 2048)
private val pageSize = Point(512, 512)
+ private val fullPageViewArea = Rect(0, 0, pageSize.x, pageSize.y)
private lateinit var bitmapFetcher: BitmapFetcher
private lateinit var tileSizePx: Point
@@ -72,23 +75,23 @@
@Test
fun setInactive_cancelsWorkAndFreesBitmaps() {
- bitmapFetcher.onScaleChanged(1.5f)
- assertThat(bitmapFetcher.renderingJob?.isActive).isTrue()
+ bitmapFetcher.updateViewProperties(1.5f, fullPageViewArea)
+ assertThat(bitmapFetcher.fetchingWorkHandle?.isActive).isTrue()
bitmapFetcher.isActive = false
- assertThat(bitmapFetcher.renderingJob).isNull()
+ assertThat(bitmapFetcher.fetchingWorkHandle).isNull()
assertThat(bitmapFetcher.pageContents).isNull()
}
@Test
- fun setScale_rendersFullPageBitmap() {
- bitmapFetcher.onScaleChanged(1.5f)
+ fun lowScale_fullPageViewArea_fetchesFullPageBitmap() {
+ bitmapFetcher.updateViewProperties(1.5f, fullPageViewArea)
testDispatcher.scheduler.runCurrent()
val pageBitmaps = bitmapFetcher.pageContents
assertThat(pageBitmaps).isInstanceOf(FullPageBitmap::class.java)
- assertThat(pageBitmaps?.renderedScale).isEqualTo(1.5f)
+ assertThat(pageBitmaps?.bitmapScale).isEqualTo(1.5f)
pageBitmaps as FullPageBitmap // Make smartcast work nicely below
assertThat(pageBitmaps.bitmap.width).isEqualTo((pageSize.x * 1.5f).roundToInt())
assertThat(pageBitmaps.bitmap.height).isEqualTo((pageSize.y * 1.5f).roundToInt())
@@ -96,14 +99,33 @@
}
@Test
- fun setScale_rendersTileBoard() {
- bitmapFetcher.onScaleChanged(5.0f)
+ fun lowScale_partialPageViewArea_fetchesFullPageBitmap() {
+ // 1.5 scale, viewing the lower right half of the page
+ bitmapFetcher.updateViewProperties(
+ 1.5f,
+ viewArea = Rect(pageSize.x / 2, pageSize.y / 2, pageSize.x, pageSize.y)
+ )
+
+ testDispatcher.scheduler.runCurrent()
+
+ val pageBitmaps = bitmapFetcher.pageContents
+ assertThat(pageBitmaps).isInstanceOf(FullPageBitmap::class.java)
+ assertThat(pageBitmaps?.bitmapScale).isEqualTo(1.5f)
+ pageBitmaps as FullPageBitmap // Make smartcast work nicely below
+ assertThat(pageBitmaps.bitmap.width).isEqualTo((pageSize.x * 1.5f).roundToInt())
+ assertThat(pageBitmaps.bitmap.height).isEqualTo((pageSize.y * 1.5f).roundToInt())
+ assertThat(invalidationCounter).isEqualTo(1)
+ }
+
+ @Test
+ fun highScale_fullPageViewArea_fetchesTileBoard() {
+ bitmapFetcher.updateViewProperties(5.0f, fullPageViewArea)
testDispatcher.scheduler.runCurrent()
val pageBitmaps = bitmapFetcher.pageContents
assertThat(pageBitmaps).isInstanceOf(TileBoard::class.java)
- assertThat(pageBitmaps?.renderedScale).isEqualTo(5.0f)
+ assertThat(pageBitmaps?.bitmapScale).isEqualTo(5.0f)
pageBitmaps as TileBoard // Make smartcast work nicely below
// Check the properties of an arbitrary full-size tile
@@ -123,21 +145,54 @@
assertThat(row3Col3.offsetPx).isEqualTo(Point(tileSizePx.x * 3, tileSizePx.y * 3))
assertThat(row3Col3.exactSizePx).isEqualTo(row3Col3Size)
+ for (tile in pageBitmaps.tiles) {
+ assertThat(tile.bitmap).isNotNull()
+ }
+
// 1 invalidation for the low-res background, 1 for each tile * 16 tiles
assertThat(invalidationCounter).isEqualTo(17)
}
@Test
- fun setScale_toRenderedValue_noNewWork() {
+ fun highScale_partialPageViewArea_fetchesPartialTileBoard() {
+ // 1.5 scale, viewing the lower right half of the page
+ bitmapFetcher.updateViewProperties(
+ 5.0f,
+ viewArea = Rect(pageSize.x / 2, pageSize.y / 2, pageSize.x, pageSize.y)
+ )
+
+ testDispatcher.scheduler.runCurrent()
+
+ val pageBitmaps = bitmapFetcher.pageContents
+ assertThat(pageBitmaps).isInstanceOf(TileBoard::class.java)
+ assertThat(pageBitmaps?.bitmapScale).isEqualTo(5.0f)
+ pageBitmaps as TileBoard // Make smartcast work nicely below
+ // This is all tiles in row >= 0 && col >= 0 (row 1 and col 1 are partially visible)
+ val expectedVisibleIndices = setOf(5, 6, 7, 9, 10, 11, 13, 14, 15)
+
+ for (tile in pageBitmaps.tiles) {
+ if (tile.index in expectedVisibleIndices) {
+ assertThat(tile.bitmap).isNotNull()
+ } else {
+ assertThat(tile.bitmap).isNull()
+ }
+ }
+
+ // 1 invalidation for the low-res background, 1 for each visible tile * 9 visible tiles
+ assertThat(invalidationCounter).isEqualTo(10)
+ }
+
+ @Test
+ fun changeScale_toFetchedValue_noNewWork() {
bitmapFetcher.isActive = true
- bitmapFetcher.onScaleChanged(1.5f)
+ bitmapFetcher.updateViewProperties(1.5f, fullPageViewArea)
testDispatcher.scheduler.runCurrent()
val firstBitmaps = bitmapFetcher.pageContents
- bitmapFetcher.onScaleChanged(1.5f)
+ bitmapFetcher.updateViewProperties(1.5f, fullPageViewArea)
// We shouldn't have started a new Job the second time onScaleChanged to the same value
- assertThat(bitmapFetcher.renderingJob).isNull()
+ assertThat(bitmapFetcher.fetchingWorkHandle?.isActive).isFalse()
// And we should still have the same bitmaps
assertThat(bitmapFetcher.pageContents).isEqualTo(firstBitmaps)
// 1 total invalidation
@@ -145,20 +200,20 @@
}
@Test
- fun setScale_toRenderingValue_noNewWork() {
- bitmapFetcher.onScaleChanged(1.5f)
- val firstJob = bitmapFetcher.renderingJob
- bitmapFetcher.onScaleChanged(1.5f)
+ fun changeScale_toFetchingValue_noNewWork() {
+ bitmapFetcher.updateViewProperties(1.5f, fullPageViewArea)
+ val firstJob = bitmapFetcher.fetchingWorkHandle
+ bitmapFetcher.updateViewProperties(1.5f, fullPageViewArea)
// This should be the same Job we started the first time onScaleChanged
- assertThat(bitmapFetcher.renderingJob).isEqualTo(firstJob)
+ assertThat(bitmapFetcher.fetchingWorkHandle).isEqualTo(firstJob)
// 0 invalidations because we're still rendering
assertThat(invalidationCounter).isEqualTo(0)
}
@Test
- fun setScale_afterInactive_rendersNewBitmaps() {
- bitmapFetcher.onScaleChanged(1.5f)
+ fun changeScale_afterInactive_fetchesNewBitmaps() {
+ bitmapFetcher.updateViewProperties(1.5f, fullPageViewArea)
testDispatcher.scheduler.runCurrent()
assertThat(bitmapFetcher.pageContents).isNotNull()
assertThat(invalidationCounter).isEqualTo(1)
@@ -167,47 +222,106 @@
assertThat(bitmapFetcher.pageContents).isNull()
bitmapFetcher.isActive = true
- bitmapFetcher.onScaleChanged(1.5f)
+ bitmapFetcher.updateViewProperties(1.5f, fullPageViewArea)
testDispatcher.scheduler.runCurrent()
assertThat(bitmapFetcher.pageContents).isNotNull()
assertThat(invalidationCounter).isEqualTo(2)
}
@Test
- fun setScale_fromFullPage_toTiled() {
- bitmapFetcher.onScaleChanged(1.5f)
+ fun changeScale_lowToHigh_fullPageToTiling() {
+ bitmapFetcher.updateViewProperties(1.5f, fullPageViewArea)
testDispatcher.scheduler.runCurrent()
val fullPageBitmap = bitmapFetcher.pageContents
assertThat(fullPageBitmap).isInstanceOf(FullPageBitmap::class.java)
- assertThat(fullPageBitmap?.renderedScale).isEqualTo(1.5f)
+ assertThat(fullPageBitmap?.bitmapScale).isEqualTo(1.5f)
assertThat(invalidationCounter).isEqualTo(1)
- bitmapFetcher.onScaleChanged(5.0f)
+ bitmapFetcher.updateViewProperties(5.0f, fullPageViewArea)
testDispatcher.scheduler.runCurrent()
val tileBoard = bitmapFetcher.pageContents
assertThat(tileBoard).isInstanceOf(TileBoard::class.java)
- assertThat(tileBoard?.renderedScale).isEqualTo(5.0f)
+ assertThat(tileBoard?.bitmapScale).isEqualTo(5.0f)
// 1 invalidation for the previous full page bitmap + 1 for the low res background
// + (1 for each tile * 16 tiles)
assertThat(invalidationCounter).isEqualTo(18)
}
@Test
- fun setScale_fromTiled_toFullPage() {
- bitmapFetcher.onScaleChanged(5.0f)
+ fun changeScale_highToLow_tilingToFullPage() {
+ bitmapFetcher.updateViewProperties(5.0f, fullPageViewArea)
testDispatcher.scheduler.runCurrent()
val tileBoard = bitmapFetcher.pageContents
assertThat(tileBoard).isInstanceOf(TileBoard::class.java)
- assertThat(tileBoard?.renderedScale).isEqualTo(5.0f)
+ assertThat(tileBoard?.bitmapScale).isEqualTo(5.0f)
// 1 invalidation for the low res background + (1 for each tile * 16 tiles)
assertThat(invalidationCounter).isEqualTo(17)
- bitmapFetcher.onScaleChanged(1.5f)
+ bitmapFetcher.updateViewProperties(1.5f, fullPageViewArea)
testDispatcher.scheduler.runCurrent()
val fullPageBitmap = bitmapFetcher.pageContents
assertThat(fullPageBitmap).isInstanceOf(FullPageBitmap::class.java)
- assertThat(fullPageBitmap?.renderedScale).isEqualTo(1.5f)
+ assertThat(fullPageBitmap?.bitmapScale).isEqualTo(1.5f)
// 1 additional invalidation for the new full page bitmap
assertThat(invalidationCounter).isEqualTo(18)
}
+
+ @Test
+ fun changeViewArea_overlapWithPrevious() {
+ // 5.0 scale, viewing the lower right half of the page
+ bitmapFetcher.updateViewProperties(
+ 5.0f,
+ Rect(pageSize.x / 2, pageSize.y / 2, pageSize.x, pageSize.y)
+ )
+ testDispatcher.scheduler.runCurrent()
+ val originalTileBoard = bitmapFetcher.pageContents
+ assertThat(originalTileBoard).isInstanceOf(TileBoard::class.java)
+ // This is all tiles in row > 0 && col > 0 (row 1 and col 1 are partially visible)
+ val originalVisibleIndices = setOf(5, 6, 7, 9, 10, 11, 13, 14, 15)
+ val originalBitmaps = mutableMapOf<Int, Bitmap>()
+ for (tile in (originalTileBoard as TileBoard).tiles) {
+ if (tile.index in originalVisibleIndices) {
+ assertThat(tile.bitmap).isNotNull()
+ originalBitmaps[tile.index] = requireNotNull(tile.bitmap)
+ } else {
+ assertThat(tile.bitmap).isNull()
+ }
+ }
+ // 1 invalidation for the low-res background, 1 for each visible tile
+ val originalInvalidations = invalidationCounter
+ assertThat(originalInvalidations).isEqualTo(originalVisibleIndices.size + 1)
+
+ // 5.0 scale, viewing the middle of the page offset by 1/4 of the page's dimensions
+ bitmapFetcher.updateViewProperties(
+ 5.0f,
+ Rect(pageSize.x / 4, pageSize.y / 4, pageSize.x * 3 / 4, pageSize.y * 3 / 4)
+ )
+ testDispatcher.scheduler.runCurrent()
+ val newTileBoard = bitmapFetcher.pageContents
+ // We should re-use the previous tile board
+ assertThat(newTileBoard).isEqualTo(originalTileBoard)
+ // This is all tiles in row < 3 and col < 3 (row 0 and 2, col 0 and 2 are partially visible)
+ val newVisibleIndices = setOf(0, 1, 2, 4, 5, 6, 8, 9, 10)
+ // This is all tiles that are visible in both view areas (original and new)
+ val expectedRetainedIndices = originalVisibleIndices.intersect(newVisibleIndices)
+ for (tile in (newTileBoard as TileBoard).tiles) {
+ if (tile.index in expectedRetainedIndices) {
+ // We should have re-used the previous tile Bitmap
+ assertThat(tile.bitmap).isNotNull()
+ assertThat(tile.bitmap).isEqualTo(originalBitmaps[tile.index])
+ } else if (tile.index in newVisibleIndices) {
+ // We should have fetched a new tile Bitmap
+ assertThat(tile.bitmap).isNotNull()
+ } else {
+ // We should have cleaned up the Bitmap
+ assertThat(tile.bitmap).isNull()
+ }
+ }
+
+ // Invalidations before the view area changed + 1 for each *newly* visible tile
+ assertThat(invalidationCounter)
+ .isEqualTo(
+ originalInvalidations + (newVisibleIndices.size - expectedRetainedIndices.size)
+ )
+ }
}
diff --git a/pdf/pdf-viewer/src/test/kotlin/androidx/pdf/view/PageTest.kt b/pdf/pdf-viewer/src/test/kotlin/androidx/pdf/view/PageTest.kt
index f360178..ad18636 100644
--- a/pdf/pdf-viewer/src/test/kotlin/androidx/pdf/view/PageTest.kt
+++ b/pdf/pdf-viewer/src/test/kotlin/androidx/pdf/view/PageTest.kt
@@ -73,7 +73,7 @@
private fun createPage(isTouchExplorationEnabled: Boolean): Page {
return Page(
0,
- pageSizePx = PAGE_SIZE,
+ pageSize = PAGE_SIZE,
pdfDocument,
testScope,
MAX_BITMAP_SIZE,
@@ -97,7 +97,7 @@
fun draw_withoutBitmap() {
// Notably we don't call testDispatcher.scheduler.runCurrent(), so we start, but do not
// finish, fetching a Bitmap
- page.updateState(zoom = 1.5F)
+ page.setVisible(zoom = 1.5F, FULL_PAGE_RECT)
val locationInView = Rect(-60, 125, -60 + PAGE_SIZE.x, 125 + PAGE_SIZE.y)
page.draw(canvasSpy, locationInView, listOf())
@@ -107,7 +107,7 @@
@Test
fun draw_withBitmap() {
- page.updateState(zoom = 1.5F)
+ page.setVisible(zoom = 1.5F, FULL_PAGE_RECT)
testDispatcher.scheduler.runCurrent()
val locationInView = Rect(50, -100, 50 + PAGE_SIZE.x, -100 + PAGE_SIZE.y)
@@ -127,7 +127,7 @@
@Test
fun draw_withHighlight() {
- page.updateState(zoom = 1.5F)
+ page.setVisible(zoom = 1.5F, FULL_PAGE_RECT)
testDispatcher.scheduler.runCurrent()
val leftEdgeInView = 650
val topEdgeInView = -320
@@ -158,7 +158,7 @@
@Test
fun updateState_withTouchExplorationEnabled_fetchesPageText() {
- page.updateState(zoom = 1.0f)
+ page.setVisible(zoom = 1.0f, FULL_PAGE_RECT)
testDispatcher.scheduler.runCurrent()
assertThat(page.pageText).isEqualTo("SampleText")
assertThat(pageTextReadyCounter).isEqualTo(1)
@@ -167,7 +167,7 @@
@Test
fun setVisible_withTouchExplorationDisabled_doesNotFetchPageText() {
page = createPage(isTouchExplorationEnabled = false)
- page.updateState(zoom = 1.0f)
+ page.setVisible(zoom = 1.0f, FULL_PAGE_RECT)
testDispatcher.scheduler.runCurrent()
assertThat(page.pageText).isEqualTo(null)
@@ -176,20 +176,20 @@
@Test
fun updateState_doesNotFetchPageTextIfAlreadyFetched() {
- page.updateState(zoom = 1.0f)
+ page.setVisible(zoom = 1.0f, FULL_PAGE_RECT)
testDispatcher.scheduler.runCurrent()
assertThat(page.pageText).isEqualTo("SampleText")
assertThat(pageTextReadyCounter).isEqualTo(1)
- page.updateState(zoom = 1.0f)
+ page.setVisible(zoom = 1.0f, FULL_PAGE_RECT)
testDispatcher.scheduler.runCurrent()
assertThat(page.pageText).isEqualTo("SampleText")
assertThat(pageTextReadyCounter).isEqualTo(1)
}
@Test
- fun setInvisible_cancelsPageTextFetch() {
- page.updateState(zoom = 1.0f)
+ fun setPageInvisible_cancelsTextFetch() {
+ page.setVisible(zoom = 1.0f, FULL_PAGE_RECT)
page.setInvisible()
testDispatcher.scheduler.runCurrent()
assertThat(page.pageText).isNull()
@@ -198,4 +198,5 @@
}
val PAGE_SIZE = Point(100, 150)
+val FULL_PAGE_RECT = Rect(0, 0, PAGE_SIZE.x, PAGE_SIZE.y)
val MAX_BITMAP_SIZE = Point(500, 500)
diff --git a/performance/performance-annotation/README.md b/performance/performance-annotation/README.md
index 2ab0c3d..b37c607 100644
--- a/performance/performance-annotation/README.md
+++ b/performance/performance-annotation/README.md
@@ -1 +1 @@
-This library is a **compile-time** only dependency.
+The annotation defined in this library only affects Android.
diff --git a/performance/performance-annotation/bcv/native/current.txt b/performance/performance-annotation/bcv/native/current.txt
index 8e4fa01..f0402c6 100644
--- a/performance/performance-annotation/bcv/native/current.txt
+++ b/performance/performance-annotation/bcv/native/current.txt
@@ -6,6 +6,6 @@
// - Show declarations: true
// Library unique name: <androidx.performance:performance-annotation>
-open annotation class dalvik.annotation.optimization/NeverInline : kotlin/Annotation { // dalvik.annotation.optimization/NeverInline|null[1]
- constructor <init>() // dalvik.annotation.optimization/NeverInline.<init>|<init>(){}[1]
+open annotation class androidx.performance.annotation/AndroidNeverInline : kotlin/Annotation { // androidx.performance.annotation/AndroidNeverInline|null[1]
+ constructor <init>() // androidx.performance.annotation/AndroidNeverInline.<init>|<init>(){}[1]
}
diff --git a/performance/performance-annotation/shrinker-rules.pro b/performance/performance-annotation/shrinker-rules.pro
new file mode 100644
index 0000000..28c2b22
--- /dev/null
+++ b/performance/performance-annotation/shrinker-rules.pro
@@ -0,0 +1,17 @@
+# Copyright (C) 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.
+
+-keepclassmembers class * {
+ @dalvik.annotation.optimization.NeverInline *;
+}
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/test/TestEnum.java b/performance/performance-annotation/src/androidMain/kotlin/androidx/performance/annotation/AndroidNeverInline.android.kt
similarity index 73%
copy from navigation/navigation-runtime/src/androidTest/java/androidx/navigation/test/TestEnum.java
copy to performance/performance-annotation/src/androidMain/kotlin/androidx/performance/annotation/AndroidNeverInline.android.kt
index f3a5276..3395134 100644
--- a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/test/TestEnum.java
+++ b/performance/performance-annotation/src/androidMain/kotlin/androidx/performance/annotation/AndroidNeverInline.android.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 The Android Open Source Project
+ * 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.
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-package androidx.navigation.test;
+package androidx.performance.annotation
-public enum TestEnum {
- VALUE_ONE, VALUE_TWO
-}
+import dalvik.annotation.optimization.NeverInline
+
+public actual typealias AndroidNeverInline = NeverInline
diff --git a/performance/performance-annotation/src/androidMain/kotlin/dalvik/annotation/optimization/NeverInline.android.kt b/performance/performance-annotation/src/androidMain/kotlin/dalvik/annotation/optimization/NeverInline.android.kt
index 50e22dc..22a6e98a 100644
--- a/performance/performance-annotation/src/androidMain/kotlin/dalvik/annotation/optimization/NeverInline.android.kt
+++ b/performance/performance-annotation/src/androidMain/kotlin/dalvik/annotation/optimization/NeverInline.android.kt
@@ -14,8 +14,10 @@
* limitations under the License.
*/
+@file:Suppress("RedundantVisibilityModifier")
+
package dalvik.annotation.optimization
@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.CONSTRUCTOR, AnnotationTarget.FUNCTION)
-public actual annotation class NeverInline()
+public annotation class NeverInline
diff --git a/performance/performance-annotation/src/commonMain/kotlin/androidx/performance/annotation/AndroidNeverInline.kt b/performance/performance-annotation/src/commonMain/kotlin/androidx/performance/annotation/AndroidNeverInline.kt
new file mode 100644
index 0000000..7ae676b
--- /dev/null
+++ b/performance/performance-annotation/src/commonMain/kotlin/androidx/performance/annotation/AndroidNeverInline.kt
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+@file:OptIn(ExperimentalMultiplatform::class)
+
+package androidx.performance.annotation
+
+/**
+ * Indicates that an API should never be inlined by ART on Android.
+ *
+ * [AndroidNeverInline] can be used to annotate methods that should not be inlined into other
+ * methods. Methods that are not called frequently, are never speed-critical, or are only used for
+ * debugging do not necessarily need to run quickly. Applying this annotation to prevent these
+ * methods from being inlined will return some size improvements in .odex files.
+ */
+@Retention(AnnotationRetention.BINARY)
+@Target(AnnotationTarget.CONSTRUCTOR, AnnotationTarget.FUNCTION)
+@OptionalExpectation
+public expect annotation class AndroidNeverInline()
diff --git a/performance/performance-annotation/src/commonMain/kotlin/dalvik/annotation/optimization/NeverInline.kt b/performance/performance-annotation/src/commonMain/kotlin/dalvik/annotation/optimization/NeverInline.kt
deleted file mode 100644
index 89cbfe3..0000000
--- a/performance/performance-annotation/src/commonMain/kotlin/dalvik/annotation/optimization/NeverInline.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright 2024 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.
- */
-
-@file:OptIn(ExperimentalMultiplatform::class)
-
-package dalvik.annotation.optimization
-
-/**
- * Indicates that an API should never be inlined by ART on Android.
- *
- * [NeverInline] can be used to annotate methods that should not be inlined into other methods.
- * Methods that are not called frequently, are never speed-critical, or are only used for debugging
- * do not necessarily need to run quickly. Applying this annotation to prevent these methods from
- * being inlined will return some size improvements in .odex files.
- *
- * Prefer using the `AndroidNeverInline` typealias instead of this annotation directly to emphasize
- * it only affects Android targets.
- */
-@Retention(AnnotationRetention.BINARY)
-@Target(AnnotationTarget.CONSTRUCTOR, AnnotationTarget.FUNCTION)
-@OptionalExpectation
-public expect annotation class NeverInline()
diff --git a/privacysandbox/tools/integration-tests/testsdk/src/main/java/androidx/privacysandbox/tools/integration/testsdk/MySdk.kt b/privacysandbox/tools/integration-tests/testsdk/src/main/java/androidx/privacysandbox/tools/integration/testsdk/MySdk.kt
index 454727b..b22e333 100644
--- a/privacysandbox/tools/integration-tests/testsdk/src/main/java/androidx/privacysandbox/tools/integration/testsdk/MySdk.kt
+++ b/privacysandbox/tools/integration-tests/testsdk/src/main/java/androidx/privacysandbox/tools/integration/testsdk/MySdk.kt
@@ -19,12 +19,12 @@
import android.content.Context
import android.content.res.Configuration
import android.os.Bundle
-import android.os.IBinder
import android.view.View
import android.widget.TextView
import androidx.privacysandbox.tools.PrivacySandboxInterface
import androidx.privacysandbox.tools.PrivacySandboxService
import androidx.privacysandbox.ui.core.SandboxedUiAdapter
+import androidx.privacysandbox.ui.core.SessionConstants
import java.util.concurrent.Executor
@PrivacySandboxService
@@ -49,7 +49,7 @@
class TextViewAdImpl : TextViewAd {
override fun openSession(
context: Context,
- windowInputToken: IBinder,
+ sessionConstants: SessionConstants,
initialWidth: Int,
initialHeight: Int,
isZOrderOnTop: Boolean,
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/MyUiInterfaceClientProxy.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/MyUiInterfaceClientProxy.kt
index 4d8eeb9..32d10ad 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/MyUiInterfaceClientProxy.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/callbacks/output/com/sdkwithcallbacks/MyUiInterfaceClientProxy.kt
@@ -2,10 +2,10 @@
import android.content.Context
import android.os.Bundle
-import android.os.IBinder
import androidx.privacysandbox.ui.client.SandboxedUiAdapterFactory
import androidx.privacysandbox.ui.core.SandboxedUiAdapter
import androidx.privacysandbox.ui.core.SandboxedUiAdapter.SessionClient
+import androidx.privacysandbox.ui.core.SessionConstants
import java.util.concurrent.Executor
public class MyUiInterfaceClientProxy(
@@ -21,14 +21,14 @@
public override fun openSession(
context: Context,
- windowInputToken: IBinder,
+ sessionConstants: SessionConstants,
initialWidth: Int,
initialHeight: Int,
isZOrderOnTop: Boolean,
clientExecutor: Executor,
client: SandboxedUiAdapter.SessionClient,
) {
- sandboxedUiAdapter.openSession(context, windowInputToken, initialWidth, initialHeight,
+ sandboxedUiAdapter.openSession(context, sessionConstants, initialWidth, initialHeight,
isZOrderOnTop, clientExecutor, client)
}
}
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MySecondInterfaceClientProxy.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MySecondInterfaceClientProxy.kt
index d7f90e3..8583243 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MySecondInterfaceClientProxy.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/interfaces/output/com/sdk/MySecondInterfaceClientProxy.kt
@@ -2,10 +2,10 @@
import android.content.Context
import android.os.Bundle
-import android.os.IBinder
import androidx.privacysandbox.ui.client.SandboxedUiAdapterFactory
import androidx.privacysandbox.ui.core.SandboxedUiAdapter
import androidx.privacysandbox.ui.core.SandboxedUiAdapter.SessionClient
+import androidx.privacysandbox.ui.core.SessionConstants
import java.util.concurrent.Executor
public class MySecondInterfaceClientProxy(
@@ -21,14 +21,14 @@
public override fun openSession(
context: Context,
- windowInputToken: IBinder,
+ sessionConstants: SessionConstants,
initialWidth: Int,
initialHeight: Int,
isZOrderOnTop: Boolean,
clientExecutor: Executor,
client: SandboxedUiAdapter.SessionClient,
) {
- sandboxedUiAdapter.openSession(context, windowInputToken, initialWidth, initialHeight,
+ sandboxedUiAdapter.openSession(context, sessionConstants, initialWidth, initialHeight,
isZOrderOnTop, clientExecutor, client)
}
}
diff --git a/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/MyUiInterfaceClientProxy.kt b/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/MyUiInterfaceClientProxy.kt
index 86709a8..7bb8599 100644
--- a/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/MyUiInterfaceClientProxy.kt
+++ b/privacysandbox/tools/tools-apigenerator/src/test/test-data/values/output/com/sdkwithvalues/MyUiInterfaceClientProxy.kt
@@ -2,10 +2,10 @@
import android.content.Context
import android.os.Bundle
-import android.os.IBinder
import androidx.privacysandbox.ui.client.SandboxedUiAdapterFactory
import androidx.privacysandbox.ui.core.SandboxedUiAdapter
import androidx.privacysandbox.ui.core.SandboxedUiAdapter.SessionClient
+import androidx.privacysandbox.ui.core.SessionConstants
import java.util.concurrent.Executor
public class MyUiInterfaceClientProxy(
@@ -21,14 +21,14 @@
public override fun openSession(
context: Context,
- windowInputToken: IBinder,
+ sessionConstants: SessionConstants,
initialWidth: Int,
initialHeight: Int,
isZOrderOnTop: Boolean,
clientExecutor: Executor,
client: SandboxedUiAdapter.SessionClient,
) {
- sandboxedUiAdapter.openSession(context, windowInputToken, initialWidth, initialHeight,
+ sandboxedUiAdapter.openSession(context, sessionConstants, initialWidth, initialHeight,
isZOrderOnTop, clientExecutor, client)
}
}
diff --git a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/ClientProxyTypeGenerator.kt b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/ClientProxyTypeGenerator.kt
index 54d5210..a8f1ee1 100644
--- a/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/ClientProxyTypeGenerator.kt
+++ b/privacysandbox/tools/tools-core/src/main/java/androidx/privacysandbox/tools/core/generator/ClientProxyTypeGenerator.kt
@@ -150,7 +150,10 @@
addParameters(
listOf(
ParameterSpec(contextPropertyName, contextClass),
- ParameterSpec("windowInputToken", ClassName("android.os", "IBinder")),
+ ParameterSpec(
+ "sessionConstants",
+ ClassName("androidx.privacysandbox.ui.core", "SessionConstants")
+ ),
ParameterSpec("initialWidth", Types.int.poetClassName()),
ParameterSpec("initialHeight", Types.int.poetClassName()),
ParameterSpec("isZOrderOnTop", Types.boolean.poetClassName()),
@@ -165,7 +168,7 @@
)
)
addStatement(
- "$sandboxedUiAdapterPropertyName.openSession(%N, windowInputToken, initialWidth, " +
+ "$sandboxedUiAdapterPropertyName.openSession(%N, sessionConstants, initialWidth, " +
"initialHeight, isZOrderOnTop, clientExecutor, client)",
contextPropertyName,
)
diff --git a/privacysandbox/ui/integration-tests/sdkproviderutils/src/main/java/androidx/privacysandbox/ui/integration/sdkproviderutils/TestAdapters.kt b/privacysandbox/ui/integration-tests/sdkproviderutils/src/main/java/androidx/privacysandbox/ui/integration/sdkproviderutils/TestAdapters.kt
index bf67e4d..ca604a5 100644
--- a/privacysandbox/ui/integration-tests/sdkproviderutils/src/main/java/androidx/privacysandbox/ui/integration/sdkproviderutils/TestAdapters.kt
+++ b/privacysandbox/ui/integration-tests/sdkproviderutils/src/main/java/androidx/privacysandbox/ui/integration/sdkproviderutils/TestAdapters.kt
@@ -26,7 +26,6 @@
import android.net.Uri
import android.os.Bundle
import android.os.Handler
-import android.os.IBinder
import android.os.Looper
import android.provider.Settings
import android.util.Log
@@ -41,6 +40,7 @@
import androidx.privacysandbox.ui.client.SandboxedUiAdapterFactory
import androidx.privacysandbox.ui.client.view.SandboxedSdkView
import androidx.privacysandbox.ui.core.SandboxedUiAdapter
+import androidx.privacysandbox.ui.core.SessionConstants
import androidx.privacysandbox.ui.provider.AbstractSandboxedUiAdapter
import androidx.webkit.WebViewAssetLoader
import androidx.webkit.WebViewClientCompat
@@ -62,7 +62,7 @@
override fun openSession(
context: Context,
- windowInputToken: IBinder,
+ sessionConstants: SessionConstants,
initialWidth: Int,
initialHeight: Int,
isZOrderOnTop: Boolean,
diff --git a/privacysandbox/ui/integration-tests/testapp/build.gradle b/privacysandbox/ui/integration-tests/testapp/build.gradle
index a9a297b..e88938e 100644
--- a/privacysandbox/ui/integration-tests/testapp/build.gradle
+++ b/privacysandbox/ui/integration-tests/testapp/build.gradle
@@ -37,6 +37,16 @@
}
}
+ experimentalProperties["android.privacySandboxSdk.apiGenerator"] =
+ project.dependencies.create(project(":privacysandbox:tools:tools-apigenerator"))
+ experimentalProperties["android.privacySandboxSdk.apiGenerator.generatedRuntimeDependencies"] =
+ [libs.kotlinStdlib.get(),
+ libs.kotlinCoroutinesAndroid.get(),
+ libs.kotlinCoroutinesCore.get(),
+ project.dependencies.create(project(":privacysandbox:ui:ui-core")),
+ project.dependencies.create(project(":privacysandbox:ui:ui-client"))
+ ]
+
privacySandbox {
enable = true
}
diff --git a/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/LazyListFragment.kt b/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/LazyListFragment.kt
new file mode 100644
index 0000000..d220ae7
--- /dev/null
+++ b/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/LazyListFragment.kt
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2024 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.privacysandbox.ui.integration.testapp
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.items
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.platform.ViewCompositionStrategy
+import androidx.compose.ui.unit.dp
+import androidx.privacysandbox.ui.client.SandboxedUiAdapterFactory
+import androidx.privacysandbox.ui.client.view.SandboxedSdkUi
+import androidx.privacysandbox.ui.core.SandboxedUiAdapter
+import kotlinx.coroutines.MainScope
+import kotlinx.coroutines.launch
+
+class LazyListFragment : BaseFragment() {
+
+ private var adapters by mutableStateOf(listOf<AdAdapterItem>())
+
+ override fun handleLoadAdFromDrawer(
+ adType: Int,
+ mediationOption: Int,
+ drawViewabilityLayer: Boolean
+ ) {
+ currentAdType = adType
+ currentMediationOption = mediationOption
+ shouldDrawViewabilityLayer = drawViewabilityLayer
+ updateBannerAdAdapter()
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ initializeBannerAdAdapter(count = 10)
+ return ComposeView(requireContext()).apply {
+ // Dispose of the Composition when the view's LifecycleOwner is destroyed
+ setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
+ setContent { AdsList(adapters) }
+ }
+ }
+
+ @Composable
+ private fun AdsList(adAdapters: List<AdAdapterItem>) {
+ LazyColumn(
+ modifier = Modifier.fillMaxSize().padding(16.dp),
+ verticalArrangement = Arrangement.spacedBy(16.dp),
+ ) {
+ items(
+ items = adAdapters,
+ key = { adapterWithId -> adapterWithId.id },
+ contentType = { adapterItem -> adapterItem.contentType }
+ ) { adapterWithId ->
+ SandboxedSdkUi(
+ adapterWithId.adapter,
+ Modifier.fillParentMaxSize(),
+ providerUiOnTop = providerUiOnTop
+ )
+ }
+ }
+ }
+
+ private fun initializeBannerAdAdapter(count: Int) {
+ val coroutineScope = MainScope()
+ coroutineScope.launch {
+ val mutableAdapterList = mutableListOf<AdAdapterItem>()
+ for (i in 1..count) {
+ mutableAdapterList.add(
+ AdAdapterItem(
+ id = i,
+ adapter =
+ SandboxedUiAdapterFactory.createFromCoreLibInfo(
+ getSdkApi()
+ .loadBannerAd(
+ currentAdType,
+ currentMediationOption,
+ false,
+ shouldDrawViewabilityLayer,
+ )
+ )
+ )
+ )
+ }
+ adapters = mutableAdapterList
+ }
+ }
+
+ private fun updateBannerAdAdapter() {
+ val coroutineScope = MainScope()
+ coroutineScope.launch {
+ val updatedAdapterList = mutableListOf<AdAdapterItem>()
+ adapters.forEach { adapterWithId ->
+ updatedAdapterList.add(
+ AdAdapterItem(
+ id = adapterWithId.id,
+ adapter =
+ SandboxedUiAdapterFactory.createFromCoreLibInfo(
+ getSdkApi()
+ .loadBannerAd(
+ currentAdType,
+ currentMediationOption,
+ false,
+ shouldDrawViewabilityLayer,
+ )
+ )
+ )
+ )
+ }
+ adapters = updatedAdapterList
+ }
+ }
+
+ private data class AdAdapterItem(
+ val id: Int,
+ // TODO(b/391558988): Specify content type for PoolingContainer CUJ
+ // in View world for consistency
+ val contentType: String = "BannerAd_$id",
+ val adapter: SandboxedUiAdapter
+ )
+}
diff --git a/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/MainActivity.kt b/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/MainActivity.kt
index 8467ece..ae42f769 100644
--- a/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/MainActivity.kt
+++ b/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/MainActivity.kt
@@ -309,7 +309,14 @@
switchContentFragment(ScrollFragment(), menuItem.title)
}
R.id.item_pooling_container ->
- switchContentFragment(PoolingContainerFragment(), menuItem.title)
+ if (useCompose) {
+ switchContentFragment(
+ LazyListFragment(),
+ "${menuItem.title} ${getString(R.string.compose)}"
+ )
+ } else {
+ switchContentFragment(PoolingContainerFragment(), menuItem.title)
+ }
R.id.item_fullscreen ->
if (useCompose) {
switchContentFragment(
diff --git a/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/ResizeComposeFragment.kt b/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/ResizeComposeFragment.kt
index c537976..9d726f2 100644
--- a/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/ResizeComposeFragment.kt
+++ b/privacysandbox/ui/integration-tests/testapp/src/main/java/androidx/privacysandbox/ui/integration/testapp/ResizeComposeFragment.kt
@@ -22,6 +22,7 @@
import android.view.ViewGroup
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
@@ -31,10 +32,13 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
@@ -60,6 +64,14 @@
val newHeight = newSize(currentDimension.height.value.toInt(), maxSizePixels).dp
bannerDimension = BannerDimension(newWidth, newHeight)
}
+ private var bannerPadding by mutableStateOf(BannerPadding())
+ private val onChangePaddingClicked: (BannerDimension) -> Unit = { currentDimension ->
+ val maxHorizontalPadding = (currentDimension.width.value.toInt() / 2) - 10
+ val maxVerticalPadding = (currentDimension.height.value.toInt() / 2) - 10
+ val horizontalPadding = (10..maxHorizontalPadding).random().dp
+ val verticalPadding = (10..maxVerticalPadding).random().dp
+ bannerPadding = BannerPadding(horizontalPadding, verticalPadding)
+ }
override fun handleLoadAdFromDrawer(
adType: Int,
@@ -81,7 +93,15 @@
return ComposeView(requireContext()).apply {
// Dispose of the Composition when the view's LifecycleOwner is destroyed
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
- setContent { ResizeableBannerAd(adapter, bannerDimension, onBannerDimensionChanged) }
+ setContent {
+ ResizeableBannerAd(
+ adapter,
+ bannerDimension,
+ onBannerDimensionChanged,
+ bannerPadding,
+ onChangePaddingClicked
+ )
+ }
}
}
@@ -89,21 +109,40 @@
fun ResizeableBannerAd(
adapter: SandboxedUiAdapter?,
bannerDimension: BannerDimension,
- onResizeClicked: (BannerDimension) -> Unit
+ onResizeClicked: (BannerDimension) -> Unit,
+ bannerPadding: BannerPadding,
+ onChangePaddingClicked: (BannerDimension) -> Unit,
) {
Column(
modifier = Modifier.fillMaxSize().padding(16.dp),
verticalArrangement = Arrangement.Top,
horizontalAlignment = Alignment.Start
) {
- val sandboxedSdkUiModifier =
+ val localDensity = LocalDensity.current
+ var ssvHeight by remember { mutableStateOf(0.dp) }
+ var ssvWidth by remember { mutableStateOf(0.dp) }
+
+ var sandboxedSdkUiModifier =
+ Modifier.onGloballyPositioned { coordinates ->
+ with(localDensity) {
+ ssvWidth = coordinates.size.width.toDp()
+ ssvHeight = coordinates.size.height.toDp()
+ }
+ }
+ .padding(
+ horizontal = bannerPadding.horizontalPadding,
+ vertical = bannerPadding.verticalPadding
+ )
+
+ sandboxedSdkUiModifier =
if (bannerDimension.height != 0.dp && bannerDimension.width != 0.dp) {
- Modifier.width(bannerDimension.width)
+ sandboxedSdkUiModifier
+ .width(bannerDimension.width)
.weight(
1f,
)
} else {
- Modifier.fillMaxWidth().weight(1f)
+ sandboxedSdkUiModifier.fillMaxWidth().weight(1f)
}
Text("Ad state: $adEventText")
@@ -128,7 +167,12 @@
},
)
}
- Button(onClick = { onResizeClicked(bannerDimension) }) { Text("Resize") }
+ Row {
+ Button(onClick = { onResizeClicked(bannerDimension) }) { Text("Resize") }
+ Button(onClick = { onChangePaddingClicked(BannerDimension(ssvWidth, ssvHeight)) }) {
+ Text("Change padding")
+ }
+ }
}
}
@@ -149,4 +193,6 @@
}
data class BannerDimension(val width: Dp = 0.dp, val height: Dp = 0.dp)
+
+ data class BannerPadding(val horizontalPadding: Dp = 0.dp, val verticalPadding: Dp = 0.dp)
}
diff --git a/privacysandbox/ui/integration-tests/testsdkprovider/build.gradle b/privacysandbox/ui/integration-tests/testsdkprovider/build.gradle
index 767199b..a4e036b0 100644
--- a/privacysandbox/ui/integration-tests/testsdkprovider/build.gradle
+++ b/privacysandbox/ui/integration-tests/testsdkprovider/build.gradle
@@ -34,6 +34,16 @@
privacySandbox {
enable = true
}
+
+ experimentalProperties["android.privacySandboxSdk.apiGenerator"] =
+ project.dependencies.create(project(":privacysandbox:tools:tools-apigenerator"))
+ experimentalProperties["android.privacySandboxSdk.apiGenerator.generatedRuntimeDependencies"] =
+ [libs.kotlinStdlib.get(),
+ libs.kotlinCoroutinesAndroid.get(),
+ libs.kotlinCoroutinesCore.get(),
+ project.dependencies.create(project(":privacysandbox:ui:ui-core")),
+ project.dependencies.create(project(":privacysandbox:ui:ui-client"))
+ ]
}
dependencies {
diff --git a/privacysandbox/ui/ui-client/src/androidTest/java/androidx/privacysandbox/ui/client/test/FailingTestSandboxedUiAdapter.kt b/privacysandbox/ui/ui-client/src/androidTest/java/androidx/privacysandbox/ui/client/test/FailingTestSandboxedUiAdapter.kt
index 79dd0c8..0582407 100644
--- a/privacysandbox/ui/ui-client/src/androidTest/java/androidx/privacysandbox/ui/client/test/FailingTestSandboxedUiAdapter.kt
+++ b/privacysandbox/ui/ui-client/src/androidTest/java/androidx/privacysandbox/ui/client/test/FailingTestSandboxedUiAdapter.kt
@@ -17,15 +17,15 @@
package androidx.privacysandbox.ui.client.test
import android.content.Context
-import android.os.IBinder
import androidx.privacysandbox.ui.core.SandboxedUiAdapter
+import androidx.privacysandbox.ui.core.SessionConstants
import androidx.privacysandbox.ui.provider.AbstractSandboxedUiAdapter
import java.util.concurrent.Executor
class FailingTestSandboxedUiAdapter : AbstractSandboxedUiAdapter() {
override fun openSession(
context: Context,
- windowInputToken: IBinder,
+ sessionConstants: SessionConstants,
initialWidth: Int,
initialHeight: Int,
isZOrderOnTop: Boolean,
diff --git a/privacysandbox/ui/ui-client/src/androidTest/java/androidx/privacysandbox/ui/client/test/SandboxedSdkUiTest.kt b/privacysandbox/ui/ui-client/src/androidTest/java/androidx/privacysandbox/ui/client/test/SandboxedSdkUiTest.kt
index e057517..30a4067 100644
--- a/privacysandbox/ui/ui-client/src/androidTest/java/androidx/privacysandbox/ui/client/test/SandboxedSdkUiTest.kt
+++ b/privacysandbox/ui/ui-client/src/androidTest/java/androidx/privacysandbox/ui/client/test/SandboxedSdkUiTest.kt
@@ -28,6 +28,7 @@
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredSize
import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.ReusableContentHost
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
@@ -542,6 +543,117 @@
assertThat(sandboxedSdkViewUiInfo?.onScreenGeometry?.isEmpty).isFalse()
}
+ @Test
+ fun sessionRemainsOpenWhenSandboxedSdkUiIsDetachedAndNotReleased() {
+ var attached by mutableStateOf(true)
+
+ composeTestRule.setContent {
+ ReusableContentHost(attached) {
+ SandboxedSdkUi(
+ sandboxedUiAdapter = testSandboxedUiAdapter,
+ modifier = Modifier.requiredSize(size),
+ providerUiOnTop = providerUiOnTop,
+ sandboxedSdkViewEventListener = eventListener
+ )
+ }
+ }
+
+ testSandboxedUiAdapter.assertSessionOpened()
+
+ attached = false
+ testSandboxedUiAdapter.assertSessionNotClosed()
+
+ attached = true
+ testSandboxedUiAdapter.assertSessionNotClosed()
+ }
+
+ @Test
+ fun sessionClosesWhenSandboxedSdkUiIsRemovedFromComposition() {
+ var showContent by mutableStateOf(true)
+
+ composeTestRule.setContent {
+ if (showContent) {
+ SandboxedSdkUi(
+ sandboxedUiAdapter = testSandboxedUiAdapter,
+ modifier = Modifier.requiredSize(size),
+ providerUiOnTop = providerUiOnTop,
+ sandboxedSdkViewEventListener = eventListener
+ )
+ }
+ }
+
+ testSandboxedUiAdapter.assertSessionOpened()
+
+ showContent = false
+ composeTestRule.waitForIdle()
+
+ testSandboxedUiAdapter.assertSessionClosed()
+ }
+
+ @Test
+ fun reAddEventListenerWhenSandboxedSdkUiIsReAttachedInLazyList() {
+ var attached by mutableStateOf(true)
+
+ composeTestRule.setContent {
+ ReusableContentHost(attached) {
+ SandboxedSdkUi(
+ sandboxedUiAdapter = testSandboxedUiAdapter,
+ modifier = Modifier.requiredSize(size),
+ providerUiOnTop = providerUiOnTop,
+ sandboxedSdkViewEventListener = eventListener
+ )
+ }
+ }
+
+ // When session is open, the events are received
+ assertThat(eventListener.uiDisplayedLatch.await(TIMEOUT, TimeUnit.MILLISECONDS)).isTrue()
+
+ attached = false
+ composeTestRule.waitForIdle()
+
+ attached = true
+ composeTestRule.waitForIdle()
+
+ // When SandboxedSdkUi is re-attached event listener is added back
+ assertThat(eventListener.uiDisplayedLatch.await(TIMEOUT, TimeUnit.MILLISECONDS)).isTrue()
+ }
+
+ @Test
+ fun onUiClosedWhenSandboxedSdkUiIsRemovedFromComposition() {
+ var showContent by mutableStateOf(true)
+ var attached by mutableStateOf(true)
+
+ composeTestRule.setContent {
+ if (showContent) {
+ ReusableContentHost(attached) {
+ SandboxedSdkUi(
+ sandboxedUiAdapter = testSandboxedUiAdapter,
+ modifier = Modifier.requiredSize(size),
+ providerUiOnTop = providerUiOnTop,
+ sandboxedSdkViewEventListener = eventListener
+ )
+ }
+ }
+ }
+
+ // verify onUiDisplayed() is called
+ assertThat(eventListener.uiDisplayedLatch.await(TIMEOUT, TimeUnit.MILLISECONDS)).isTrue()
+
+ // detach SandboxedSdkUi from composition hierarchy of ReusableContentHost
+ attached = false
+ composeTestRule.waitForIdle()
+
+ // verify onUiClosed() is not called
+ assertThat(eventListener.sessionClosedLatch.await(TIMEOUT, TimeUnit.MILLISECONDS)).isFalse()
+
+ // remove SandboxedSdkUi from composition hierarchy
+ showContent = false
+ composeTestRule.waitForIdle()
+
+ // verify onUiClosed() is called
+ assertThat(eventListener.sessionClosedLatch.await(TIMEOUT, TimeUnit.MILLISECONDS)).isTrue()
+ }
+
private fun addNodeToLayout() {
composeTestRule.setContent {
SandboxedSdkUi(
diff --git a/privacysandbox/ui/ui-client/src/androidTest/java/androidx/privacysandbox/ui/client/test/SandboxedSdkViewTest.kt b/privacysandbox/ui/ui-client/src/androidTest/java/androidx/privacysandbox/ui/client/test/SandboxedSdkViewTest.kt
index 9002a19..a9300b4 100644
--- a/privacysandbox/ui/ui-client/src/androidTest/java/androidx/privacysandbox/ui/client/test/SandboxedSdkViewTest.kt
+++ b/privacysandbox/ui/ui-client/src/androidTest/java/androidx/privacysandbox/ui/client/test/SandboxedSdkViewTest.kt
@@ -19,6 +19,7 @@
import android.content.Context
import android.content.Intent
import android.content.pm.ActivityInfo
+import android.os.Build
import android.os.IBinder
import android.view.SurfaceView
import android.view.View
@@ -27,10 +28,12 @@
import android.view.ViewTreeObserver
import android.widget.LinearLayout
import android.widget.TextView
+import androidx.annotation.RequiresApi
import androidx.lifecycle.Lifecycle
import androidx.privacysandbox.ui.client.view.SandboxedSdkView
import androidx.privacysandbox.ui.core.BackwardCompatUtil
import androidx.privacysandbox.ui.core.SandboxedUiAdapter
+import androidx.privacysandbox.ui.core.SessionConstants
import androidx.privacysandbox.ui.integration.testingutils.TestEventListener
import androidx.privacysandbox.ui.provider.AbstractSandboxedUiAdapter
import androidx.test.ext.junit.rules.ActivityScenarioRule
@@ -270,7 +273,7 @@
class CustomUiAdapter : AbstractSandboxedUiAdapter() {
override fun openSession(
context: Context,
- windowInputToken: IBinder,
+ sessionConstants: SessionConstants,
initialWidth: Int,
initialHeight: Int,
isZOrderOnTop: Boolean,
@@ -307,7 +310,7 @@
class CustomUiAdapter : AbstractSandboxedUiAdapter() {
override fun openSession(
context: Context,
- windowInputToken: IBinder,
+ sessionConstants: SessionConstants,
initialWidth: Int,
initialHeight: Int,
isZOrderOnTop: Boolean,
@@ -441,8 +444,10 @@
*/
@SuppressLint("NewApi") // Test runs on U+ devices
@Test
- fun inputTokenIsCorrect() {
- // Input token is only needed when provider can be located on a separate process.
+ fun windowInputTokenIsCorrect() {
+ // Input token is only needed when provider can be located on a separate process. It is
+ // also only needed on U devices, on V+ we will use InputTransferToken
+ assumeTrue(Build.VERSION.SDK_INT == Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
assumeTrue(BackwardCompatUtil.canProviderBeRemote())
lateinit var layout: LinearLayout
val surfaceView = SurfaceView(context)
@@ -471,7 +476,19 @@
// Verify that the UI adapter receives the same host token object when opening a session.
addViewToLayout()
testSandboxedUiAdapter.assertSessionOpened()
- assertThat(testSandboxedUiAdapter.inputToken).isEqualTo(token)
+ assertThat(testSandboxedUiAdapter.sessionConstants?.windowInputToken).isEqualTo(token)
+ }
+
+ @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ @Test
+ fun inputTransferTokenIsCorrect() {
+ // InputTransferToken is only sent on V+
+ assumeTrue(Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ addViewToLayoutAndWaitToBeActive()
+ val inputTransferToken = view.rootSurfaceControl?.inputTransferToken
+ assertThat(testSandboxedUiAdapter.sessionConstants?.inputTransferToken).isNotNull()
+ assertThat(testSandboxedUiAdapter.sessionConstants?.inputTransferToken)
+ .isEqualTo(inputTransferToken)
}
@Ignore("b/307829956")
diff --git a/privacysandbox/ui/ui-client/src/androidTest/java/androidx/privacysandbox/ui/client/test/TestSandboxedUiAdapter.kt b/privacysandbox/ui/ui-client/src/androidTest/java/androidx/privacysandbox/ui/client/test/TestSandboxedUiAdapter.kt
index f9ebe61..1eff37a 100644
--- a/privacysandbox/ui/ui-client/src/androidTest/java/androidx/privacysandbox/ui/client/test/TestSandboxedUiAdapter.kt
+++ b/privacysandbox/ui/ui-client/src/androidTest/java/androidx/privacysandbox/ui/client/test/TestSandboxedUiAdapter.kt
@@ -18,11 +18,11 @@
import android.content.Context
import android.content.res.Configuration
import android.os.Bundle
-import android.os.IBinder
import android.os.SystemClock
import android.view.View
import androidx.privacysandbox.ui.core.SandboxedSdkViewUiInfo
import androidx.privacysandbox.ui.core.SandboxedUiAdapter
+import androidx.privacysandbox.ui.core.SessionConstants
import androidx.privacysandbox.ui.provider.AbstractSandboxedUiAdapter
import com.google.common.truth.Truth
import java.util.concurrent.CountDownLatch
@@ -35,7 +35,8 @@
var internalClient: SandboxedUiAdapter.SessionClient? = null
var testSession: TestSession? = null
var isZOrderOnTop = true
- var inputToken: IBinder? = null
+ var sessionConstants: SessionConstants? = null
+
// When set to true, the onSessionOpened callback will only be invoked when specified
// by the test. This is to test race conditions when the session is being loaded.
var delayOpenSessionCallback = false
@@ -46,7 +47,7 @@
override fun openSession(
context: Context,
- windowInputToken: IBinder,
+ sessionConstants: SessionConstants,
initialWidth: Int,
initialHeight: Int,
isZOrderOnTop: Boolean,
@@ -61,7 +62,7 @@
}
isSessionOpened = true
this.isZOrderOnTop = isZOrderOnTop
- this.inputToken = windowInputToken
+ this.sessionConstants = sessionConstants
openSessionLatch.countDown()
}
}
@@ -102,6 +103,13 @@
.isFalse()
}
+ internal fun assertSessionClosed() {
+ Truth.assertThat(
+ sessionClosedLatch.await(SandboxedSdkViewTest.TIMEOUT, TimeUnit.MILLISECONDS)
+ )
+ .isTrue()
+ }
+
inner class TestSession(context: Context, override val signalOptions: Set<String>) :
SandboxedUiAdapter.Session {
var zOrderChangedLatch: CountDownLatch = CountDownLatch(1)
diff --git a/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/ClientDelegatingAdapter.kt b/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/ClientDelegatingAdapter.kt
index 404139d..7eee3a9 100644
--- a/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/ClientDelegatingAdapter.kt
+++ b/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/ClientDelegatingAdapter.kt
@@ -18,7 +18,6 @@
import android.content.Context
import android.os.Bundle
-import android.os.IBinder
import androidx.annotation.GuardedBy
import androidx.core.util.Consumer
import androidx.privacysandbox.ui.client.SandboxedUiAdapterFactory.createFromCoreLibInfo
@@ -29,6 +28,7 @@
import androidx.privacysandbox.ui.core.ISessionRefreshCallback
import androidx.privacysandbox.ui.core.RemoteCallManager.tryToCallRemoteObject
import androidx.privacysandbox.ui.core.SandboxedUiAdapter
+import androidx.privacysandbox.ui.core.SessionConstants
import java.util.concurrent.Executor
import kotlin.coroutines.resume
import kotlinx.coroutines.*
@@ -143,7 +143,7 @@
*/
override fun openSession(
context: Context,
- windowInputToken: IBinder,
+ sessionConstants: SessionConstants,
initialWidth: Int,
initialHeight: Int,
isZOrderOnTop: Boolean,
@@ -153,7 +153,7 @@
val delegateUsed: SandboxedUiAdapter = synchronized(lock) { latestDelegate }
delegateUsed.openSession(
context,
- windowInputToken,
+ sessionConstants,
initialWidth,
initialHeight,
isZOrderOnTop,
diff --git a/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/SandboxedUiAdapterFactory.kt b/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/SandboxedUiAdapterFactory.kt
index e80268a..2339430 100644
--- a/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/SandboxedUiAdapterFactory.kt
+++ b/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/SandboxedUiAdapterFactory.kt
@@ -22,7 +22,6 @@
import android.hardware.display.DisplayManager
import android.os.Build
import android.os.Bundle
-import android.os.IBinder
import android.util.Log
import android.view.Display
import android.view.SurfaceControlViewHost
@@ -39,6 +38,7 @@
import androidx.privacysandbox.ui.core.RemoteCallManager.closeRemoteSession
import androidx.privacysandbox.ui.core.RemoteCallManager.tryToCallRemoteObject
import androidx.privacysandbox.ui.core.SandboxedUiAdapter
+import androidx.privacysandbox.ui.core.SessionConstants
import java.lang.reflect.InvocationHandler
import java.lang.reflect.Method
import java.lang.reflect.Proxy
@@ -113,6 +113,16 @@
uiProviderBinder.javaClass.classLoader
)
+ private val targetSessionConstantsClass =
+ Class.forName(
+ SessionConstants::class.java.name,
+ /* initialize = */ false,
+ uiProviderBinder.javaClass.classLoader
+ )
+
+ private val targetSessionConstantsCompanionObject =
+ targetSessionConstantsClass.getDeclaredField("Companion").get(null)
+
// The adapterInterface provided must have a openSession method on its class.
// Since the object itself has been instantiated on a different classloader, we
// need reflection to get hold of it.
@@ -125,7 +135,7 @@
.getMethod(
"openSession",
Context::class.java,
- IBinder::class.java,
+ targetSessionConstantsClass,
Int::class.java,
Int::class.java,
Boolean::class.java,
@@ -133,9 +143,15 @@
targetSessionClientClass
)
+ private val fromBundleMethod: Method =
+ targetSessionConstantsCompanionObject.javaClass.getMethod(
+ "fromBundle",
+ Bundle::class.java
+ )
+
override fun openSession(
context: Context,
- windowInputToken: IBinder,
+ sessionConstants: SessionConstants,
initialWidth: Int,
initialHeight: Int,
isZOrderOnTop: Boolean,
@@ -154,7 +170,10 @@
openSessionMethod.invoke(
uiProviderBinder,
context,
- windowInputToken,
+ fromBundleMethod.invoke(
+ targetSessionConstantsCompanionObject,
+ SessionConstants.toBundle(sessionConstants)
+ ),
initialWidth,
initialHeight,
isZOrderOnTop,
@@ -270,7 +289,7 @@
override fun openSession(
context: Context,
- windowInputToken: IBinder,
+ sessionConstants: SessionConstants,
initialWidth: Int,
initialHeight: Int,
isZOrderOnTop: Boolean,
@@ -283,7 +302,7 @@
tryToCallRemoteObject(adapterInterface) {
this.openRemoteSession(
- windowInputToken,
+ SessionConstants.toBundle(sessionConstants),
displayId,
initialWidth,
initialHeight,
diff --git a/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/view/SandboxedSdkUi.kt b/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/view/SandboxedSdkUi.kt
index 29f873a..1a1b137 100644
--- a/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/view/SandboxedSdkUi.kt
+++ b/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/view/SandboxedSdkUi.kt
@@ -17,8 +17,6 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.viewinterop.AndroidView
@@ -41,33 +39,17 @@
providerUiOnTop: Boolean = true,
sandboxedSdkViewEventListener: SandboxedSdkViewEventListener? = null
) {
- val delegatedListener =
- remember {
- object : SandboxedSdkViewEventListener {
- var delegate by mutableStateOf(sandboxedSdkViewEventListener)
-
- override fun onUiDisplayed() {
- delegate?.onUiDisplayed()
- }
-
- override fun onUiError(error: Throwable) {
- delegate?.onUiError(error)
- }
-
- override fun onUiClosed() {
- delegate?.onUiClosed()
- }
- }
- }
- .apply { delegate = sandboxedSdkViewEventListener }
AndroidView(
modifier = modifier,
- factory = { context ->
- SandboxedSdkView(context).apply { setEventListener(delegatedListener) }
- },
+ factory = { context -> SandboxedSdkView(context).apply { isInComposeNode = true } },
update = { view ->
- view.setAdapter(sandboxedUiAdapter)
- view.orderProviderUiAboveClientUi(providerUiOnTop)
- }
+ view.apply {
+ setEventListener(sandboxedSdkViewEventListener)
+ setAdapter(sandboxedUiAdapter)
+ orderProviderUiAboveClientUi(providerUiOnTop)
+ }
+ },
+ onReset = { view -> view.setEventListener(null) },
+ onRelease = { view -> view.closeClient() }
)
}
diff --git a/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/view/SandboxedSdkView.kt b/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/view/SandboxedSdkView.kt
index 01e3212..a4f35ac 100644
--- a/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/view/SandboxedSdkView.kt
+++ b/privacysandbox/ui/ui-client/src/main/java/androidx/privacysandbox/ui/client/view/SandboxedSdkView.kt
@@ -18,9 +18,7 @@
import android.content.Context
import android.content.res.Configuration
-import android.os.Binder
import android.os.Build
-import android.os.IBinder
import android.util.AttributeSet
import android.view.SurfaceView
import android.view.View
@@ -35,6 +33,7 @@
import androidx.customview.poolingcontainer.removePoolingContainerListener
import androidx.privacysandbox.ui.core.SandboxedUiAdapter
import androidx.privacysandbox.ui.core.SandboxedUiAdapter.SessionClient
+import androidx.privacysandbox.ui.core.SessionConstants
import kotlin.math.min
/** A listener for events relating to the SandboxedSdkView UI presentation. */
@@ -81,14 +80,15 @@
private var requestedWidth = -1
private var requestedHeight = -1
private var isTransitionGroupSet = false
- private var windowInputToken: IBinder? = null
private var previousChildWidth = -1
private var previousChildHeight = -1
+ private var sessionConstants: SessionConstants? = null
private var viewContainingPoolingContainerListener: View? = null
private var poolingContainerListener = PoolingContainerListener {}
private var eventListener: SandboxedSdkViewEventListener? = null
private val frameCommitCallback = Runnable { eventListener?.onUiDisplayed() }
internal var signalMeasurer: SandboxedSdkViewSignalMeasurer? = null
+ internal var isInComposeNode = false
/**
* Sets an event listener to the [SandboxedSdkView] and starts reporting the new events. To
@@ -136,7 +136,7 @@
val adapter = adapter
if (
adapter != null &&
- windowInputToken != null &&
+ sessionConstants != null &&
width > 0 &&
height > 0 &&
windowVisibility == View.VISIBLE
@@ -145,7 +145,7 @@
client = Client(this)
adapter.openSession(
context,
- windowInputToken!!,
+ sessionConstants!!,
width,
height,
isZOrderOnTop,
@@ -157,7 +157,7 @@
this.refreshCallback = callback
adapter.openSession(
context,
- windowInputToken!!,
+ sessionConstants!!,
width,
height,
isZOrderOnTop,
@@ -307,10 +307,10 @@
signalMeasurer?.maybeSendSignals()
}
- private fun closeClient() {
+ internal fun closeClient() {
client?.close()
client = null
- windowInputToken = null
+ sessionConstants = null
}
private fun attachPoolingContainerListener() {
@@ -345,16 +345,16 @@
override fun onAttachedToWindow() {
super.onAttachedToWindow()
addCallbacksOnWindowAttachment()
- if (client == null || viewContainingPoolingContainerListener == null) {
- if (this.isWithinPoolingContainer) {
- attachPoolingContainerListener()
- }
+ if (viewContainingPoolingContainerListener == null && this.isWithinPoolingContainer) {
+ attachPoolingContainerListener()
+ }
+ if (client == null) {
CompatImpl.deriveInputTokenAndOpenSession(context, this)
}
}
override fun onDetachedFromWindow() {
- if (!this.isWithinPoolingContainer) {
+ if (!this.isInComposeNode && !this.isWithinPoolingContainer) {
closeClient()
}
removeCallbacksOnWindowDetachment()
@@ -534,13 +534,12 @@
private object CompatImpl {
fun deriveInputTokenAndOpenSession(context: Context, sandboxedSdkView: SandboxedSdkView) {
- // TODO(b/284147223): Remove this logic in V+
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+ Api35PlusImpl.setInputTransferTokenAndOpenSession(sandboxedSdkView)
+ } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
Api34PlusImpl.attachTemporarySurfaceViewAndOpenSession(context, sandboxedSdkView)
} else {
- // the openSession signature requires a non-null input token, so the session
- // will not be opened until this is set
- sandboxedSdkView.windowInputToken = Binder()
+ sandboxedSdkView.sessionConstants = SessionConstants()
sandboxedSdkView.checkClientOpenSession()
}
}
@@ -559,6 +558,19 @@
}
}
+ @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ private object Api35PlusImpl {
+ @JvmStatic
+ fun setInputTransferTokenAndOpenSession(sandboxedSdkView: SandboxedSdkView) {
+ sandboxedSdkView.sessionConstants =
+ SessionConstants(
+ windowInputToken = null,
+ inputTransferToken = sandboxedSdkView.rootSurfaceControl?.inputTransferToken
+ )
+ sandboxedSdkView.checkClientOpenSession()
+ }
+ }
+
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
private object Api34PlusImpl {
@@ -573,7 +585,13 @@
override fun onViewAttachedToWindow(view: View) {
view.removeOnAttachStateChangeListener(this)
@Suppress("DEPRECATION")
- sandboxedSdkView.windowInputToken = surfaceView.hostToken
+ surfaceView.hostToken?.let {
+ sandboxedSdkView.sessionConstants =
+ SessionConstants(
+ windowInputToken = it,
+ inputTransferToken = null
+ )
+ }
sandboxedSdkView.removeTemporarySurfaceView(surfaceView)
sandboxedSdkView.checkClientOpenSession()
}
diff --git a/privacysandbox/ui/ui-core/api/current.txt b/privacysandbox/ui/ui-core/api/current.txt
index 313129f..8a52b96 100644
--- a/privacysandbox/ui/ui-core/api/current.txt
+++ b/privacysandbox/ui/ui-core/api/current.txt
@@ -5,7 +5,7 @@
ctor public DelegatingSandboxedUiAdapter(android.os.Bundle delegate);
method public void addDelegateChangeListener(androidx.privacysandbox.ui.core.DelegatingSandboxedUiAdapter.DelegateChangeListener listener);
method public android.os.Bundle getDelegate();
- method public void openSession(android.content.Context context, android.os.IBinder windowInputToken, int initialWidth, int initialHeight, boolean isZOrderOnTop, java.util.concurrent.Executor clientExecutor, androidx.privacysandbox.ui.core.SandboxedUiAdapter.SessionClient client);
+ method public void openSession(android.content.Context context, androidx.privacysandbox.ui.core.SessionConstants sessionConstants, int initialWidth, int initialHeight, boolean isZOrderOnTop, java.util.concurrent.Executor clientExecutor, androidx.privacysandbox.ui.core.SandboxedUiAdapter.SessionClient client);
method public void removeDelegateChangeListener(androidx.privacysandbox.ui.core.DelegatingSandboxedUiAdapter.DelegateChangeListener listener);
method public suspend Object? updateDelegate(android.os.Bundle delegate, kotlin.coroutines.Continuation<? super kotlin.Unit>);
}
@@ -41,7 +41,7 @@
}
public interface SandboxedUiAdapter {
- method public void openSession(android.content.Context context, android.os.IBinder windowInputToken, int initialWidth, int initialHeight, boolean isZOrderOnTop, java.util.concurrent.Executor clientExecutor, androidx.privacysandbox.ui.core.SandboxedUiAdapter.SessionClient client);
+ method public void openSession(android.content.Context context, androidx.privacysandbox.ui.core.SessionConstants sessionConstants, int initialWidth, int initialHeight, boolean isZOrderOnTop, java.util.concurrent.Executor clientExecutor, androidx.privacysandbox.ui.core.SandboxedUiAdapter.SessionClient client);
}
public static interface SandboxedUiAdapter.Session extends java.lang.AutoCloseable {
@@ -70,6 +70,17 @@
field public static final int apiVersion = 1; // 0x1
}
+ public final class SessionConstants {
+ method public android.window.InputTransferToken? getInputTransferToken();
+ method public android.os.IBinder? getWindowInputToken();
+ property @RequiresApi(android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public final android.window.InputTransferToken? inputTransferToken;
+ property public final android.os.IBinder? windowInputToken;
+ field public static final androidx.privacysandbox.ui.core.SessionConstants.Companion Companion;
+ }
+
+ public static final class SessionConstants.Companion {
+ }
+
public interface SessionObserver {
method public void onSessionClosed();
method public void onSessionOpened(androidx.privacysandbox.ui.core.SessionObserverContext sessionObserverContext);
diff --git a/privacysandbox/ui/ui-core/api/restricted_current.txt b/privacysandbox/ui/ui-core/api/restricted_current.txt
index 313129f..8a52b96 100644
--- a/privacysandbox/ui/ui-core/api/restricted_current.txt
+++ b/privacysandbox/ui/ui-core/api/restricted_current.txt
@@ -5,7 +5,7 @@
ctor public DelegatingSandboxedUiAdapter(android.os.Bundle delegate);
method public void addDelegateChangeListener(androidx.privacysandbox.ui.core.DelegatingSandboxedUiAdapter.DelegateChangeListener listener);
method public android.os.Bundle getDelegate();
- method public void openSession(android.content.Context context, android.os.IBinder windowInputToken, int initialWidth, int initialHeight, boolean isZOrderOnTop, java.util.concurrent.Executor clientExecutor, androidx.privacysandbox.ui.core.SandboxedUiAdapter.SessionClient client);
+ method public void openSession(android.content.Context context, androidx.privacysandbox.ui.core.SessionConstants sessionConstants, int initialWidth, int initialHeight, boolean isZOrderOnTop, java.util.concurrent.Executor clientExecutor, androidx.privacysandbox.ui.core.SandboxedUiAdapter.SessionClient client);
method public void removeDelegateChangeListener(androidx.privacysandbox.ui.core.DelegatingSandboxedUiAdapter.DelegateChangeListener listener);
method public suspend Object? updateDelegate(android.os.Bundle delegate, kotlin.coroutines.Continuation<? super kotlin.Unit>);
}
@@ -41,7 +41,7 @@
}
public interface SandboxedUiAdapter {
- method public void openSession(android.content.Context context, android.os.IBinder windowInputToken, int initialWidth, int initialHeight, boolean isZOrderOnTop, java.util.concurrent.Executor clientExecutor, androidx.privacysandbox.ui.core.SandboxedUiAdapter.SessionClient client);
+ method public void openSession(android.content.Context context, androidx.privacysandbox.ui.core.SessionConstants sessionConstants, int initialWidth, int initialHeight, boolean isZOrderOnTop, java.util.concurrent.Executor clientExecutor, androidx.privacysandbox.ui.core.SandboxedUiAdapter.SessionClient client);
}
public static interface SandboxedUiAdapter.Session extends java.lang.AutoCloseable {
@@ -70,6 +70,17 @@
field public static final int apiVersion = 1; // 0x1
}
+ public final class SessionConstants {
+ method public android.window.InputTransferToken? getInputTransferToken();
+ method public android.os.IBinder? getWindowInputToken();
+ property @RequiresApi(android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) public final android.window.InputTransferToken? inputTransferToken;
+ property public final android.os.IBinder? windowInputToken;
+ field public static final androidx.privacysandbox.ui.core.SessionConstants.Companion Companion;
+ }
+
+ public static final class SessionConstants.Companion {
+ }
+
public interface SessionObserver {
method public void onSessionClosed();
method public void onSessionOpened(androidx.privacysandbox.ui.core.SessionObserverContext sessionObserverContext);
diff --git a/privacysandbox/ui/ui-core/build.gradle b/privacysandbox/ui/ui-core/build.gradle
index f451589..a4690d0 100644
--- a/privacysandbox/ui/ui-core/build.gradle
+++ b/privacysandbox/ui/ui-core/build.gradle
@@ -48,6 +48,7 @@
buildFeatures {
aidl = true
}
+ compileSdk 35
}
androidx {
diff --git a/privacysandbox/ui/ui-core/src/main/aidl/androidx/privacysandbox/ui/core/ISandboxedUiAdapter.aidl b/privacysandbox/ui/ui-core/src/main/aidl/androidx/privacysandbox/ui/core/ISandboxedUiAdapter.aidl
index 63aa0ac..69fbf09 100644
--- a/privacysandbox/ui/ui-core/src/main/aidl/androidx/privacysandbox/ui/core/ISandboxedUiAdapter.aidl
+++ b/privacysandbox/ui/ui-core/src/main/aidl/androidx/privacysandbox/ui/core/ISandboxedUiAdapter.aidl
@@ -18,11 +18,12 @@
import androidx.privacysandbox.ui.core.IRemoteSessionClient;
import android.content.Context;
+import android.os.Bundle;
@JavaPassthrough(annotation="@androidx.annotation.RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY)")
oneway interface ISandboxedUiAdapter {
@JavaPassthrough(annotation="@androidx.annotation.RequiresApi(34)")
void openRemoteSession(
- IBinder hostToken, int displayId, int initialWidth, int initialHeight, boolean isZOrderOnTop,
+ in Bundle sessionConstants, int displayId, int initialWidth, int initialHeight, boolean isZOrderOnTop,
IRemoteSessionClient remoteSessionClient);
}
\ No newline at end of file
diff --git a/privacysandbox/ui/ui-core/src/main/java/androidx/privacysandbox/ui/core/DelegatingSandboxedUiAdapter.kt b/privacysandbox/ui/ui-core/src/main/java/androidx/privacysandbox/ui/core/DelegatingSandboxedUiAdapter.kt
index 3c0ad76..7e8cccb4 100644
--- a/privacysandbox/ui/ui-core/src/main/java/androidx/privacysandbox/ui/core/DelegatingSandboxedUiAdapter.kt
+++ b/privacysandbox/ui/ui-core/src/main/java/androidx/privacysandbox/ui/core/DelegatingSandboxedUiAdapter.kt
@@ -19,7 +19,6 @@
import android.annotation.SuppressLint
import android.content.Context
import android.os.Bundle
-import android.os.IBinder
import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.Executor
import kotlinx.coroutines.coroutineScope
@@ -71,7 +70,7 @@
override fun openSession(
context: Context,
- windowInputToken: IBinder,
+ sessionConstants: SessionConstants,
initialWidth: Int,
initialHeight: Int,
isZOrderOnTop: Boolean,
diff --git a/privacysandbox/ui/ui-core/src/main/java/androidx/privacysandbox/ui/core/SandboxedUiAdapter.kt b/privacysandbox/ui/ui-core/src/main/java/androidx/privacysandbox/ui/core/SandboxedUiAdapter.kt
index d1b6995..23c219e 100644
--- a/privacysandbox/ui/ui-core/src/main/java/androidx/privacysandbox/ui/core/SandboxedUiAdapter.kt
+++ b/privacysandbox/ui/ui-core/src/main/java/androidx/privacysandbox/ui/core/SandboxedUiAdapter.kt
@@ -19,7 +19,6 @@
import android.content.Context
import android.content.res.Configuration
import android.os.Bundle
-import android.os.IBinder
import android.view.View
import java.lang.AutoCloseable
import java.util.concurrent.Executor
@@ -38,7 +37,7 @@
*/
fun openSession(
context: Context,
- windowInputToken: IBinder,
+ sessionConstants: SessionConstants,
initialWidth: Int,
initialHeight: Int,
isZOrderOnTop: Boolean,
diff --git a/privacysandbox/ui/ui-core/src/main/java/androidx/privacysandbox/ui/core/SessionConstants.kt b/privacysandbox/ui/ui-core/src/main/java/androidx/privacysandbox/ui/core/SessionConstants.kt
new file mode 100644
index 0000000..b54cc66
--- /dev/null
+++ b/privacysandbox/ui/ui-core/src/main/java/androidx/privacysandbox/ui/core/SessionConstants.kt
@@ -0,0 +1,121 @@
+/*
+ * 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.privacysandbox.ui.core
+
+import android.os.Build
+import android.os.Bundle
+import android.os.IBinder
+import android.window.InputTransferToken
+import androidx.annotation.RequiresApi
+import androidx.annotation.RestrictTo
+
+/**
+ * A class containing values that will be constant for the lifetime of a
+ * [SandboxedUiAdapter.Session].
+ */
+class SessionConstants
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+constructor(
+ /**
+ * The input token of the window hosting this session.
+ *
+ * This value will be used when [Build.VERSION.SDK_INT] is equal to
+ * [Build.VERSION_CODES.UPSIDE_DOWN_CAKE].
+ */
+ val windowInputToken: IBinder?,
+ @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ /**
+ * The input transfer token of the window hosting this session.
+ *
+ * This will be non-null when [Build.VERSION.SDK_INT] is greater than
+ * [Build.VERSION_CODES.UPSIDE_DOWN_CAKE].
+ */
+ val inputTransferToken: InputTransferToken?
+) {
+
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor() : this(null, null)
+
+ companion object {
+ private const val KEY_WINDOW_INPUT_TOKEN = "windowInputToken"
+ private const val KEY_INPUT_TRANSFER_TOKEN = "inputTransferToken"
+
+ @JvmStatic
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ fun toBundle(sessionConstants: SessionConstants): Bundle {
+ val bundle = Bundle()
+ sessionConstants.windowInputToken?.let { bundle.putBinder(KEY_WINDOW_INPUT_TOKEN, it) }
+ CompatImpl.addInputTransferTokenToBundle(sessionConstants, bundle)
+ return bundle
+ }
+
+ @JvmStatic
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ fun fromBundle(bundle: Bundle): SessionConstants {
+ val windowInputToken = bundle.getBinder(KEY_WINDOW_INPUT_TOKEN)
+ val inputTransferToken = CompatImpl.deriveInputTransferToken(bundle)
+ return SessionConstants(windowInputToken, inputTransferToken)
+ }
+ }
+
+ override fun toString() =
+ "SessionConstants windowInputToken=$windowInputToken, inputTransferToken=$inputTransferToken"
+
+ override fun hashCode(): Int {
+ var result = windowInputToken.hashCode()
+ result += 31 * inputTransferToken.hashCode()
+ return result
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is SessionConstants) return false
+
+ return windowInputToken == other.windowInputToken &&
+ inputTransferToken == other.inputTransferToken
+ }
+
+ private object CompatImpl {
+
+ fun deriveInputTransferToken(bundle: Bundle): InputTransferToken? {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+ return Api35PlusImpl.deriveInputTransferToken(bundle)
+ } else {
+ return null
+ }
+ }
+
+ fun addInputTransferTokenToBundle(sessionConstants: SessionConstants, bundle: Bundle) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+ Api35PlusImpl.addInputTransferTokenToBundle(sessionConstants, bundle)
+ }
+ }
+
+ @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ private object Api35PlusImpl {
+ fun deriveInputTransferToken(bundle: Bundle): InputTransferToken? {
+ return bundle.getParcelable(
+ KEY_INPUT_TRANSFER_TOKEN,
+ InputTransferToken::class.java
+ )
+ }
+
+ fun addInputTransferTokenToBundle(sessionConstants: SessionConstants, bundle: Bundle) {
+ bundle.putParcelable(KEY_INPUT_TRANSFER_TOKEN, sessionConstants.inputTransferToken)
+ }
+ }
+ }
+}
diff --git a/privacysandbox/ui/ui-provider/build.gradle b/privacysandbox/ui/ui-provider/build.gradle
index 0ebe484..e5c4b13 100644
--- a/privacysandbox/ui/ui-provider/build.gradle
+++ b/privacysandbox/ui/ui-provider/build.gradle
@@ -51,6 +51,7 @@
android {
namespace = "androidx.privacysandbox.ui.provider"
+ compileSdk 35
}
androidx {
diff --git a/privacysandbox/ui/ui-provider/src/main/java/androidx/privacysandbox/ui/provider/BinderAdapterDelegate.kt b/privacysandbox/ui/ui-provider/src/main/java/androidx/privacysandbox/ui/provider/BinderAdapterDelegate.kt
index 697e3c8..c77b49c 100644
--- a/privacysandbox/ui/ui-provider/src/main/java/androidx/privacysandbox/ui/provider/BinderAdapterDelegate.kt
+++ b/privacysandbox/ui/ui-provider/src/main/java/androidx/privacysandbox/ui/provider/BinderAdapterDelegate.kt
@@ -23,7 +23,6 @@
import android.os.Build
import android.os.Bundle
import android.os.Handler
-import android.os.IBinder
import android.os.Looper
import android.util.Log
import android.view.Display
@@ -42,6 +41,7 @@
import androidx.privacysandbox.ui.core.ProtocolConstants
import androidx.privacysandbox.ui.core.RemoteCallManager.tryToCallRemoteObject
import androidx.privacysandbox.ui.core.SandboxedUiAdapter
+import androidx.privacysandbox.ui.core.SessionConstants
import androidx.privacysandbox.ui.core.SessionObserver
import androidx.privacysandbox.ui.core.SessionObserverContext
import androidx.privacysandbox.ui.provider.impl.DeferredSessionClient
@@ -123,7 +123,7 @@
/** Called in local mode via reflection. */
override fun openSession(
context: Context,
- windowInputToken: IBinder,
+ sessionConstants: SessionConstants,
initialWidth: Int,
initialHeight: Int,
isZOrderOnTop: Boolean,
@@ -137,7 +137,7 @@
val displayContext = sandboxContext.createDisplayContext(display)
openSessionInternal(
displayContext,
- windowInputToken,
+ sessionConstants,
initialWidth,
initialHeight,
isZOrderOnTop,
@@ -149,7 +149,7 @@
/** Called in remote mode via binder call. */
override fun openRemoteSession(
- windowInputToken: IBinder,
+ sessionConstants: Bundle,
displayId: Int,
initialWidth: Int,
initialHeight: Int,
@@ -162,7 +162,7 @@
}
return
}
-
+ val constants = SessionConstants.fromBundle(sessionConstants)
MainThreadExecutor.execute {
try {
val displayManager =
@@ -173,10 +173,10 @@
val deferredClient =
DeferredSessionClient.create(
clientFactory = {
- Api34PlusImpl.createSessionClientProxy(
+ RemoteCompatImpl.createSessionClientProxy(
displayContext,
display,
- windowInputToken,
+ constants,
isZOrderOnTop,
remoteSessionClient
)
@@ -191,7 +191,7 @@
openSessionInternal(
displayContext,
- windowInputToken,
+ constants,
initialWidth,
initialHeight,
isZOrderOnTop,
@@ -210,7 +210,7 @@
private fun openSessionInternal(
context: Context,
- windowInputToken: IBinder,
+ sessionConstants: SessionConstants,
initialWidth: Int,
initialHeight: Int,
isZOrderOnTop: Boolean,
@@ -219,7 +219,7 @@
) {
adapter.openSession(
context,
- windowInputToken,
+ sessionConstants,
initialWidth,
initialHeight,
isZOrderOnTop,
@@ -412,17 +412,28 @@
}
}
+ /**
+ * Provides backward compat support for APIs.
+ *
+ * If the API is available, it's called from a version-specific static inner class gated with
+ * version check, otherwise a fallback action is taken depending on the situation.
+ */
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
- private object Api34PlusImpl {
+ private object RemoteCompatImpl {
+
fun createSessionClientProxy(
displayContext: Context,
display: Display,
- windowInputToken: IBinder,
+ sessionConstants: SessionConstants,
isZOrderOnTop: Boolean,
remoteSessionClient: IRemoteSessionClient
): SessionClientProxy {
val surfaceControlViewHost =
- SurfaceControlViewHost(displayContext, display, windowInputToken)
+ checkNotNull(
+ createSurfaceControlViewHost(displayContext, display, sessionConstants)
+ ) {
+ "Failed to create SurfaceControlViewHost"
+ }
val touchTransferringView =
TouchFocusTransferringView(displayContext, surfaceControlViewHost)
return SessionClientProxy(
@@ -432,5 +443,50 @@
remoteSessionClient
)
}
+
+ fun createSurfaceControlViewHost(
+ displayContext: Context,
+ display: Display,
+ sessionConstants: SessionConstants
+ ): SurfaceControlViewHost? {
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+ Api35PlusImpl.createSurfaceControlViewHost(
+ displayContext,
+ display,
+ sessionConstants
+ )
+ } else
+ Api34PlusImpl.createSurfaceControlViewHost(
+ displayContext,
+ display,
+ sessionConstants
+ )
+ }
+
+ @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ private object Api35PlusImpl {
+
+ @JvmStatic
+ fun createSurfaceControlViewHost(
+ context: Context,
+ display: Display,
+ sessionConstants: SessionConstants
+ ): SurfaceControlViewHost {
+ return SurfaceControlViewHost(context, display, sessionConstants.inputTransferToken)
+ }
+ }
+
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ private object Api34PlusImpl {
+
+ @JvmStatic
+ fun createSurfaceControlViewHost(
+ context: Context,
+ display: Display,
+ sessionConstants: SessionConstants
+ ): SurfaceControlViewHost {
+ return SurfaceControlViewHost(context, display, sessionConstants.windowInputToken)
+ }
+ }
}
}
diff --git a/privacysandbox/ui/ui-provider/src/main/java/androidx/privacysandbox/ui/provider/TouchFocusTransferringView.kt b/privacysandbox/ui/ui-provider/src/main/java/androidx/privacysandbox/ui/provider/TouchFocusTransferringView.kt
index a9e2182..d1d55a5 100644
--- a/privacysandbox/ui/ui-provider/src/main/java/androidx/privacysandbox/ui/provider/TouchFocusTransferringView.kt
+++ b/privacysandbox/ui/ui-provider/src/main/java/androidx/privacysandbox/ui/provider/TouchFocusTransferringView.kt
@@ -44,6 +44,7 @@
private val scvh: SurfaceControlViewHost = surfaceControlViewHost
private val detector = ScrollDetector(context)
+ @Suppress("deprecation") // transferTouchGestureToHost needs to be called on U- devices
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
detector.onTouchEvent(ev)
if (!detector.isScrolling) {
diff --git a/privacysandbox/ui/ui-tests/src/androidTest/java/androidx/privacysandbox/ui/tests/endtoend/IntegrationTests.kt b/privacysandbox/ui/ui-tests/src/androidTest/java/androidx/privacysandbox/ui/tests/endtoend/IntegrationTests.kt
index 4263899..3e24557 100644
--- a/privacysandbox/ui/ui-tests/src/androidTest/java/androidx/privacysandbox/ui/tests/endtoend/IntegrationTests.kt
+++ b/privacysandbox/ui/ui-tests/src/androidTest/java/androidx/privacysandbox/ui/tests/endtoend/IntegrationTests.kt
@@ -19,15 +19,19 @@
import android.content.Context
import android.content.pm.ActivityInfo
import android.content.res.Configuration
+import android.os.Binder
+import android.os.Build
import android.os.SystemClock
import android.view.MotionEvent
import android.view.View
import android.view.View.OnLayoutChangeListener
import android.view.ViewGroup
import android.widget.LinearLayout
+import androidx.annotation.RequiresApi
import androidx.privacysandbox.ui.client.view.SandboxedSdkView
import androidx.privacysandbox.ui.core.SandboxedSdkViewUiInfo
import androidx.privacysandbox.ui.core.SandboxedUiAdapter
+import androidx.privacysandbox.ui.core.SessionConstants
import androidx.privacysandbox.ui.integration.testingutils.TestEventListener
import androidx.privacysandbox.ui.tests.endtoend.IntegrationTestSetupRule.Companion.INITIAL_HEIGHT
import androidx.privacysandbox.ui.tests.endtoend.IntegrationTestSetupRule.Companion.INITIAL_WIDTH
@@ -138,7 +142,11 @@
@Test
fun testOpenSession_fromAdapter() {
- val adapter = sessionManager.createAdapterAndEstablishSession(viewForSession = null)
+ val adapter =
+ sessionManager.createAdapterAndEstablishSession(
+ viewForSession = null,
+ sessionConstants = deriveSessionConstants()
+ )
assertThat(adapter.session).isNotNull()
}
@@ -526,4 +534,22 @@
)
}
}
+
+ private fun deriveSessionConstants(): SessionConstants {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+ return Api35PlusImpl.deriveSessionConstants(view)
+ } else {
+ return SessionConstants(windowInputToken = Binder(), inputTransferToken = null)
+ }
+ }
+
+ @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ private object Api35PlusImpl {
+ fun deriveSessionConstants(view: SandboxedSdkView): SessionConstants {
+ return SessionConstants(
+ windowInputToken = Binder(),
+ inputTransferToken = view.rootSurfaceControl?.inputTransferToken
+ )
+ }
+ }
}
diff --git a/privacysandbox/ui/ui-tests/src/androidTest/java/androidx/privacysandbox/ui/tests/util/TestSessionManager.kt b/privacysandbox/ui/ui-tests/src/androidTest/java/androidx/privacysandbox/ui/tests/util/TestSessionManager.kt
index b8e878c..ac65321 100644
--- a/privacysandbox/ui/ui-tests/src/androidTest/java/androidx/privacysandbox/ui/tests/util/TestSessionManager.kt
+++ b/privacysandbox/ui/ui-tests/src/androidTest/java/androidx/privacysandbox/ui/tests/util/TestSessionManager.kt
@@ -21,9 +21,7 @@
import android.content.res.Configuration
import android.graphics.Canvas
import android.graphics.Color
-import android.os.Binder
import android.os.Bundle
-import android.os.IBinder
import android.view.Display
import android.view.View
import android.widget.FrameLayout
@@ -32,6 +30,7 @@
import androidx.privacysandbox.ui.core.DelegatingSandboxedUiAdapter
import androidx.privacysandbox.ui.core.ExperimentalFeatures
import androidx.privacysandbox.ui.core.SandboxedUiAdapter
+import androidx.privacysandbox.ui.core.SessionConstants
import androidx.privacysandbox.ui.core.SessionObserver
import androidx.privacysandbox.ui.core.SessionObserverContext
import androidx.privacysandbox.ui.core.SessionObserverFactory
@@ -69,7 +68,8 @@
placeViewInsideFrameLayout: Boolean = false,
viewForSession: SandboxedSdkView?,
testSessionClient: TestSessionClient = TestSessionClient(),
- sessionObserverFactories: List<SessionObserverFactory>? = null
+ sessionObserverFactories: List<SessionObserverFactory>? = null,
+ sessionConstants: SessionConstants = SessionConstants()
): TestSandboxedUiAdapter {
val adapter = TestSandboxedUiAdapter(failToProvideUi, placeViewInsideFrameLayout)
@@ -81,7 +81,7 @@
} else {
adapterFromCoreLibInfo.openSession(
context,
- windowInputToken = Binder(),
+ sessionConstants,
INITIAL_WIDTH,
INITIAL_HEIGHT,
isZOrderOnTop = true,
@@ -199,7 +199,7 @@
override fun openSession(
context: Context,
- windowInputToken: IBinder,
+ sessionConstants: SessionConstants,
initialWidth: Int,
initialHeight: Int,
isZOrderOnTop: Boolean,
diff --git a/room/room-common/api/api_lint.ignore b/room/room-common/api/api_lint.ignore
index 0f16720..934672f 100644
--- a/room/room-common/api/api_lint.ignore
+++ b/room/room-common/api/api_lint.ignore
@@ -1,8 +1,4 @@
// Baseline format: 1.0
-AcronymName: androidx.room.ColumnInfo.SQLiteTypeAffinity:
- Acronyms should not be capitalized in class names: was `SQLiteTypeAffinity`, should this be `SqLiteTypeAffinity`?
-
-
GetterSetterNames: androidx.room.ColumnInfo#index:
Invalid name for boolean property `index`. Should start with one of `has`, `can`, `should`, `is`.
GetterSetterNames: androidx.room.Database#exportSchema:
diff --git a/room/room-common/build.gradle b/room/room-common/build.gradle
index 1cf5db4..e8eed4f 100644
--- a/room/room-common/build.gradle
+++ b/room/room-common/build.gradle
@@ -32,12 +32,14 @@
}
androidXMultiplatform {
+ js()
jvm() {
withJava()
}
mac()
linux()
ios()
+ wasmJs()
defaultPlatform(PlatformIdentifier.JVM)
@@ -45,7 +47,7 @@
commonMain {
dependencies {
api(libs.kotlinStdlib)
- api("androidx.annotation:annotation:1.8.1")
+ api("androidx.annotation:annotation:1.9.1")
}
}
diff --git a/room/room-common/src/jsMain/kotlin/androidx/room/ConstructedBy.js.kt b/room/room-common/src/jsMain/kotlin/androidx/room/ConstructedBy.js.kt
new file mode 100644
index 0000000..b43bd1e
--- /dev/null
+++ b/room/room-common/src/jsMain/kotlin/androidx/room/ConstructedBy.js.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2024 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.room
+
+import kotlin.reflect.AssociatedObjectKey
+import kotlin.reflect.ExperimentalAssociatedObjects
+import kotlin.reflect.KClass
+
+/**
+ * Defines the [androidx.room.RoomDatabaseConstructor] that will instantiate the Room generated
+ * implementation of the annotated [Database].
+ *
+ * A [androidx.room.RoomDatabase] database definition must be annotated with this annotation if it
+ * is located in a common source set on a Kotlin Multiplatform project such that at runtime the
+ * implementation generated by the annotation processor can be used. The [value] must be an 'expect
+ * object' that implements [androidx.room.RoomDatabaseConstructor].
+ *
+ * Example usage:
+ * ```
+ * @Database(version = 1, entities = [Song::class, Album::class])
+ * @ConstructedBy(MusicDatabaseConstructor::class)
+ * abstract class MusicDatabase : RoomDatabase
+ *
+ * expect object MusicDatabaseConstructor : RoomDatabaseConstructor<MusicDatabase>
+ * ```
+ *
+ * @see androidx.room.RoomDatabaseConstructor
+ */
+@OptIn(ExperimentalAssociatedObjects::class)
+@AssociatedObjectKey
+@Target(AnnotationTarget.CLASS)
+@Retention(AnnotationRetention.BINARY)
+actual annotation class ConstructedBy(
+ /**
+ * The 'expect' declaration of an 'object' that implements
+ * [androidx.room.RoomDatabaseConstructor] and is able to instantiate a
+ * [androidx.room.RoomDatabase].
+ */
+ actual val value: KClass<*>
+)
diff --git a/room/room-common/src/wasmJsMain/kotlin/androidx/room/ConstructedBy.wasmJs.kt b/room/room-common/src/wasmJsMain/kotlin/androidx/room/ConstructedBy.wasmJs.kt
new file mode 100644
index 0000000..b43bd1e
--- /dev/null
+++ b/room/room-common/src/wasmJsMain/kotlin/androidx/room/ConstructedBy.wasmJs.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2024 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.room
+
+import kotlin.reflect.AssociatedObjectKey
+import kotlin.reflect.ExperimentalAssociatedObjects
+import kotlin.reflect.KClass
+
+/**
+ * Defines the [androidx.room.RoomDatabaseConstructor] that will instantiate the Room generated
+ * implementation of the annotated [Database].
+ *
+ * A [androidx.room.RoomDatabase] database definition must be annotated with this annotation if it
+ * is located in a common source set on a Kotlin Multiplatform project such that at runtime the
+ * implementation generated by the annotation processor can be used. The [value] must be an 'expect
+ * object' that implements [androidx.room.RoomDatabaseConstructor].
+ *
+ * Example usage:
+ * ```
+ * @Database(version = 1, entities = [Song::class, Album::class])
+ * @ConstructedBy(MusicDatabaseConstructor::class)
+ * abstract class MusicDatabase : RoomDatabase
+ *
+ * expect object MusicDatabaseConstructor : RoomDatabaseConstructor<MusicDatabase>
+ * ```
+ *
+ * @see androidx.room.RoomDatabaseConstructor
+ */
+@OptIn(ExperimentalAssociatedObjects::class)
+@AssociatedObjectKey
+@Target(AnnotationTarget.CLASS)
+@Retention(AnnotationRetention.BINARY)
+actual annotation class ConstructedBy(
+ /**
+ * The 'expect' declaration of an 'object' that implements
+ * [androidx.room.RoomDatabaseConstructor] and is able to instantiate a
+ * [androidx.room.RoomDatabase].
+ */
+ actual val value: KClass<*>
+)
diff --git a/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/compiler/Ksp2Compilation.kt b/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/compiler/Ksp2Compilation.kt
index 4764bed..3d7aaa3 100644
--- a/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/compiler/Ksp2Compilation.kt
+++ b/room/room-compiler-processing-testing/src/main/java/androidx/room/compiler/processing/util/compiler/Ksp2Compilation.kt
@@ -62,7 +62,7 @@
diagnostics = kspDiagnostics.messages,
sourceSets = arguments.sourceSets + generatedSources
)
- val outputResources = workingDir.resolve(RESOURCES_OUT_FOLDER_NAME)
+ val outputResources = workingDir.resolve(RESOURCE_OUT_FOLDER_NAME)
val outputClasspath = listOf(workingDir.resolve(CLASS_OUT_FOLDER_NAME))
val generatedResources =
outputResources
@@ -168,6 +168,5 @@
private const val RESOURCE_OUT_FOLDER_NAME = "ksp-resource-out"
private const val CACHE_FOLDER_NAME = "ksp-cache"
private const val CLASS_OUT_FOLDER_NAME = "class-out"
- private const val RESOURCES_OUT_FOLDER_NAME = "ksp-compiler/resourceOutputDir"
}
}
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspFilerTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspFilerTest.kt
index 1753a3e..ab518b2 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspFilerTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/ksp/KspFilerTest.kt
@@ -169,19 +169,22 @@
@Test
fun writeResource() {
+ val logFileName = "test.log"
+ val serviceFileName = "META-INF/services/com.test.Foo"
runKspTest(sources = emptyList()) { invocation ->
invocation.processingEnv.filer
- .writeResource(filePath = Path("test.log"), originatingElements = emptyList())
+ .writeResource(filePath = Path(logFileName), originatingElements = emptyList())
.bufferedWriter(Charsets.UTF_8)
.use { it.write("Hello!") }
invocation.processingEnv.filer
- .writeResource(
- filePath = Path("META-INF/services/com.test.Foo"),
- originatingElements = emptyList()
- )
+ .writeResource(filePath = Path(serviceFileName), originatingElements = emptyList())
.bufferedWriter(Charsets.UTF_8)
.use { it.write("Not a real service...") }
- invocation.assertCompilationResult { hasNoWarnings() }
+ invocation.assertCompilationResult {
+ generatedResourceFileWithPath(logFileName)
+ generatedResourceFileWithPath(serviceFileName)
+ hasNoWarnings()
+ }
}
}
diff --git a/room/room-paging/src/androidMain/kotlin/androidx/room/paging/util/RoomPagingUtil.android.kt b/room/room-paging/src/androidMain/kotlin/androidx/room/paging/util/RoomPagingUtil.android.kt
index 35104e8..cd0fa67 100644
--- a/room/room-paging/src/androidMain/kotlin/androidx/room/paging/util/RoomPagingUtil.android.kt
+++ b/room/room-paging/src/androidMain/kotlin/androidx/room/paging/util/RoomPagingUtil.android.kt
@@ -31,7 +31,6 @@
*
* Any loaded data or queued loads prior to returning INVALID will be discarded
*/
-@get:Suppress("AcronymName")
@get:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public val INVALID: LoadResult<Any, Any> = LoadResult.Invalid<Any, Any>()
diff --git a/room/room-runtime/api/restricted_current.txt b/room/room-runtime/api/restricted_current.txt
index bb0324f..232e51e 100644
--- a/room/room-runtime/api/restricted_current.txt
+++ b/room/room-runtime/api/restricted_current.txt
@@ -203,20 +203,29 @@
public abstract class RoomDatabase {
ctor public RoomDatabase();
method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void assertNotMainThread();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void assertNotSuspendingTransaction();
method @Deprecated public void beginTransaction();
method @WorkerThread public abstract void clearAllTables();
method public void close();
method public androidx.sqlite.db.SupportSQLiteStatement compileStatement(String sql);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public java.util.List<androidx.room.migration.Migration> createAutoMigrations(java.util.Map<kotlin.reflect.KClass<? extends androidx.room.migration.AutoMigrationSpec>,? extends androidx.room.migration.AutoMigrationSpec> autoMigrationSpecs);
method protected abstract androidx.room.InvalidationTracker createInvalidationTracker();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) protected androidx.room.RoomOpenDelegateMarker createOpenDelegate();
method @Deprecated protected androidx.sqlite.db.SupportSQLiteOpenHelper createOpenHelper(androidx.room.DatabaseConfiguration config);
method @Deprecated public void endTransaction();
+ method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @kotlin.jvm.JvmSuppressWildcards public java.util.List<androidx.room.migration.Migration> getAutoMigrations(java.util.Map<java.lang.Class<? extends androidx.room.migration.AutoMigrationSpec>,androidx.room.migration.AutoMigrationSpec> autoMigrationSpecs);
method public androidx.room.InvalidationTracker getInvalidationTracker();
method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) protected java.util.List<androidx.room.RoomDatabase.Callback>? getMCallbacks();
method @Deprecated @kotlin.jvm.Volatile protected androidx.sqlite.db.SupportSQLiteDatabase? getMDatabase();
method public androidx.sqlite.db.SupportSQLiteOpenHelper getOpenHelper();
method public java.util.concurrent.Executor getQueryExecutor();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public java.util.Set<kotlin.reflect.KClass<? extends androidx.room.migration.AutoMigrationSpec>> getRequiredAutoMigrationSpecClasses();
+ method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public java.util.Set<java.lang.Class<? extends androidx.room.migration.AutoMigrationSpec>> getRequiredAutoMigrationSpecs();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) protected java.util.Map<kotlin.reflect.KClass<? extends java.lang.Object?>,java.util.List<kotlin.reflect.KClass<? extends java.lang.Object?>>> getRequiredTypeConverterClasses();
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) protected java.util.Map<java.lang.Class<? extends java.lang.Object?>,java.util.List<java.lang.Class<? extends java.lang.Object?>>> getRequiredTypeConverters();
method public java.util.concurrent.Executor getTransactionExecutor();
method @Deprecated public <T> T? getTypeConverter(Class<T> klass);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public final <T> T getTypeConverter(kotlin.reflect.KClass<T> klass);
method public boolean inTransaction();
method @CallSuper public void init(androidx.room.DatabaseConfiguration configuration);
method @Deprecated protected void internalInitInvalidationTracker(androidx.sqlite.db.SupportSQLiteDatabase db);
@@ -326,6 +335,31 @@
method public static suspend <R> Object? withTransaction(androidx.room.RoomDatabase, kotlin.jvm.functions.Function1<? super kotlin.coroutines.Continuation<? super R>,? extends java.lang.Object?> block, kotlin.coroutines.Continuation<? super R>);
}
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public abstract class RoomOpenDelegate implements androidx.room.RoomOpenDelegateMarker {
+ ctor public RoomOpenDelegate(int version, String identityHash, String legacyIdentityHash);
+ method public abstract void createAllTables(androidx.sqlite.SQLiteConnection connection);
+ method public abstract void dropAllTables(androidx.sqlite.SQLiteConnection connection);
+ method public final String getIdentityHash();
+ method public final String getLegacyIdentityHash();
+ method public final int getVersion();
+ method public abstract void onCreate(androidx.sqlite.SQLiteConnection connection);
+ method public abstract void onOpen(androidx.sqlite.SQLiteConnection connection);
+ method public abstract void onPostMigrate(androidx.sqlite.SQLiteConnection connection);
+ method public abstract void onPreMigrate(androidx.sqlite.SQLiteConnection connection);
+ method public abstract androidx.room.RoomOpenDelegate.ValidationResult onValidateSchema(androidx.sqlite.SQLiteConnection connection);
+ property public final String identityHash;
+ property public final String legacyIdentityHash;
+ property public final int version;
+ }
+
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static final class RoomOpenDelegate.ValidationResult {
+ ctor public RoomOpenDelegate.ValidationResult(boolean isValid, String? expectedFoundMsg);
+ property public final String? expectedFoundMsg;
+ property public final boolean isValid;
+ field public final String? expectedFoundMsg;
+ field public final boolean isValid;
+ }
+
public interface RoomOpenDelegateMarker {
}
diff --git a/room/room-runtime/src/androidMain/java/androidx/room/paging/LimitOffsetDataSource.java b/room/room-runtime/src/androidMain/java/androidx/room/paging/LimitOffsetDataSource.java
index 3cdc6c0..6e7b6a5 100644
--- a/room/room-runtime/src/androidMain/java/androidx/room/paging/LimitOffsetDataSource.java
+++ b/room/room-runtime/src/androidMain/java/androidx/room/paging/LimitOffsetDataSource.java
@@ -49,7 +49,7 @@
*
*/
@SuppressWarnings("deprecation")
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
public abstract class LimitOffsetDataSource<T> extends androidx.paging.PositionalDataSource<T> {
private final RoomSQLiteQuery mSourceQuery;
private final String mCountQuery;
diff --git a/room/room-runtime/src/androidMain/kotlin/androidx/room/CoroutinesRoom.android.kt b/room/room-runtime/src/androidMain/kotlin/androidx/room/CoroutinesRoom.android.kt
index 415065d..1544630 100644
--- a/room/room-runtime/src/androidMain/kotlin/androidx/room/CoroutinesRoom.android.kt
+++ b/room/room-runtime/src/androidMain/kotlin/androidx/room/CoroutinesRoom.android.kt
@@ -29,7 +29,7 @@
import kotlinx.coroutines.withContext
/** A helper class for supporting Kotlin Coroutines in Room. */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
public class CoroutinesRoom private constructor() {
public companion object {
diff --git a/room/room-runtime/src/androidMain/kotlin/androidx/room/DatabaseConfiguration.android.kt b/room/room-runtime/src/androidMain/kotlin/androidx/room/DatabaseConfiguration.android.kt
index 5329a272..c800d84 100644
--- a/room/room-runtime/src/androidMain/kotlin/androidx/room/DatabaseConfiguration.android.kt
+++ b/room/room-runtime/src/androidMain/kotlin/androidx/room/DatabaseConfiguration.android.kt
@@ -33,7 +33,7 @@
@Suppress("UNUSED_PARAMETER")
actual open class DatabaseConfiguration
@SuppressLint("LambdaLast")
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
constructor(
/* The context to use while connecting to the database. */
@JvmField val context: Context,
@@ -67,7 +67,7 @@
*
* @see [multiInstanceInvalidation]
*/
- @field:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+ @field:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
@JvmField
val multiInstanceInvalidationServiceIntent: Intent?,
@@ -130,7 +130,7 @@
* @param migrationNotRequiredFrom The collection of schema versions from which migrations
* aren't required.
*/
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
@Deprecated("This constructor is deprecated.")
constructor(
context: Context,
@@ -188,7 +188,7 @@
* aren't required.
*/
@OptIn(ExperimentalRoomApi::class)
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
@Deprecated("This constructor is deprecated.")
constructor(
context: Context,
@@ -254,7 +254,7 @@
* @param copyFromFile The pre-packaged database file.
*/
@OptIn(ExperimentalRoomApi::class)
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
@Deprecated("This constructor is deprecated.")
constructor(
context: Context,
@@ -324,7 +324,7 @@
* database file will be copied from.
*/
@OptIn(ExperimentalRoomApi::class)
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
@Deprecated("This constructor is deprecated.")
constructor(
context: Context,
@@ -397,7 +397,7 @@
*/
@OptIn(ExperimentalRoomApi::class)
@SuppressLint("LambdaLast")
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
@Deprecated("This constructor is deprecated.")
constructor(
context: Context,
@@ -472,7 +472,7 @@
*/
@OptIn(ExperimentalRoomApi::class)
@SuppressLint("LambdaLast")
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
@Deprecated("This constructor is deprecated.")
constructor(
context: Context,
@@ -549,7 +549,7 @@
*/
@OptIn(ExperimentalRoomApi::class)
@SuppressLint("LambdaLast")
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
@Deprecated("This constructor is deprecated.")
constructor(
context: Context,
@@ -627,7 +627,7 @@
* @param autoMigrationSpecs The auto migration specs.
*/
@SuppressLint("LambdaLast")
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
@Deprecated("This constructor is deprecated.")
constructor(
context: Context,
@@ -674,7 +674,7 @@
queryCoroutineContext = null
)
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
@Deprecated("This constructor is deprecated.")
constructor(
context: Context,
diff --git a/room/room-runtime/src/androidMain/kotlin/androidx/room/EntityDeletionOrUpdateAdapter.android.kt b/room/room-runtime/src/androidMain/kotlin/androidx/room/EntityDeletionOrUpdateAdapter.android.kt
index 16ecdf8..a1a916e 100644
--- a/room/room-runtime/src/androidMain/kotlin/androidx/room/EntityDeletionOrUpdateAdapter.android.kt
+++ b/room/room-runtime/src/androidMain/kotlin/androidx/room/EntityDeletionOrUpdateAdapter.android.kt
@@ -27,7 +27,7 @@
* @constructor Creates a DeletionOrUpdateAdapter that can delete or update the entity type T on the
* given database.
*/
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
@Deprecated("No longer used by generated code.", ReplaceWith("EntityDeleteOrUpdateAdapter"))
abstract class EntityDeletionOrUpdateAdapter<T>(database: RoomDatabase) :
SharedSQLiteStatement(database) {
diff --git a/room/room-runtime/src/androidMain/kotlin/androidx/room/EntityInsertionAdapter.android.kt b/room/room-runtime/src/androidMain/kotlin/androidx/room/EntityInsertionAdapter.android.kt
index aa1be1a..0774ade 100644
--- a/room/room-runtime/src/androidMain/kotlin/androidx/room/EntityInsertionAdapter.android.kt
+++ b/room/room-runtime/src/androidMain/kotlin/androidx/room/EntityInsertionAdapter.android.kt
@@ -27,7 +27,7 @@
* @constructor Creates an InsertionAdapter that can insert the entity type T into the given
* database.
*/
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
@Deprecated("No longer used by generated code.", ReplaceWith("EntityInsertAdapter"))
abstract class EntityInsertionAdapter<T>(database: RoomDatabase) : SharedSQLiteStatement(database) {
/**
diff --git a/room/room-runtime/src/androidMain/kotlin/androidx/room/EntityUpsertionAdapter.android.kt b/room/room-runtime/src/androidMain/kotlin/androidx/room/EntityUpsertionAdapter.android.kt
index bc8c8f2..9d30a08 100644
--- a/room/room-runtime/src/androidMain/kotlin/androidx/room/EntityUpsertionAdapter.android.kt
+++ b/room/room-runtime/src/androidMain/kotlin/androidx/room/EntityUpsertionAdapter.android.kt
@@ -42,7 +42,7 @@
* using the given insertionAdapter to perform insertion and updateAdapter to perform update when
* the insertion fails
*/
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
@Deprecated("No longer used by generated code.", ReplaceWith("EntityUpsertAdapter"))
class EntityUpsertionAdapter<T>(
@Suppress("DEPRECATION") private val insertionAdapter: EntityInsertionAdapter<T>,
diff --git a/room/room-runtime/src/androidMain/kotlin/androidx/room/InvalidationTracker.android.kt b/room/room-runtime/src/androidMain/kotlin/androidx/room/InvalidationTracker.android.kt
index 9b5e5d8..0454358 100644
--- a/room/room-runtime/src/androidMain/kotlin/androidx/room/InvalidationTracker.android.kt
+++ b/room/room-runtime/src/androidMain/kotlin/androidx/room/InvalidationTracker.android.kt
@@ -47,7 +47,7 @@
* new value.
*/
actual open class InvalidationTracker
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
actual constructor(
internal val database: RoomDatabase,
private val shadowTablesMap: Map<String, String>,
@@ -91,7 +91,7 @@
private val trackerLock = Any()
@Deprecated("No longer called by generated implementation")
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
constructor(
database: RoomDatabase,
vararg tableNames: String
@@ -290,7 +290,7 @@
* @param observer The observer to which InvalidationTracker will keep a weak reference.
*/
@WorkerThread
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
open fun addWeakObserver(observer: Observer) {
addObserver(WeakObserver(this, observer))
}
@@ -340,7 +340,7 @@
* @see refresh
*/
@WorkerThread
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
open fun refreshVersionsSync(): Unit = runBlocking {
implementation.refreshInvalidation(emptyArray(), onRefreshScheduled, onRefreshCompleted)
}
@@ -381,7 +381,7 @@
* @return A new LiveData that computes the given function when the given list of tables
* invalidates.
*/
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
@Deprecated(
message = "Replaced with overload that takes 'inTransaction 'parameter.",
replaceWith = ReplaceWith("createLiveData(tableNames, false, computeFunction")
@@ -407,7 +407,7 @@
* @return A new LiveData that computes the given function when the given list of tables
* invalidates.
*/
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
open fun <T> createLiveData(
tableNames: Array<out String>,
inTransaction: Boolean,
@@ -433,7 +433,7 @@
* @return A new LiveData that computes the given function when the given list of tables
* invalidates.
*/
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
fun <T> createLiveData(
tableNames: Array<out String>,
inTransaction: Boolean,
diff --git a/room/room-runtime/src/androidMain/kotlin/androidx/room/RoomDatabase.android.kt b/room/room-runtime/src/androidMain/kotlin/androidx/room/RoomDatabase.android.kt
index c72e4e6..1152eef 100644
--- a/room/room-runtime/src/androidMain/kotlin/androidx/room/RoomDatabase.android.kt
+++ b/room/room-runtime/src/androidMain/kotlin/androidx/room/RoomDatabase.android.kt
@@ -154,7 +154,7 @@
message = "This property is always null and will be removed in a future version.",
level = DeprecationLevel.ERROR
)
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
protected var mCallbacks: List<Callback>? = null
private var autoCloser: AutoCloser? = null
@@ -193,7 +193,7 @@
* @param T The type of the expected Type Converter subclass.
* @return An instance of T.
*/
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
@Suppress("UNCHECKED_CAST")
actual fun <T : Any> getTypeConverter(klass: KClass<T>): T {
return typeConverters[klass] as T
@@ -320,7 +320,7 @@
* @return A list of migration instances each of which is a generated autoMigration
*/
@Deprecated("No longer implemented by generated")
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
@JvmSuppressWildcards // Suppress wildcards due to generated Java code
open fun getAutoMigrations(
autoMigrationSpecs: Map<Class<out AutoMigrationSpec>, AutoMigrationSpec>
@@ -328,7 +328,7 @@
return emptyList()
}
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
actual open fun createAutoMigrations(
autoMigrationSpecs: Map<KClass<out AutoMigrationSpec>, AutoMigrationSpec>
): List<Migration> {
@@ -386,7 +386,7 @@
* @return A new delegate to be used while opening the database
* @throws NotImplementedError by default
*/
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
protected actual open fun createOpenDelegate(): RoomOpenDelegateMarker {
throw NotImplementedError()
}
@@ -424,7 +424,7 @@
*
* @return Creates a map that will include all required type converters for this database.
*/
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
protected open fun getRequiredTypeConverters(): Map<Class<*>, List<Class<*>>> {
return emptyMap()
}
@@ -439,7 +439,7 @@
*
* @return A map that will include all required type converters for this database.
*/
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
protected actual open fun getRequiredTypeConverterClasses(): Map<KClass<*>, List<KClass<*>>> {
// For backwards compatibility when newer runtime is used with older generated code,
// call the Java version this function.
@@ -460,12 +460,12 @@
* @return Creates a set that will include all required auto migration specs for this database.
*/
@Deprecated("No longer implemented by generated")
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
open fun getRequiredAutoMigrationSpecs(): Set<Class<out AutoMigrationSpec>> {
return emptySet()
}
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
actual open fun getRequiredAutoMigrationSpecClasses(): Set<KClass<out AutoMigrationSpec>> {
// For backwards compatibility when newer runtime is used with older generated code,
// call the Java version of this function.
@@ -565,7 +565,7 @@
}
/** Asserts that we are not on a suspending transaction. */
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) // used in generated code
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
open fun assertNotSuspendingTransaction() {
check(!inCompatibilityMode() || inTransaction() || suspendingTransactionId.get() == null) {
"Cannot access database on a different coroutine" +
@@ -755,7 +755,7 @@
*
* @param connection The database connection.
*/
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
protected actual fun internalInitInvalidationTracker(connection: SQLiteConnection) {
invalidationTracker.internalInit(connection)
}
@@ -1931,7 +1931,8 @@
/**
* Unfortunately, we cannot read this value so we are only setting it to the SQLite default.
*/
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) const val MAX_BIND_PARAMETER_CNT = 999
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
+ const val MAX_BIND_PARAMETER_CNT = 999
}
}
diff --git a/room/room-runtime/src/androidMain/kotlin/androidx/room/RoomOpenHelper.android.kt b/room/room-runtime/src/androidMain/kotlin/androidx/room/RoomOpenHelper.android.kt
index 4ba4223..af2f515 100644
--- a/room/room-runtime/src/androidMain/kotlin/androidx/room/RoomOpenHelper.android.kt
+++ b/room/room-runtime/src/androidMain/kotlin/androidx/room/RoomOpenHelper.android.kt
@@ -25,7 +25,7 @@
/** An open helper that holds a reference to the configuration until the database is opened. */
@Suppress("DEPRECATION") // Due to usage of RoomOpenHelper.Delegate
@Deprecated("Replaced by RoomConnectionManager and no longer used in generated code.")
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
open class RoomOpenHelper(
configuration: DatabaseConfiguration,
delegate: Delegate,
@@ -180,7 +180,7 @@
}
@Deprecated("Replaced by OpenDelegate and no longer used in generated code.")
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
abstract class Delegate(@JvmField val version: Int) {
abstract fun dropAllTables(db: SupportSQLiteDatabase)
@@ -227,7 +227,7 @@
}
@Deprecated("Replaced by OpenDelegate.ValidationResult and no longer used in generated code.")
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
open class ValidationResult(
@JvmField val isValid: Boolean,
@JvmField val expectedFoundMsg: String?
diff --git a/room/room-runtime/src/androidMain/kotlin/androidx/room/RoomSQLiteQuery.android.kt b/room/room-runtime/src/androidMain/kotlin/androidx/room/RoomSQLiteQuery.android.kt
index e88a1cd..f8b7203 100644
--- a/room/room-runtime/src/androidMain/kotlin/androidx/room/RoomSQLiteQuery.android.kt
+++ b/room/room-runtime/src/androidMain/kotlin/androidx/room/RoomSQLiteQuery.android.kt
@@ -31,7 +31,7 @@
* Because it is relatively a big object, they are pooled and must be released after each use.
*/
@SuppressLint("WrongConstant")
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
class RoomSQLiteQuery private constructor(@field:VisibleForTesting val capacity: Int) :
SupportSQLiteQuery, SupportSQLiteProgram {
@Volatile private var query: String? = null
diff --git a/room/room-runtime/src/androidMain/kotlin/androidx/room/SharedSQLiteStatement.android.kt b/room/room-runtime/src/androidMain/kotlin/androidx/room/SharedSQLiteStatement.android.kt
index df05367..052c3aa 100644
--- a/room/room-runtime/src/androidMain/kotlin/androidx/room/SharedSQLiteStatement.android.kt
+++ b/room/room-runtime/src/androidMain/kotlin/androidx/room/SharedSQLiteStatement.android.kt
@@ -31,7 +31,7 @@
* @constructor Creates an SQLite prepared statement that can be re-used across threads. If it is in
* use, it automatically creates a new one.
*/
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
abstract class SharedSQLiteStatement(private val database: RoomDatabase) {
private val lock = AtomicBoolean(false)
diff --git a/room/room-runtime/src/androidMain/kotlin/androidx/room/support/QueryInterceptorDatabase.android.kt b/room/room-runtime/src/androidMain/kotlin/androidx/room/support/QueryInterceptorDatabase.android.kt
index 58dce9f..01dc16b 100644
--- a/room/room-runtime/src/androidMain/kotlin/androidx/room/support/QueryInterceptorDatabase.android.kt
+++ b/room/room-runtime/src/androidMain/kotlin/androidx/room/support/QueryInterceptorDatabase.android.kt
@@ -111,17 +111,11 @@
return delegate.query(query)
}
- // Suppress warning about `SQL` in execSQL not being camel case. This is an override function
- // and it can't be renamed.
- @Suppress("AcronymName")
override fun execSQL(sql: String) {
queryCallbackScope.launch { queryCallback.onQuery(sql, emptyList()) }
delegate.execSQL(sql)
}
- // Suppress warning about `SQL` in execSQL not being camel case. This is an override function
- // and it can't be renamed.
- @Suppress("AcronymName")
override fun execSQL(sql: String, bindArgs: Array<out Any?>) {
val argsCopy = bindArgs.toList()
queryCallbackScope.launch { queryCallback.onQuery(sql, argsCopy) }
diff --git a/room/room-runtime/src/androidMain/kotlin/androidx/room/util/CursorUtil.android.kt b/room/room-runtime/src/androidMain/kotlin/androidx/room/util/CursorUtil.android.kt
index 8bb4ef8..34c07eb 100644
--- a/room/room-runtime/src/androidMain/kotlin/androidx/room/util/CursorUtil.android.kt
+++ b/room/room-runtime/src/androidMain/kotlin/androidx/room/util/CursorUtil.android.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
@file:JvmName("CursorUtil")
-@file:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@file:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
package androidx.room.util
diff --git a/room/room-runtime/src/androidMain/kotlin/androidx/room/util/DBUtil.android.kt b/room/room-runtime/src/androidMain/kotlin/androidx/room/util/DBUtil.android.kt
index 7faeb6a..ec520d9 100644
--- a/room/room-runtime/src/androidMain/kotlin/androidx/room/util/DBUtil.android.kt
+++ b/room/room-runtime/src/androidMain/kotlin/androidx/room/util/DBUtil.android.kt
@@ -42,7 +42,7 @@
import kotlinx.coroutines.withContext
/** Performs a database operation. */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
actual suspend fun <R> performSuspending(
db: RoomDatabase,
isReadOnly: Boolean,
@@ -57,7 +57,7 @@
}
/** Blocking version of [performSuspending] */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
fun <R> performBlocking(
db: RoomDatabase,
isReadOnly: Boolean,
@@ -80,7 +80,7 @@
* This function should only be invoked from generated code and is needed to support `@Transaction`
* delegates in Java and Kotlin. It is preferred to use the other 'perform' functions.
*/
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
actual suspend fun <R> performInTransactionSuspending(db: RoomDatabase, block: suspend () -> R): R =
if (db.inCompatibilityMode()) {
db.withTransactionContext {
@@ -138,7 +138,7 @@
* @return Result of the query.
*/
@Deprecated("This is only used in the generated code and shouldn't be called directly.")
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
fun query(db: RoomDatabase, sqLiteQuery: SupportSQLiteQuery, maybeCopy: Boolean): Cursor {
return query(db, sqLiteQuery, maybeCopy, null)
}
@@ -156,7 +156,7 @@
* @param signal The cancellation signal to be attached to the query.
* @return Result of the query.
*/
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
fun query(
db: RoomDatabase,
sqLiteQuery: SupportSQLiteQuery,
@@ -188,13 +188,13 @@
* @param db The database.
*/
@Deprecated("Replaced by dropFtsSyncTriggers(connection: SQLiteConnection)")
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
fun dropFtsSyncTriggers(db: SupportSQLiteDatabase) {
dropFtsSyncTriggers(SupportSQLiteConnection(db))
}
/** Checks for foreign key violations by executing a PRAGMA foreign_key_check. */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
fun foreignKeyCheck(db: SupportSQLiteDatabase, tableName: String) {
foreignKeyCheck(SupportSQLiteConnection(db), tableName)
}
@@ -208,7 +208,7 @@
* missing permissions.
* @see [User Version Number](https://www.sqlite.org/fileformat.html.user_version_number).
*/
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
@Throws(IOException::class)
fun readVersion(databaseFile: File): Int {
FileInputStream(databaseFile).channel.use { input ->
@@ -230,12 +230,12 @@
* @return A new instance of CancellationSignal.
*/
@Deprecated("Use constructor", ReplaceWith("CancellationSignal()", "android.os.CancellationSignal"))
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
fun createCancellationSignal(): CancellationSignal {
return CancellationSignal()
}
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
fun toSQLiteConnection(db: SupportSQLiteDatabase): SQLiteConnection {
return SupportSQLiteConnection(db)
}
diff --git a/room/room-runtime/src/androidMain/kotlin/androidx/room/util/FileUtil.android.kt b/room/room-runtime/src/androidMain/kotlin/androidx/room/util/FileUtil.android.kt
index 89ca2d8..2caff1f 100644
--- a/room/room-runtime/src/androidMain/kotlin/androidx/room/util/FileUtil.android.kt
+++ b/room/room-runtime/src/androidMain/kotlin/androidx/room/util/FileUtil.android.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
@file:JvmName("FileUtil")
-@file:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@file:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
package androidx.room.util
diff --git a/room/room-runtime/src/androidMain/kotlin/androidx/room/util/FtsTableInfo.android.kt b/room/room-runtime/src/androidMain/kotlin/androidx/room/util/FtsTableInfo.android.kt
index 5f12aa9..60a9f04 100644
--- a/room/room-runtime/src/androidMain/kotlin/androidx/room/util/FtsTableInfo.android.kt
+++ b/room/room-runtime/src/androidMain/kotlin/androidx/room/util/FtsTableInfo.android.kt
@@ -21,7 +21,7 @@
import androidx.sqlite.db.SupportSQLiteDatabase
/** A data class that holds the information about an FTS table. */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
actual class FtsTableInfo(
/** The table name */
@JvmField actual val name: String,
diff --git a/room/room-runtime/src/androidMain/kotlin/androidx/room/util/RelationUtil.android.kt b/room/room-runtime/src/androidMain/kotlin/androidx/room/util/RelationUtil.android.kt
index 1ea7240..76af3114 100644
--- a/room/room-runtime/src/androidMain/kotlin/androidx/room/util/RelationUtil.android.kt
+++ b/room/room-runtime/src/androidMain/kotlin/androidx/room/util/RelationUtil.android.kt
@@ -31,7 +31,7 @@
* @param isRelationCollection - True if [V] is a [Collection] which means it is non null.
* @param fetchBlock - A lambda for calling the generated _fetchRelationship function.
*/
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
fun <K : Any, V> recursiveFetchHashMap(
map: HashMap<K, V>,
isRelationCollection: Boolean,
@@ -71,7 +71,7 @@
}
/** Same as [recursiveFetchHashMap] but for [ArrayMap]. */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
fun <K : Any, V> recursiveFetchArrayMap(
map: ArrayMap<K, V>,
isRelationCollection: Boolean,
diff --git a/room/room-runtime/src/androidMain/kotlin/androidx/room/util/TableInfo.android.kt b/room/room-runtime/src/androidMain/kotlin/androidx/room/util/TableInfo.android.kt
index bc9b664..fdf5756 100644
--- a/room/room-runtime/src/androidMain/kotlin/androidx/room/util/TableInfo.android.kt
+++ b/room/room-runtime/src/androidMain/kotlin/androidx/room/util/TableInfo.android.kt
@@ -31,7 +31,7 @@
*
* Even though SQLite column names are case insensitive, this class uses case sensitive matching.
*/
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
actual class TableInfo
actual constructor(
/** The table name. */
@@ -102,7 +102,7 @@
}
/** Holds the information about a database column. */
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
actual class Column
actual constructor(
/** The column name. */
@@ -153,7 +153,7 @@
}
/** Holds the information about an SQLite foreign key */
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
actual class ForeignKey
actual constructor(
@JvmField actual val referenceTable: String,
@@ -170,7 +170,7 @@
}
/** Holds the information about an SQLite index */
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
actual class Index
actual constructor(
@JvmField actual val name: String,
diff --git a/room/room-runtime/src/androidMain/kotlin/androidx/room/util/ViewInfo.android.kt b/room/room-runtime/src/androidMain/kotlin/androidx/room/util/ViewInfo.android.kt
index 41fa707..b5e1448 100644
--- a/room/room-runtime/src/androidMain/kotlin/androidx/room/util/ViewInfo.android.kt
+++ b/room/room-runtime/src/androidMain/kotlin/androidx/room/util/ViewInfo.android.kt
@@ -27,7 +27,7 @@
*
* Even though SQLite column names are case insensitive, this class uses case sensitive matching.
*/
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
actual class ViewInfo
actual constructor(
/** The view name */
diff --git a/room/room-runtime/src/commonMain/kotlin/androidx/room/EntityDeleteOrUpdateAdapter.kt b/room/room-runtime/src/commonMain/kotlin/androidx/room/EntityDeleteOrUpdateAdapter.kt
index 871f7d5..01c0402 100644
--- a/room/room-runtime/src/commonMain/kotlin/androidx/room/EntityDeleteOrUpdateAdapter.kt
+++ b/room/room-runtime/src/commonMain/kotlin/androidx/room/EntityDeleteOrUpdateAdapter.kt
@@ -29,7 +29,7 @@
* @constructor Creates a DeletionOrUpdateAdapter that can delete or update the entity type T on the
* given database.
*/
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
abstract class EntityDeleteOrUpdateAdapter<T> {
/**
* Create the deletion or update query
diff --git a/room/room-runtime/src/commonMain/kotlin/androidx/room/EntityInsertAdapter.kt b/room/room-runtime/src/commonMain/kotlin/androidx/room/EntityInsertAdapter.kt
index e9d7bfe..743ee70 100644
--- a/room/room-runtime/src/commonMain/kotlin/androidx/room/EntityInsertAdapter.kt
+++ b/room/room-runtime/src/commonMain/kotlin/androidx/room/EntityInsertAdapter.kt
@@ -29,7 +29,7 @@
* @constructor Creates an InsertionAdapter that can insert the entity type T into the given
* database.
*/
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
abstract class EntityInsertAdapter<T> {
/**
* Create the query.
diff --git a/room/room-runtime/src/commonMain/kotlin/androidx/room/EntityUpsertAdapter.kt b/room/room-runtime/src/commonMain/kotlin/androidx/room/EntityUpsertAdapter.kt
index 3bbb3a9..9dd5110 100644
--- a/room/room-runtime/src/commonMain/kotlin/androidx/room/EntityUpsertAdapter.kt
+++ b/room/room-runtime/src/commonMain/kotlin/androidx/room/EntityUpsertAdapter.kt
@@ -29,7 +29,7 @@
* using the given insertionAdapter to perform insertion and updateAdapter to perform update when
* the insertion fails
*/
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
class EntityUpsertAdapter<T>(
private val entityInsertAdapter: EntityInsertAdapter<T>,
private val updateAdapter: EntityDeleteOrUpdateAdapter<T>
diff --git a/room/room-runtime/src/commonMain/kotlin/androidx/room/InvalidationTracker.kt b/room/room-runtime/src/commonMain/kotlin/androidx/room/InvalidationTracker.kt
index 5730b60..f958350 100644
--- a/room/room-runtime/src/commonMain/kotlin/androidx/room/InvalidationTracker.kt
+++ b/room/room-runtime/src/commonMain/kotlin/androidx/room/InvalidationTracker.kt
@@ -46,7 +46,7 @@
* created from, then such table is considered 'invalidated' and the [Flow] will emit a new value.
*/
expect class InvalidationTracker
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
constructor(
database: RoomDatabase,
shadowTablesMap: Map<String, String>,
diff --git a/room/room-runtime/src/commonMain/kotlin/androidx/room/RoomDatabase.kt b/room/room-runtime/src/commonMain/kotlin/androidx/room/RoomDatabase.kt
index 1dbaafd..ae3a382 100644
--- a/room/room-runtime/src/commonMain/kotlin/androidx/room/RoomDatabase.kt
+++ b/room/room-runtime/src/commonMain/kotlin/androidx/room/RoomDatabase.kt
@@ -89,7 +89,7 @@
*
* @return A new delegate to be used while opening the database
*/
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
protected open fun createOpenDelegate(): RoomOpenDelegateMarker
/**
@@ -113,7 +113,7 @@
* @return Creates a set that will include the classes of all required auto migration specs for
* this database.
*/
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
open fun getRequiredAutoMigrationSpecClasses(): Set<KClass<out AutoMigrationSpec>>
/**
@@ -125,7 +125,7 @@
* @param autoMigrationSpecs the provided specs needed by certain migrations.
* @return A list of migration instances each of which is a generated 'auto migration'.
*/
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
open fun createAutoMigrations(
autoMigrationSpecs: Map<KClass<out AutoMigrationSpec>, AutoMigrationSpec>
): List<Migration>
@@ -139,7 +139,8 @@
* @param T The type of the expected Type Converter subclass.
* @return An instance of T.
*/
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) fun <T : Any> getTypeConverter(klass: KClass<T>): T
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
+ fun <T : Any> getTypeConverter(klass: KClass<T>): T
/**
* Adds a provided type converter to be used in the database DAOs.
@@ -159,7 +160,7 @@
*
* @return A map that will include all required type converters for this database.
*/
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
protected open fun getRequiredTypeConverterClasses(): Map<KClass<*>, List<KClass<*>>>
/** Property delegate of [getRequiredTypeConverterClasses] for common ext functionality. */
@@ -171,7 +172,7 @@
*
* @param connection The database connection.
*/
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
protected fun internalInitInvalidationTracker(connection: SQLiteConnection)
/**
diff --git a/room/room-runtime/src/commonMain/kotlin/androidx/room/RoomOpenDelegate.kt b/room/room-runtime/src/commonMain/kotlin/androidx/room/RoomOpenDelegate.kt
index 9c3d501..9fd2bf6 100644
--- a/room/room-runtime/src/commonMain/kotlin/androidx/room/RoomOpenDelegate.kt
+++ b/room/room-runtime/src/commonMain/kotlin/androidx/room/RoomOpenDelegate.kt
@@ -27,9 +27,9 @@
* implementation of a RoomDatabase with runtime.
*
* @see [RoomDatabase.createOpenDelegate]
- * @see [RoomConnectionManager.openDelegate]
+ * @see [BaseRoomConnectionManager.openDelegate]
*/
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
abstract class RoomOpenDelegate(
val version: Int,
val identityHash: String,
@@ -49,7 +49,7 @@
abstract fun dropAllTables(connection: SQLiteConnection)
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
class ValidationResult(@JvmField val isValid: Boolean, @JvmField val expectedFoundMsg: String?)
}
diff --git a/room/room-runtime/src/commonMain/kotlin/androidx/room/Transactor.kt b/room/room-runtime/src/commonMain/kotlin/androidx/room/Transactor.kt
index 105f220..7ba38d0 100644
--- a/room/room-runtime/src/commonMain/kotlin/androidx/room/Transactor.kt
+++ b/room/room-runtime/src/commonMain/kotlin/androidx/room/Transactor.kt
@@ -42,7 +42,6 @@
}
/** Executes a single SQL statement that returns no values. */
-@Suppress("AcronymName") // SQL is a known term and should remain capitalized
suspend fun PooledConnection.execSQL(sql: String) {
usePrepared(sql) { it.step() }
}
@@ -78,7 +77,6 @@
*
* @see Transactor.withTransaction
*/
- @Suppress("AcronymName") // SQL is a known term and should remain capitalized
enum class SQLiteTransactionType {
/**
* The transaction mode that does not start the actual transaction until the database is
diff --git a/room/room-runtime/src/commonMain/kotlin/androidx/room/coroutines/FlowBuilder.kt b/room/room-runtime/src/commonMain/kotlin/androidx/room/coroutines/FlowBuilder.kt
index 3fdf0d1..526454f 100644
--- a/room/room-runtime/src/commonMain/kotlin/androidx/room/coroutines/FlowBuilder.kt
+++ b/room/room-runtime/src/commonMain/kotlin/androidx/room/coroutines/FlowBuilder.kt
@@ -27,7 +27,7 @@
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.map
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
fun <R> createFlow(
db: RoomDatabase,
inTransaction: Boolean,
diff --git a/room/room-runtime/src/commonMain/kotlin/androidx/room/util/ByteArrayWrapper.kt b/room/room-runtime/src/commonMain/kotlin/androidx/room/util/ByteArrayWrapper.kt
index cdb18cb..f4a10bd 100644
--- a/room/room-runtime/src/commonMain/kotlin/androidx/room/util/ByteArrayWrapper.kt
+++ b/room/room-runtime/src/commonMain/kotlin/androidx/room/util/ByteArrayWrapper.kt
@@ -19,7 +19,7 @@
import kotlin.jvm.JvmField
/** A [ByteArray] wrapper that implements equals and hashCode to be used as a Map key.typ */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
class ByteArrayWrapper(@JvmField val array: ByteArray) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
diff --git a/room/room-runtime/src/commonMain/kotlin/androidx/room/util/ConnectionUtil.kt b/room/room-runtime/src/commonMain/kotlin/androidx/room/util/ConnectionUtil.kt
index e9f7bc5..7a3512f 100644
--- a/room/room-runtime/src/commonMain/kotlin/androidx/room/util/ConnectionUtil.kt
+++ b/room/room-runtime/src/commonMain/kotlin/androidx/room/util/ConnectionUtil.kt
@@ -30,7 +30,7 @@
* See (official SQLite documentation)[http://www.sqlite.org/lang_corefunc.html#last_insert_rowid]
* for details.
*/
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
fun getLastInsertedRowId(connection: SQLiteConnection): Long {
if (getTotalChangedRows(connection) == 0) {
return -1
@@ -48,7 +48,7 @@
* See the (official SQLite documentation)[http://www.sqlite.org/lang_corefunc.html#changes] for
* details.
*/
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
fun getTotalChangedRows(connection: SQLiteConnection): Int {
return connection.prepare("SELECT changes()").use {
it.step()
diff --git a/room/room-runtime/src/commonMain/kotlin/androidx/room/util/DBUtil.kt b/room/room-runtime/src/commonMain/kotlin/androidx/room/util/DBUtil.kt
index b15c5f3..2c031c6 100644
--- a/room/room-runtime/src/commonMain/kotlin/androidx/room/util/DBUtil.kt
+++ b/room/room-runtime/src/commonMain/kotlin/androidx/room/util/DBUtil.kt
@@ -32,7 +32,7 @@
import kotlin.jvm.JvmName
/** Performs a database operation. */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
expect suspend fun <R> performSuspending(
db: RoomDatabase,
isReadOnly: Boolean,
@@ -82,7 +82,7 @@
* delegates in Java and Kotlin. It is preferred to use the other 'perform' functions.
*/
// TODO(b/309996304): Replace with proper suspending transaction API for common.
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
expect suspend fun <R> performInTransactionSuspending(db: RoomDatabase, block: suspend () -> R): R
/**
@@ -93,7 +93,7 @@
*
* @param connection The database connection.
*/
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
fun dropFtsSyncTriggers(connection: SQLiteConnection) {
val existingTriggers = buildList {
connection.prepare("SELECT name FROM sqlite_master WHERE type = 'trigger'").use {
@@ -111,7 +111,7 @@
}
/** Checks for foreign key violations by executing a PRAGMA foreign_key_check. */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
fun foreignKeyCheck(db: SQLiteConnection, tableName: String) {
db.prepare("PRAGMA foreign_key_check(`$tableName`)").use { stmt ->
if (stmt.step()) {
diff --git a/room/room-runtime/src/commonMain/kotlin/androidx/room/util/FtsTableInfo.kt b/room/room-runtime/src/commonMain/kotlin/androidx/room/util/FtsTableInfo.kt
index f6fffde..dcec8e2 100644
--- a/room/room-runtime/src/commonMain/kotlin/androidx/room/util/FtsTableInfo.kt
+++ b/room/room-runtime/src/commonMain/kotlin/androidx/room/util/FtsTableInfo.kt
@@ -21,7 +21,7 @@
import kotlin.jvm.JvmStatic
/** A data class that holds the information about an FTS table. */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
expect class FtsTableInfo {
/** The table name */
val name: String
diff --git a/room/room-runtime/src/commonMain/kotlin/androidx/room/util/RelationUtil.kt b/room/room-runtime/src/commonMain/kotlin/androidx/room/util/RelationUtil.kt
index 828c589..1b85296 100644
--- a/room/room-runtime/src/commonMain/kotlin/androidx/room/util/RelationUtil.kt
+++ b/room/room-runtime/src/commonMain/kotlin/androidx/room/util/RelationUtil.kt
@@ -32,7 +32,7 @@
* @param isRelationCollection - True if [V] is a [Collection] which means it is non null.
* @param fetchBlock - A lambda for calling the generated _fetchRelationship function.
*/
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
fun <K : Any, V> recursiveFetchMap(
map: MutableMap<K, V>,
isRelationCollection: Boolean,
@@ -72,7 +72,7 @@
}
/** Same as [recursiveFetchMap] but for [LongSparseArray]. */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
fun <V> recursiveFetchLongSparseArray(
map: LongSparseArray<V>,
isRelationCollection: Boolean,
diff --git a/room/room-runtime/src/commonMain/kotlin/androidx/room/util/StatementUtil.kt b/room/room-runtime/src/commonMain/kotlin/androidx/room/util/StatementUtil.kt
index 7272ad6..211017e 100644
--- a/room/room-runtime/src/commonMain/kotlin/androidx/room/util/StatementUtil.kt
+++ b/room/room-runtime/src/commonMain/kotlin/androidx/room/util/StatementUtil.kt
@@ -28,7 +28,7 @@
* Returns the zero-based index for the given column name, or throws [IllegalArgumentException] if
* the column doesn't exist.
*/
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
fun getColumnIndexOrThrow(stmt: SQLiteStatement, name: String): Int {
val index: Int = stmt.columnIndexOf(name)
if (index >= 0) {
@@ -56,7 +56,7 @@
}
/** Returns the zero-based index for the given column name, or -1 if the column doesn't exist. */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
fun getColumnIndex(stmt: SQLiteStatement, name: String): Int {
return stmt.columnIndexOf(name)
}
@@ -76,7 +76,7 @@
* @param mapping the cursor column indices of the columns at `columnNames`.
* @return the wrapped Cursor.
*/
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
fun wrapMappedColumns(
statement: SQLiteStatement,
columnNames: Array<String>,
diff --git a/room/room-runtime/src/commonMain/kotlin/androidx/room/util/StringUtil.kt b/room/room-runtime/src/commonMain/kotlin/androidx/room/util/StringUtil.kt
index cd65582..fedd164 100644
--- a/room/room-runtime/src/commonMain/kotlin/androidx/room/util/StringUtil.kt
+++ b/room/room-runtime/src/commonMain/kotlin/androidx/room/util/StringUtil.kt
@@ -15,7 +15,7 @@
*/
@file:JvmName("StringUtil")
-@file:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@file:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
package androidx.room.util
diff --git a/room/room-runtime/src/commonMain/kotlin/androidx/room/util/TableInfo.kt b/room/room-runtime/src/commonMain/kotlin/androidx/room/util/TableInfo.kt
index 2ac97ab..427f035 100644
--- a/room/room-runtime/src/commonMain/kotlin/androidx/room/util/TableInfo.kt
+++ b/room/room-runtime/src/commonMain/kotlin/androidx/room/util/TableInfo.kt
@@ -29,7 +29,7 @@
*
* Even though SQLite column names are case insensitive, this class uses case sensitive matching.
*/
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
expect class TableInfo(
name: String,
columns: Map<String, Column>,
@@ -75,7 +75,7 @@
}
/** Holds the information about a database column. */
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
class Column(
name: String,
type: String,
@@ -117,7 +117,7 @@
}
/** Holds the information about an SQLite foreign key */
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
class ForeignKey(
referenceTable: String,
onDelete: String,
@@ -139,7 +139,7 @@
}
/** Holds the information about an SQLite index */
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
class Index(name: String, unique: Boolean, columns: List<String>, orders: List<String>) {
val name: String
diff --git a/room/room-runtime/src/commonMain/kotlin/androidx/room/util/ViewInfo.kt b/room/room-runtime/src/commonMain/kotlin/androidx/room/util/ViewInfo.kt
index 0f80341..a0486cd 100644
--- a/room/room-runtime/src/commonMain/kotlin/androidx/room/util/ViewInfo.kt
+++ b/room/room-runtime/src/commonMain/kotlin/androidx/room/util/ViewInfo.kt
@@ -26,7 +26,7 @@
*
* Even though SQLite column names are case insensitive, this class uses case sensitive matching.
*/
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
expect class ViewInfo(name: String, sql: String?) {
/** The view name */
val name: String
diff --git a/room/room-runtime/src/jvmAndroidMain/kotlin/androidx/room/util/KClassUtil.jvmAndroid.kt b/room/room-runtime/src/jvmAndroidMain/kotlin/androidx/room/util/KClassUtil.jvmAndroid.kt
index 5f7b12e..ec3aeaf 100644
--- a/room/room-runtime/src/jvmAndroidMain/kotlin/androidx/room/util/KClassUtil.jvmAndroid.kt
+++ b/room/room-runtime/src/jvmAndroidMain/kotlin/androidx/room/util/KClassUtil.jvmAndroid.kt
@@ -15,7 +15,7 @@
*/
@file:JvmName("KClassUtil")
-@file:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@file:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
package androidx.room.util
@@ -25,7 +25,7 @@
* Finds and instantiates via reflection the implementation class generated by Room of an
* `@Database` annotated type.
*/
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
fun <T, C> findAndInstantiateDatabaseImpl(klass: Class<C>, suffix: String = "_Impl"): T {
val fullPackage: String = klass.getPackage()?.name ?: ""
val name: String = klass.canonicalName!!
diff --git a/room/room-runtime/src/jvmAndroidMain/kotlin/androidx/room/util/UUIDUtil.jvmAndroid.kt b/room/room-runtime/src/jvmAndroidMain/kotlin/androidx/room/util/UUIDUtil.jvmAndroid.kt
index 8191d4e..3040ce9 100644
--- a/room/room-runtime/src/jvmAndroidMain/kotlin/androidx/room/util/UUIDUtil.jvmAndroid.kt
+++ b/room/room-runtime/src/jvmAndroidMain/kotlin/androidx/room/util/UUIDUtil.jvmAndroid.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
@file:JvmName("UUIDUtil")
-@file:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@file:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
package androidx.room.util
diff --git a/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/InvalidationTracker.jvmNative.kt b/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/InvalidationTracker.jvmNative.kt
index 64020fb..cffa497 100644
--- a/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/InvalidationTracker.jvmNative.kt
+++ b/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/InvalidationTracker.jvmNative.kt
@@ -31,7 +31,7 @@
* created from, then such table is considered 'invalidated' and the [Flow] will emit a new value.
*/
actual class InvalidationTracker
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
actual constructor(
private val database: RoomDatabase,
shadowTablesMap: Map<String, String>,
diff --git a/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/RoomDatabase.jvmNative.kt b/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/RoomDatabase.jvmNative.kt
index ba4a415..5d56a72 100644
--- a/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/RoomDatabase.jvmNative.kt
+++ b/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/RoomDatabase.jvmNative.kt
@@ -117,7 +117,7 @@
* @return A new delegate to be used while opening the database
* @throws NotImplementedError by default
*/
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
protected actual open fun createOpenDelegate(): RoomOpenDelegateMarker {
throw NotImplementedError()
}
@@ -147,7 +147,7 @@
* this database.
* @throws NotImplementedError by default
*/
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
actual open fun getRequiredAutoMigrationSpecClasses(): Set<KClass<out AutoMigrationSpec>> {
throw NotImplementedError()
}
@@ -162,7 +162,7 @@
* @return A list of migration instances each of which is a generated 'auto migration'.
* @throws NotImplementedError by default
*/
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
actual open fun createAutoMigrations(
autoMigrationSpecs: Map<KClass<out AutoMigrationSpec>, AutoMigrationSpec>
): List<Migration> {
@@ -178,7 +178,7 @@
* @param T The type of the expected Type Converter subclass.
* @return An instance of T.
*/
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
@Suppress("UNCHECKED_CAST")
actual fun <T : Any> getTypeConverter(klass: KClass<T>): T {
return typeConverters[klass] as T
@@ -204,7 +204,7 @@
*
* @return A map that will include all required type converters for this database.
*/
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
protected actual open fun getRequiredTypeConverterClasses(): Map<KClass<*>, List<KClass<*>>> {
throw NotImplementedError()
}
@@ -219,7 +219,7 @@
*
* @param connection The database connection.
*/
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
protected actual fun internalInitInvalidationTracker(connection: SQLiteConnection) {
invalidationTracker.internalInit(connection)
}
diff --git a/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/util/DBUtil.jvmNative.kt b/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/util/DBUtil.jvmNative.kt
index a60784a..61e53f1 100644
--- a/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/util/DBUtil.jvmNative.kt
+++ b/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/util/DBUtil.jvmNative.kt
@@ -29,7 +29,7 @@
import kotlinx.coroutines.withContext
/** Performs a database operation. */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
actual suspend fun <R> performSuspending(
db: RoomDatabase,
isReadOnly: Boolean,
@@ -58,7 +58,7 @@
* This function should only be invoked from generated code and is needed to support `@Transaction`
* delegates in Java and Kotlin. It is preferred to use the other 'perform' functions.
*/
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
actual suspend fun <R> performInTransactionSuspending(db: RoomDatabase, block: suspend () -> R): R =
withContext(db.getCoroutineContext(inTransaction = true)) {
db.internalPerform(isReadOnly = false, inTransaction = true) { block.invoke() }
diff --git a/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/util/FtsTableInfo.jvmNative.kt b/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/util/FtsTableInfo.jvmNative.kt
index 8d60b9a..b315548 100644
--- a/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/util/FtsTableInfo.jvmNative.kt
+++ b/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/util/FtsTableInfo.jvmNative.kt
@@ -21,7 +21,7 @@
import kotlin.jvm.JvmStatic
/** A data class that holds the information about an FTS table. */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
actual class FtsTableInfo(
/** The table name */
@JvmField actual val name: String,
diff --git a/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/util/TableInfo.jvmNative.kt b/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/util/TableInfo.jvmNative.kt
index 7bfa4b1..5c5a8b7 100644
--- a/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/util/TableInfo.jvmNative.kt
+++ b/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/util/TableInfo.jvmNative.kt
@@ -30,7 +30,7 @@
*
* Even though SQLite column names are case insensitive, this class uses case sensitive matching.
*/
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
actual class TableInfo
actual constructor(
/** The table name. */
@@ -80,7 +80,7 @@
}
/** Holds the information about a database column. */
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
actual class Column
actual constructor(
/** The column name. */
@@ -117,7 +117,7 @@
}
/** Holds the information about an SQLite foreign key */
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
actual class ForeignKey
actual constructor(
actual val referenceTable: String,
@@ -134,7 +134,7 @@
}
/** Holds the information about an SQLite index */
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
actual class Index
actual constructor(
actual val name: String,
diff --git a/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/util/ViewInfo.jvmNative.kt b/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/util/ViewInfo.jvmNative.kt
index 60e07e7..a31fac8 100644
--- a/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/util/ViewInfo.jvmNative.kt
+++ b/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/util/ViewInfo.jvmNative.kt
@@ -26,7 +26,7 @@
*
* Even though SQLite column names are case insensitive, this class uses case sensitive matching.
*/
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
actual class ViewInfo
actual constructor(
/** The view name */
diff --git a/room/room-runtime/src/nativeMain/kotlin/androidx/room/util/KClassUtil.native.kt b/room/room-runtime/src/nativeMain/kotlin/androidx/room/util/KClassUtil.native.kt
index 3be16d4..0a3ccce 100644
--- a/room/room-runtime/src/nativeMain/kotlin/androidx/room/util/KClassUtil.native.kt
+++ b/room/room-runtime/src/nativeMain/kotlin/androidx/room/util/KClassUtil.native.kt
@@ -32,7 +32,7 @@
* annotated type.
*/
@OptIn(ExperimentalAssociatedObjects::class)
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX) // used in generated code
fun <T : RoomDatabase> findDatabaseConstructorAndInitDatabaseImpl(klass: KClass<*>): T {
val constructor = klass.findAssociatedObject<ConstructedBy>() as? RoomDatabaseConstructor<*>
checkNotNull(constructor) {
diff --git a/savedstate/savedstate/api/current.txt b/savedstate/savedstate/api/current.txt
index 8a63bfe..2c7e9e3d 100644
--- a/savedstate/savedstate/api/current.txt
+++ b/savedstate/savedstate/api/current.txt
@@ -169,10 +169,8 @@
}
public final class SavedStateRegistryOwnerDelegatesKt {
- method public static inline <reified T> kotlin.properties.ReadWriteProperty<java.lang.Object?,T> saved(androidx.savedstate.SavedStateRegistryOwner, String key, kotlin.jvm.functions.Function0<? extends T> init);
- method public static <T> kotlin.properties.ReadWriteProperty<java.lang.Object?,T> saved(androidx.savedstate.SavedStateRegistryOwner, String key, kotlinx.serialization.KSerializer<T> serializer, kotlin.jvm.functions.Function0<? extends T> init);
- method public static inline <reified T> kotlin.properties.ReadWriteProperty<java.lang.Object?,T> saved(androidx.savedstate.SavedStateRegistryOwner, kotlin.jvm.functions.Function0<? extends T> init);
- method public static <T> kotlin.properties.ReadWriteProperty<java.lang.Object?,T> saved(androidx.savedstate.SavedStateRegistryOwner, kotlinx.serialization.KSerializer<T> serializer, kotlin.jvm.functions.Function0<? extends T> init);
+ method public static inline <reified T> kotlin.properties.ReadWriteProperty<java.lang.Object?,T> saved(androidx.savedstate.SavedStateRegistryOwner, optional String? key, kotlin.jvm.functions.Function0<? extends T> init);
+ method public static <T> kotlin.properties.ReadWriteProperty<java.lang.Object?,T> saved(androidx.savedstate.SavedStateRegistryOwner, kotlinx.serialization.KSerializer<T> serializer, optional String? key, kotlin.jvm.functions.Function0<? extends T> init);
}
}
@@ -219,6 +217,11 @@
property public final kotlinx.serialization.descriptors.SerialDescriptor descriptor;
}
+ public final class MutableStateFlowSerializerKt {
+ method public static inline <reified T> kotlinx.serialization.KSerializer<kotlinx.coroutines.flow.MutableStateFlow<T>> MutableStateFlowSerializer();
+ method public static <T> kotlinx.serialization.KSerializer<kotlinx.coroutines.flow.MutableStateFlow<T>> MutableStateFlowSerializer(kotlinx.serialization.KSerializer<T> serializer);
+ }
+
public final class ParcelableArraySerializer implements kotlinx.serialization.KSerializer<android.os.Parcelable[]> {
ctor public ParcelableArraySerializer();
method public android.os.Parcelable[] deserialize(kotlinx.serialization.encoding.Decoder decoder);
diff --git a/savedstate/savedstate/api/restricted_current.txt b/savedstate/savedstate/api/restricted_current.txt
index 0f65840..f87e8a6 100644
--- a/savedstate/savedstate/api/restricted_current.txt
+++ b/savedstate/savedstate/api/restricted_current.txt
@@ -194,10 +194,8 @@
}
public final class SavedStateRegistryOwnerDelegatesKt {
- method public static inline <reified T> kotlin.properties.ReadWriteProperty<java.lang.Object?,T> saved(androidx.savedstate.SavedStateRegistryOwner, String key, kotlin.jvm.functions.Function0<? extends T> init);
- method public static <T> kotlin.properties.ReadWriteProperty<java.lang.Object?,T> saved(androidx.savedstate.SavedStateRegistryOwner, String key, kotlinx.serialization.KSerializer<T> serializer, kotlin.jvm.functions.Function0<? extends T> init);
- method public static inline <reified T> kotlin.properties.ReadWriteProperty<java.lang.Object?,T> saved(androidx.savedstate.SavedStateRegistryOwner, kotlin.jvm.functions.Function0<? extends T> init);
- method public static <T> kotlin.properties.ReadWriteProperty<java.lang.Object?,T> saved(androidx.savedstate.SavedStateRegistryOwner, kotlinx.serialization.KSerializer<T> serializer, kotlin.jvm.functions.Function0<? extends T> init);
+ method public static inline <reified T> kotlin.properties.ReadWriteProperty<java.lang.Object?,T> saved(androidx.savedstate.SavedStateRegistryOwner, optional String? key, kotlin.jvm.functions.Function0<? extends T> init);
+ method public static <T> kotlin.properties.ReadWriteProperty<java.lang.Object?,T> saved(androidx.savedstate.SavedStateRegistryOwner, kotlinx.serialization.KSerializer<T> serializer, optional String? key, kotlin.jvm.functions.Function0<? extends T> init);
}
}
@@ -244,6 +242,11 @@
property public final kotlinx.serialization.descriptors.SerialDescriptor descriptor;
}
+ public final class MutableStateFlowSerializerKt {
+ method public static inline <reified T> kotlinx.serialization.KSerializer<kotlinx.coroutines.flow.MutableStateFlow<T>> MutableStateFlowSerializer();
+ method public static <T> kotlinx.serialization.KSerializer<kotlinx.coroutines.flow.MutableStateFlow<T>> MutableStateFlowSerializer(kotlinx.serialization.KSerializer<T> serializer);
+ }
+
public final class ParcelableArraySerializer implements kotlinx.serialization.KSerializer<android.os.Parcelable[]> {
ctor public ParcelableArraySerializer();
method public android.os.Parcelable[] deserialize(kotlinx.serialization.encoding.Decoder decoder);
diff --git a/savedstate/savedstate/bcv/native/current.txt b/savedstate/savedstate/bcv/native/current.txt
index 2f3971d..9b72797 100644
--- a/savedstate/savedstate/bcv/native/current.txt
+++ b/savedstate/savedstate/bcv/native/current.txt
@@ -161,16 +161,16 @@
final const val androidx.savedstate/DEFAULT_LONG // androidx.savedstate/DEFAULT_LONG|{}DEFAULT_LONG[0]
final fun <get-DEFAULT_LONG>(): kotlin/Long // androidx.savedstate/DEFAULT_LONG.<get-DEFAULT_LONG>|<get-DEFAULT_LONG>(){}[0]
-final fun <#A: kotlin/Any> (androidx.savedstate/SavedStateRegistryOwner).androidx.savedstate.serialization/saved(kotlin/String, kotlinx.serialization/KSerializer<#A>, kotlin/Function0<#A>): kotlin.properties/ReadWriteProperty<kotlin/Any?, #A> // androidx.savedstate.serialization/saved|saved@androidx.savedstate.SavedStateRegistryOwner(kotlin.String;kotlinx.serialization.KSerializer<0:0>;kotlin.Function0<0:0>){0§<kotlin.Any>}[0]
-final fun <#A: kotlin/Any> (androidx.savedstate/SavedStateRegistryOwner).androidx.savedstate.serialization/saved(kotlinx.serialization/KSerializer<#A>, kotlin/Function0<#A>): kotlin.properties/ReadWriteProperty<kotlin/Any?, #A> // androidx.savedstate.serialization/saved|saved@androidx.savedstate.SavedStateRegistryOwner(kotlinx.serialization.KSerializer<0:0>;kotlin.Function0<0:0>){0§<kotlin.Any>}[0]
+final fun <#A: kotlin/Any> (androidx.savedstate/SavedStateRegistryOwner).androidx.savedstate.serialization/saved(kotlinx.serialization/KSerializer<#A>, kotlin/String? = ..., kotlin/Function0<#A>): kotlin.properties/ReadWriteProperty<kotlin/Any?, #A> // androidx.savedstate.serialization/saved|saved@androidx.savedstate.SavedStateRegistryOwner(kotlinx.serialization.KSerializer<0:0>;kotlin.String?;kotlin.Function0<0:0>){0§<kotlin.Any>}[0]
final fun <#A: kotlin/Any> androidx.savedstate.serialization/decodeFromSavedState(kotlinx.serialization/DeserializationStrategy<#A>, androidx.savedstate/SavedState): #A // androidx.savedstate.serialization/decodeFromSavedState|decodeFromSavedState(kotlinx.serialization.DeserializationStrategy<0:0>;androidx.savedstate.SavedState){0§<kotlin.Any>}[0]
final fun <#A: kotlin/Any> androidx.savedstate.serialization/encodeToSavedState(kotlinx.serialization/SerializationStrategy<#A>, #A): androidx.savedstate/SavedState // androidx.savedstate.serialization/encodeToSavedState|encodeToSavedState(kotlinx.serialization.SerializationStrategy<0:0>;0:0){0§<kotlin.Any>}[0]
+final fun <#A: kotlin/Any?> androidx.savedstate.serialization.serializers/MutableStateFlowSerializer(kotlinx.serialization/KSerializer<#A>): kotlinx.serialization/KSerializer<kotlinx.coroutines.flow/MutableStateFlow<#A>> // androidx.savedstate.serialization.serializers/MutableStateFlowSerializer|MutableStateFlowSerializer(kotlinx.serialization.KSerializer<0:0>){0§<kotlin.Any?>}[0]
final inline fun <#A: kotlin/Any?> (androidx.savedstate/SavedState).androidx.savedstate/read(kotlin/Function1<androidx.savedstate/SavedStateReader, #A>): #A // androidx.savedstate/read|read@androidx.savedstate.SavedState(kotlin.Function1<androidx.savedstate.SavedStateReader,0:0>){0§<kotlin.Any?>}[0]
final inline fun <#A: kotlin/Any?> (androidx.savedstate/SavedState).androidx.savedstate/write(kotlin/Function1<androidx.savedstate/SavedStateWriter, #A>): #A // androidx.savedstate/write|write@androidx.savedstate.SavedState(kotlin.Function1<androidx.savedstate.SavedStateWriter,0:0>){0§<kotlin.Any?>}[0]
-final inline fun <#A: reified kotlin/Any> (androidx.savedstate/SavedStateRegistryOwner).androidx.savedstate.serialization/saved(kotlin/String, noinline kotlin/Function0<#A>): kotlin.properties/ReadWriteProperty<kotlin/Any?, #A> // androidx.savedstate.serialization/saved|saved@androidx.savedstate.SavedStateRegistryOwner(kotlin.String;kotlin.Function0<0:0>){0§<kotlin.Any>}[0]
-final inline fun <#A: reified kotlin/Any> (androidx.savedstate/SavedStateRegistryOwner).androidx.savedstate.serialization/saved(noinline kotlin/Function0<#A>): kotlin.properties/ReadWriteProperty<kotlin/Any?, #A> // androidx.savedstate.serialization/saved|saved@androidx.savedstate.SavedStateRegistryOwner(kotlin.Function0<0:0>){0§<kotlin.Any>}[0]
+final inline fun <#A: reified kotlin/Any> (androidx.savedstate/SavedStateRegistryOwner).androidx.savedstate.serialization/saved(kotlin/String? = ..., noinline kotlin/Function0<#A>): kotlin.properties/ReadWriteProperty<kotlin/Any?, #A> // androidx.savedstate.serialization/saved|saved@androidx.savedstate.SavedStateRegistryOwner(kotlin.String?;kotlin.Function0<0:0>){0§<kotlin.Any>}[0]
final inline fun <#A: reified kotlin/Any> androidx.savedstate.serialization/decodeFromSavedState(androidx.savedstate/SavedState): #A // androidx.savedstate.serialization/decodeFromSavedState|decodeFromSavedState(androidx.savedstate.SavedState){0§<kotlin.Any>}[0]
final inline fun <#A: reified kotlin/Any> androidx.savedstate.serialization/encodeToSavedState(#A): androidx.savedstate/SavedState // androidx.savedstate.serialization/encodeToSavedState|encodeToSavedState(0:0){0§<kotlin.Any>}[0]
+final inline fun <#A: reified kotlin/Any?> androidx.savedstate.serialization.serializers/MutableStateFlowSerializer(): kotlinx.serialization/KSerializer<kotlinx.coroutines.flow/MutableStateFlow<#A>> // androidx.savedstate.serialization.serializers/MutableStateFlowSerializer|MutableStateFlowSerializer(){0§<kotlin.Any?>}[0]
final inline fun androidx.savedstate/keyNotFoundError(kotlin/String): kotlin/Nothing // androidx.savedstate/keyNotFoundError|keyNotFoundError(kotlin.String){}[0]
final inline fun androidx.savedstate/savedState(kotlin.collections/Map<kotlin/String, kotlin/Any?> = ..., kotlin/Function1<androidx.savedstate/SavedStateWriter, kotlin/Unit> = ...): androidx.savedstate/SavedState // androidx.savedstate/savedState|savedState(kotlin.collections.Map<kotlin.String,kotlin.Any?>;kotlin.Function1<androidx.savedstate.SavedStateWriter,kotlin.Unit>){}[0]
final inline fun androidx.savedstate/valueNotFoundError(kotlin/String): kotlin/Nothing // androidx.savedstate/valueNotFoundError|valueNotFoundError(kotlin.String){}[0]
diff --git a/savedstate/savedstate/build.gradle b/savedstate/savedstate/build.gradle
index b3e7304..9d326c2 100644
--- a/savedstate/savedstate/build.gradle
+++ b/savedstate/savedstate/build.gradle
@@ -32,6 +32,7 @@
api(libs.kotlinStdlib)
api("androidx.annotation:annotation:1.8.0")
api(projectOrArtifact(":lifecycle:lifecycle-common"))
+ api(libs.kotlinCoroutinesCore)
api(libs.kotlinSerializationCore)
}
}
diff --git a/savedstate/savedstate/src/androidMain/kotlin/androidx/savedstate/serialization/serializers/BuiltInSerializer.android.kt b/savedstate/savedstate/src/androidMain/kotlin/androidx/savedstate/serialization/serializers/BuiltInSerializer.android.kt
index 071f4c1..adb16e1 100644
--- a/savedstate/savedstate/src/androidMain/kotlin/androidx/savedstate/serialization/serializers/BuiltInSerializer.android.kt
+++ b/savedstate/savedstate/src/androidMain/kotlin/androidx/savedstate/serialization/serializers/BuiltInSerializer.android.kt
@@ -117,7 +117,6 @@
encoder.run { savedState.write { putCharSequence(key, value) } }
}
- @Suppress("UNCHECKED_CAST")
override fun deserialize(decoder: Decoder): CharSequence {
require(decoder is SavedStateDecoder) {
decoderErrorMessage(descriptor.serialName, decoder)
diff --git a/savedstate/savedstate/src/commonMain/kotlin/androidx/savedstate/serialization/SavedStateRegistryOwnerDelegates.kt b/savedstate/savedstate/src/commonMain/kotlin/androidx/savedstate/serialization/SavedStateRegistryOwnerDelegates.kt
index a890db3..c11359f 100644
--- a/savedstate/savedstate/src/commonMain/kotlin/androidx/savedstate/serialization/SavedStateRegistryOwnerDelegates.kt
+++ b/savedstate/savedstate/src/commonMain/kotlin/androidx/savedstate/serialization/SavedStateRegistryOwnerDelegates.kt
@@ -28,16 +28,17 @@
* within the [SavedStateRegistry] of this [SavedStateRegistryOwner].
*
* @sample androidx.savedstate.serialization.savedStateRegistryOwner_saved_withKey_withSerializer
- * @param key The [String] key to use for storing the value in the [SavedStateRegistry].
* @param serializer The [KSerializer] to use for serializing and deserializing the value.
+ * @param key An optional [String] key to use for storing the value in the [SavedStateRegistry]. A
+ * default key will be generated if it's omitted or when 'null' is passed.
* @param init The function to provide the initial value of the property.
* @return A property delegate provider that manages the saving and restoring of the value.
* @see encodeToSavedState
* @see decodeFromSavedState
*/
public fun <T : Any> SavedStateRegistryOwner.saved(
- key: String,
serializer: KSerializer<T>,
+ key: String? = null,
init: () -> T,
): ReadWriteProperty<Any?, T> {
return SavedStateRegistryOwnerDelegate(
@@ -52,34 +53,9 @@
* Returns a property delegate provider that manages the saving and restoring of a value of type [T]
* within the [SavedStateRegistry] of this [SavedStateRegistryOwner].
*
- * This is a convenience overload that uses the name of the property as the key for storing the
- * value in the [SavedStateRegistry]
- *
- * @sample androidx.savedstate.serialization.savedStateRegistryOwner_saved_withSerializer
- * @param serializer The [KSerializer] to use for serializing and deserializing the value.
- * @param init The function to provide the initial value of the property.
- * @return A property delegate provider that manages the saving and restoring of the value.
- * @see encodeToSavedState
- * @see decodeFromSavedState
- */
-public fun <T : Any> SavedStateRegistryOwner.saved(
- serializer: KSerializer<T>,
- init: () -> T,
-): ReadWriteProperty<Any?, T> {
- return SavedStateRegistryOwnerDelegate(
- registry = savedStateRegistry,
- key = null,
- serializer = serializer,
- init = init
- )
-}
-
-/**
- * Returns a property delegate provider that manages the saving and restoring of a value of type [T]
- * within the [SavedStateRegistry] of this [SavedStateRegistryOwner].
- *
* @sample androidx.savedstate.serialization.savedStateRegistryOwner_saved_withKey
- * @param key The [String] key to use for storing the value in the [SavedStateRegistry].
+ * @param key An optional [String] key to use for storing the value in the [SavedStateRegistry]. A
+ * default key will be generated if it's omitted or when 'null' is passed.
* @param init The function to provide the initial value of the property.
* @return A property delegate provider that manages the saving and restoring of the value.
* @see encodeToSavedState
@@ -87,30 +63,9 @@
* @see serializer
*/
public inline fun <reified T : Any> SavedStateRegistryOwner.saved(
- key: String,
+ key: String? = null,
noinline init: () -> T,
-): ReadWriteProperty<Any?, T> = saved(key = key, serializer = serializer(), init = init)
-
-/**
- * Returns a property delegate provider that manages the saving and restoring of a value of type [T]
- * within the [SavedStateRegistry] of this [SavedStateRegistryOwner].
- *
- * This is a convenience overload that uses the [serializer] function to obtain the serializer for
- * the reified type [T].
- *
- * The name of the property will be used as the key for storing the value in the
- * [SavedStateRegistry].
- *
- * @sample androidx.savedstate.serialization.savedStateRegistryOwner_saved
- * @param init The function to provide the initial value of the property.
- * @return A property delegate provider that manages the saving and restoring of the value.
- * @see encodeToSavedState
- * @see decodeFromSavedState
- * @see serializer
- */
-public inline fun <reified T : Any> SavedStateRegistryOwner.saved(
- noinline init: () -> T,
-): ReadWriteProperty<Any?, T> = saved(serializer = serializer(), init = init)
+): ReadWriteProperty<Any?, T> = saved(serializer = serializer(), key = key, init = init)
private class SavedStateRegistryOwnerDelegate<T : Any>(
private val registry: SavedStateRegistry,
diff --git a/savedstate/savedstate/src/commonMain/kotlin/androidx/savedstate/serialization/serializers/MutableStateFlowSerializer.kt b/savedstate/savedstate/src/commonMain/kotlin/androidx/savedstate/serialization/serializers/MutableStateFlowSerializer.kt
new file mode 100644
index 0000000..eb076e05
--- /dev/null
+++ b/savedstate/savedstate/src/commonMain/kotlin/androidx/savedstate/serialization/serializers/MutableStateFlowSerializer.kt
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+@file:OptIn(InternalSerializationApi::class, ExperimentalTypeInference::class)
+
+package androidx.savedstate.serialization.serializers
+
+import kotlin.experimental.ExperimentalTypeInference
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.serialization.InternalSerializationApi
+import kotlinx.serialization.KSerializer
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.descriptors.SerialDescriptor
+import kotlinx.serialization.encoding.Decoder
+import kotlinx.serialization.encoding.Encoder
+import kotlinx.serialization.serializer
+
+/**
+ * Creates a [KSerializer] for a [MutableStateFlow] containing a [Serializable] value of type [T].
+ *
+ * This inline function infers the state type [T] automatically and retrieves the appropriate
+ * [KSerializer] for serialization and deserialization of [MutableStateFlow].
+ *
+ * @param T The type of the value stored in the [MutableStateFlow].
+ * @return A [KSerializer] for handling [MutableStateFlow] containing a [Serializable] type [T].
+ */
+public inline fun <reified T> MutableStateFlowSerializer(): KSerializer<MutableStateFlow<T>> {
+ return MutableStateFlowSerializer(serializer())
+}
+
+/**
+ * Creates a [KSerializer] for a [MutableStateFlow] containing a [Serializable] value of type [T].
+ *
+ * This function allows for explicit specification of the [KSerializer] for the state type [T]. It
+ * provides serialization and deserialization capabilities for [MutableStateFlow] objects.
+ *
+ * @param T The type of the value stored in the [MutableStateFlow].
+ * @param serializer The [KSerializer] for the [Serializable] type [T].
+ * @return A [KSerializer] for handling [MutableStateFlow] containing a [Serializable] type [T].
+ */
+public fun <T> MutableStateFlowSerializer(
+ serializer: KSerializer<T>
+): KSerializer<MutableStateFlow<T>> {
+ return MutableStateFlowSerializerImpl<T>(serializer)
+}
+
+/**
+ * Internal implementation of [KSerializer] for [MutableStateFlow].
+ *
+ * This private class wraps a [KSerializer] for the inner value type [T], enabling serialization and
+ * deserialization of [MutableStateFlow] instances. The inner value serialization is delegated to
+ * the provided [valueSerializer].
+ *
+ * @param T The type of the value stored in the [MutableStateFlow].
+ * @property valueSerializer The [KSerializer] used to serialize and deserialize the inner value.
+ */
+private class MutableStateFlowSerializerImpl<T>(
+ private val valueSerializer: KSerializer<T>,
+) : KSerializer<MutableStateFlow<T>> {
+
+ override val descriptor: SerialDescriptor = valueSerializer.descriptor
+
+ override fun serialize(encoder: Encoder, value: MutableStateFlow<T>) {
+ valueSerializer.serialize(encoder, value.value)
+ }
+
+ override fun deserialize(decoder: Decoder): MutableStateFlow<T> {
+ return MutableStateFlow(valueSerializer.deserialize(decoder))
+ }
+}
diff --git a/savedstate/savedstate/src/commonTest/kotlin/androidx/savedstate/serialization/MutableStateFlowSerializerTest.kt b/savedstate/savedstate/src/commonTest/kotlin/androidx/savedstate/serialization/MutableStateFlowSerializerTest.kt
new file mode 100644
index 0000000..1c38889
--- /dev/null
+++ b/savedstate/savedstate/src/commonTest/kotlin/androidx/savedstate/serialization/MutableStateFlowSerializerTest.kt
@@ -0,0 +1,58 @@
+/*
+ * 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.savedstate.serialization
+
+import androidx.kruth.assertThat
+import androidx.savedstate.RobolectricTest
+import androidx.savedstate.serialization.serializers.MutableStateFlowSerializer
+import kotlin.test.Test
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.serialization.InternalSerializationApi
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.serializer
+
+internal class MutableStateFlowSerializerTest : RobolectricTest() {
+
+ @Test
+ fun encodeDecode_withImplicitSerializer() {
+ val state = MutableStateFlow(USER_JOHN_DOE)
+ val serializer = MutableStateFlowSerializer<User>()
+
+ val encoded = encodeToSavedState(serializer, state)
+ val decoded = decodeFromSavedState(serializer, encoded)
+
+ assertThat(state.value).isEqualTo(decoded.value)
+ }
+
+ @Test
+ fun encodeDecode_withExplicitSerializer() {
+ val state = MutableStateFlow(USER_JOHN_DOE)
+ val serializer = MutableStateFlowSerializer(USER_SERIALIZER)
+
+ val encoded = encodeToSavedState(serializer, state)
+ val decoded = decodeFromSavedState(serializer, encoded)
+
+ assertThat(state.value).isEqualTo(decoded.value)
+ }
+
+ companion object {
+ val USER_JOHN_DOE = User(name = "John", surname = "Doe")
+ @OptIn(InternalSerializationApi::class) val USER_SERIALIZER = User::class.serializer()
+ }
+
+ @Serializable data class User(val name: String = "John", val surname: String = "Doe")
+}
diff --git a/security/security-state-provider/src/main/java/androidx/security/state/provider/UpdateInfoManager.kt b/security/security-state-provider/src/main/java/androidx/security/state/provider/UpdateInfoManager.kt
index f04ac2ff..b0e695b 100644
--- a/security/security-state-provider/src/main/java/androidx/security/state/provider/UpdateInfoManager.kt
+++ b/security/security-state-provider/src/main/java/androidx/security/state/provider/UpdateInfoManager.kt
@@ -18,6 +18,7 @@
import android.content.Context
import androidx.security.state.SecurityPatchState
+import androidx.security.state.SecurityPatchState.Companion.getComponentSecurityPatchLevel
import kotlinx.serialization.json.Json
/**
@@ -93,11 +94,7 @@
// Ignore unknown components.
return@forEach
}
- val updateSpl =
- securityState.getComponentSecurityPatchLevel(
- component,
- updateInfo.securityPatchLevel
- )
+ val updateSpl = getComponentSecurityPatchLevel(component, updateInfo.securityPatchLevel)
if (updateSpl <= currentSpl) {
val key = getKeyForUpdateInfo(updateInfo)
diff --git a/security/security-state-provider/src/test/java/androidx/security/state/provider/UpdateInfoManagerTest.kt b/security/security-state-provider/src/test/java/androidx/security/state/provider/UpdateInfoManagerTest.kt
index 81b6111..a87a5d7 100644
--- a/security/security-state-provider/src/test/java/androidx/security/state/provider/UpdateInfoManagerTest.kt
+++ b/security/security-state-provider/src/test/java/androidx/security/state/provider/UpdateInfoManagerTest.kt
@@ -33,20 +33,13 @@
import org.mockito.Mockito.times
import org.mockito.Mockito.`when`
import org.mockito.kotlin.doReturn
-import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
@RunWith(AndroidJUnit4::class)
class UpdateInfoManagerTest {
private lateinit var manager: UpdateInfoManager
- private val mockSpl = SecurityPatchState.DateBasedSecurityPatchLevel(2022, 1, 1)
- private val mockSecurityState: SecurityPatchState =
- mock<SecurityPatchState> {
- on {
- getComponentSecurityPatchLevel(eq(COMPONENT_SYSTEM), Mockito.anyString())
- } doReturn mockSpl
- }
+ private val mockSecurityState: SecurityPatchState = mock<SecurityPatchState>()
@SuppressLint("NewApi")
private val publishedDate = Date.from(LocalDate.now().atStartOfDay().toInstant(ZoneOffset.UTC))
private val updateInfo =
diff --git a/security/security-state/api/current.txt b/security/security-state/api/current.txt
index 510499b..8cae039 100644
--- a/security/security-state/api/current.txt
+++ b/security/security-state/api/current.txt
@@ -3,16 +3,17 @@
public class SecurityPatchState {
ctor public SecurityPatchState(android.content.Context context);
- ctor public SecurityPatchState(android.content.Context context, optional java.util.List<java.lang.String> systemModules);
- ctor public SecurityPatchState(android.content.Context context, optional java.util.List<java.lang.String> systemModules, optional androidx.security.state.SecurityStateManager? customSecurityStateManager);
+ ctor public SecurityPatchState(android.content.Context context, optional java.util.List<java.lang.String> systemModulePackageNames);
+ ctor public SecurityPatchState(android.content.Context context, optional java.util.List<java.lang.String> systemModulePackageNames, optional androidx.security.state.SecurityStateManagerCompat? customSecurityStateManagerCompat);
+ ctor public SecurityPatchState(android.content.Context context, optional java.util.List<java.lang.String> systemModulePackageNames, optional androidx.security.state.SecurityStateManagerCompat? customSecurityStateManagerCompat, optional String? vulnerabilityReportJsonString);
method public final boolean areCvesPatched(java.util.List<java.lang.String> cveList);
- method public androidx.security.state.SecurityPatchState.SecurityPatchLevel getComponentSecurityPatchLevel(String component, String securityPatchLevel);
+ method public static final androidx.security.state.SecurityPatchState.SecurityPatchLevel getComponentSecurityPatchLevel(String component, String securityPatchLevel);
method public androidx.security.state.SecurityPatchState.SecurityPatchLevel getDeviceSecurityPatchLevel(String component);
method public java.util.Map<androidx.security.state.SecurityPatchState.Severity,java.util.Set<java.lang.String>> getPatchedCves(String component, androidx.security.state.SecurityPatchState.SecurityPatchLevel spl);
method public java.util.List<androidx.security.state.SecurityPatchState.SecurityPatchLevel> getPublishedSecurityPatchLevel(String component);
- method @RequiresApi(26) public final android.net.Uri getVulnerabilityReportUrl(android.net.Uri serverUrl);
+ method @RequiresApi(26) public static final android.net.Uri getVulnerabilityReportUrl(optional android.net.Uri serverUrl);
method public final boolean isDeviceFullyUpdated();
- method public final void loadVulnerabilityReport(String jsonString);
+ method @WorkerThread public final void loadVulnerabilityReport(String jsonString);
field public static final String COMPONENT_KERNEL = "KERNEL";
field public static final String COMPONENT_SYSTEM = "SYSTEM";
field public static final String COMPONENT_SYSTEM_MODULES = "SYSTEM_MODULES";
@@ -22,6 +23,8 @@
}
public static final class SecurityPatchState.Companion {
+ method public androidx.security.state.SecurityPatchState.SecurityPatchLevel getComponentSecurityPatchLevel(String component, String securityPatchLevel);
+ method @RequiresApi(26) public android.net.Uri getVulnerabilityReportUrl(optional android.net.Uri serverUrl);
property public static final String COMPONENT_KERNEL;
property public static final String COMPONENT_SYSTEM;
property public static final String COMPONENT_SYSTEM_MODULES;
@@ -75,16 +78,16 @@
method public androidx.security.state.SecurityPatchState.VersionedSecurityPatchLevel fromString(String value);
}
- public class SecurityStateManager {
- ctor public SecurityStateManager(android.content.Context context);
- method public android.os.Bundle getGlobalSecurityState(optional String? moduleMetadataProvider);
- field public static final androidx.security.state.SecurityStateManager.Companion Companion;
+ public class SecurityStateManagerCompat {
+ ctor public SecurityStateManagerCompat(android.content.Context context);
+ method public android.os.Bundle getGlobalSecurityState(optional String moduleMetadataProviderPackageName);
+ field public static final androidx.security.state.SecurityStateManagerCompat.Companion Companion;
field public static final String KEY_KERNEL_VERSION = "kernel_version";
field public static final String KEY_SYSTEM_SPL = "system_spl";
field public static final String KEY_VENDOR_SPL = "vendor_spl";
}
- public static final class SecurityStateManager.Companion {
+ public static final class SecurityStateManagerCompat.Companion {
property public static final String KEY_KERNEL_VERSION;
property public static final String KEY_SYSTEM_SPL;
property public static final String KEY_VENDOR_SPL;
diff --git a/security/security-state/api/restricted_current.txt b/security/security-state/api/restricted_current.txt
index 510499b..8cae039 100644
--- a/security/security-state/api/restricted_current.txt
+++ b/security/security-state/api/restricted_current.txt
@@ -3,16 +3,17 @@
public class SecurityPatchState {
ctor public SecurityPatchState(android.content.Context context);
- ctor public SecurityPatchState(android.content.Context context, optional java.util.List<java.lang.String> systemModules);
- ctor public SecurityPatchState(android.content.Context context, optional java.util.List<java.lang.String> systemModules, optional androidx.security.state.SecurityStateManager? customSecurityStateManager);
+ ctor public SecurityPatchState(android.content.Context context, optional java.util.List<java.lang.String> systemModulePackageNames);
+ ctor public SecurityPatchState(android.content.Context context, optional java.util.List<java.lang.String> systemModulePackageNames, optional androidx.security.state.SecurityStateManagerCompat? customSecurityStateManagerCompat);
+ ctor public SecurityPatchState(android.content.Context context, optional java.util.List<java.lang.String> systemModulePackageNames, optional androidx.security.state.SecurityStateManagerCompat? customSecurityStateManagerCompat, optional String? vulnerabilityReportJsonString);
method public final boolean areCvesPatched(java.util.List<java.lang.String> cveList);
- method public androidx.security.state.SecurityPatchState.SecurityPatchLevel getComponentSecurityPatchLevel(String component, String securityPatchLevel);
+ method public static final androidx.security.state.SecurityPatchState.SecurityPatchLevel getComponentSecurityPatchLevel(String component, String securityPatchLevel);
method public androidx.security.state.SecurityPatchState.SecurityPatchLevel getDeviceSecurityPatchLevel(String component);
method public java.util.Map<androidx.security.state.SecurityPatchState.Severity,java.util.Set<java.lang.String>> getPatchedCves(String component, androidx.security.state.SecurityPatchState.SecurityPatchLevel spl);
method public java.util.List<androidx.security.state.SecurityPatchState.SecurityPatchLevel> getPublishedSecurityPatchLevel(String component);
- method @RequiresApi(26) public final android.net.Uri getVulnerabilityReportUrl(android.net.Uri serverUrl);
+ method @RequiresApi(26) public static final android.net.Uri getVulnerabilityReportUrl(optional android.net.Uri serverUrl);
method public final boolean isDeviceFullyUpdated();
- method public final void loadVulnerabilityReport(String jsonString);
+ method @WorkerThread public final void loadVulnerabilityReport(String jsonString);
field public static final String COMPONENT_KERNEL = "KERNEL";
field public static final String COMPONENT_SYSTEM = "SYSTEM";
field public static final String COMPONENT_SYSTEM_MODULES = "SYSTEM_MODULES";
@@ -22,6 +23,8 @@
}
public static final class SecurityPatchState.Companion {
+ method public androidx.security.state.SecurityPatchState.SecurityPatchLevel getComponentSecurityPatchLevel(String component, String securityPatchLevel);
+ method @RequiresApi(26) public android.net.Uri getVulnerabilityReportUrl(optional android.net.Uri serverUrl);
property public static final String COMPONENT_KERNEL;
property public static final String COMPONENT_SYSTEM;
property public static final String COMPONENT_SYSTEM_MODULES;
@@ -75,16 +78,16 @@
method public androidx.security.state.SecurityPatchState.VersionedSecurityPatchLevel fromString(String value);
}
- public class SecurityStateManager {
- ctor public SecurityStateManager(android.content.Context context);
- method public android.os.Bundle getGlobalSecurityState(optional String? moduleMetadataProvider);
- field public static final androidx.security.state.SecurityStateManager.Companion Companion;
+ public class SecurityStateManagerCompat {
+ ctor public SecurityStateManagerCompat(android.content.Context context);
+ method public android.os.Bundle getGlobalSecurityState(optional String moduleMetadataProviderPackageName);
+ field public static final androidx.security.state.SecurityStateManagerCompat.Companion Companion;
field public static final String KEY_KERNEL_VERSION = "kernel_version";
field public static final String KEY_SYSTEM_SPL = "system_spl";
field public static final String KEY_VENDOR_SPL = "vendor_spl";
}
- public static final class SecurityStateManager.Companion {
+ public static final class SecurityStateManagerCompat.Companion {
property public static final String KEY_KERNEL_VERSION;
property public static final String KEY_SYSTEM_SPL;
property public static final String KEY_VENDOR_SPL;
diff --git a/security/security-state/src/androidTest/java/androidx/security/state/SecurityStateManagerTest.kt b/security/security-state/src/androidTest/java/androidx/security/state/SecurityStateManagerCompatTest.kt
similarity index 88%
rename from security/security-state/src/androidTest/java/androidx/security/state/SecurityStateManagerTest.kt
rename to security/security-state/src/androidTest/java/androidx/security/state/SecurityStateManagerCompatTest.kt
index 7d2ddfa..3c72447 100644
--- a/security/security-state/src/androidTest/java/androidx/security/state/SecurityStateManagerTest.kt
+++ b/security/security-state/src/androidTest/java/androidx/security/state/SecurityStateManagerCompatTest.kt
@@ -32,14 +32,14 @@
@MediumTest
@RunWith(AndroidJUnit4::class)
-class SecurityStateManagerTest {
+class SecurityStateManagerCompatTest {
private val context = ApplicationProvider.getApplicationContext<Context>()
- private lateinit var securityStateManager: SecurityStateManager
+ private lateinit var securityStateManagerCompat: SecurityStateManagerCompat
@Before
fun setup() {
- securityStateManager = SecurityStateManager(context)
+ securityStateManagerCompat = SecurityStateManagerCompat(context)
}
/** Returns `true` if [date] is in the format "YYYY-MM-DD". */
@@ -85,7 +85,7 @@
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
@Test
fun testGetGlobalSecurityState_sdkAbove29() {
- val bundle = securityStateManager.getGlobalSecurityState()
+ val bundle = securityStateManagerCompat.getGlobalSecurityState()
assertTrue(matchesDateFormat(bundle.getString("system_spl")!!))
assertTrue(matchesKernelFormat(bundle.getString("kernel_version")!!))
assertTrue(containsModuleMetadataPackage(bundle))
@@ -95,7 +95,7 @@
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O, maxSdkVersion = Build.VERSION_CODES.P)
@Test
fun testGetGlobalSecurityState_sdkAbove25Below29_doesNotContainModuleMetadata() {
- val bundle = securityStateManager.getGlobalSecurityState()
+ val bundle = securityStateManagerCompat.getGlobalSecurityState()
assertTrue(matchesDateFormat(bundle.getString("system_spl")!!))
assertTrue(matchesKernelFormat(bundle.getString("kernel_version")!!))
assertTrue(containsWebViewPackage(bundle))
@@ -105,7 +105,7 @@
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.M, maxSdkVersion = Build.VERSION_CODES.N_MR1)
@Test
fun testGetGlobalSecurityState_sdkAbove22Below26_doesNotContainModuleMetadataOrWebView() {
- val bundle = securityStateManager.getGlobalSecurityState()
+ val bundle = securityStateManagerCompat.getGlobalSecurityState()
assertTrue(matchesDateFormat(bundle.getString("system_spl")!!))
assertTrue(matchesKernelFormat(bundle.getString("kernel_version")!!))
assertFalse(containsModuleMetadataPackage(bundle))
@@ -118,7 +118,7 @@
)
@Test
fun testGetGlobalSecurityState_sdkBelow23_containsOnlyKernel() {
- val bundle = securityStateManager.getGlobalSecurityState()
+ val bundle = securityStateManagerCompat.getGlobalSecurityState()
assertTrue(matchesKernelFormat(bundle.getString("kernel_version")!!))
assertFalse(bundle.containsKey("system_spl"))
assertFalse(bundle.containsKey("vendor_spl"))
@@ -130,7 +130,7 @@
@Test
fun testGetGlobalSecurityState_whenVendorIsEnabled_containsVendorSpl() {
SecurityPatchState.Companion.USE_VENDOR_SPL = true
- val bundle = securityStateManager.getGlobalSecurityState()
+ val bundle = securityStateManagerCompat.getGlobalSecurityState()
assertTrue(bundle.containsKey("vendor_spl"))
}
@@ -138,7 +138,7 @@
@Test
fun testGetGlobalSecurityState_whenVendorIsDisabled_doesNotContainVendorSpl() {
SecurityPatchState.Companion.USE_VENDOR_SPL = false
- val bundle = securityStateManager.getGlobalSecurityState()
+ val bundle = securityStateManagerCompat.getGlobalSecurityState()
assertFalse(bundle.containsKey("vendor_spl"))
}
@@ -149,7 +149,7 @@
return // Skip this test on non-Google devices.
}
val bundle =
- securityStateManager.getGlobalSecurityState("com.google.android.modulemetadata")
+ securityStateManagerCompat.getGlobalSecurityState("com.google.android.modulemetadata")
DateBasedSecurityPatchLevel.fromString(
bundle.getString("com.google.android.modulemetadata")!!
)
diff --git a/security/security-state/src/main/java/androidx/security/state/SecurityPatchState.kt b/security/security-state/src/main/java/androidx/security/state/SecurityPatchState.kt
index 455fe21..bcc3f13 100644
--- a/security/security-state/src/main/java/androidx/security/state/SecurityPatchState.kt
+++ b/security/security-state/src/main/java/androidx/security/state/SecurityPatchState.kt
@@ -19,11 +19,13 @@
import android.annotation.SuppressLint
import android.content.Context
import android.net.Uri
+import android.os.Build
import androidx.annotation.RequiresApi
import androidx.annotation.StringDef
-import androidx.security.state.SecurityStateManager.Companion.KEY_KERNEL_VERSION
-import androidx.security.state.SecurityStateManager.Companion.KEY_SYSTEM_SPL
-import androidx.security.state.SecurityStateManager.Companion.KEY_VENDOR_SPL
+import androidx.annotation.WorkerThread
+import androidx.security.state.SecurityStateManagerCompat.Companion.KEY_KERNEL_VERSION
+import androidx.security.state.SecurityStateManagerCompat.Companion.KEY_SYSTEM_SPL
+import androidx.security.state.SecurityStateManagerCompat.Companion.KEY_VENDOR_SPL
import java.text.ParseException
import java.text.SimpleDateFormat
import java.util.Calendar
@@ -48,27 +50,48 @@
* The class uses a combination of local data storage and external data fetching to maintain and
* update security states.
*
+ * Recommended pattern of usage:
+ * - call [getVulnerabilityReportUrl] and make a request to download the JSON file containing
+ * vulnerability report data
+ * - create SecurityPatchState object, passing in the downloaded JSON as a [String]
+ * - call [getPublishedSecurityPatchLevel] or other APIs
+ *
* @param context Application context used for accessing shared preferences, resources, and other
* context-dependent features.
- * @param systemModules A list of system module package names, defaults to Google provided system
- * modules if none are provided. The first module on the list must be the system modules metadata
- * provider package.
- * @param customSecurityStateManager An optional custom manager for obtaining security state
+ * @param systemModulePackageNames A list of system module package names, defaults to Google
+ * provided system modules if none are provided. The first module on the list must be the system
+ * modules metadata provider package.
+ * @param customSecurityStateManagerCompat An optional custom manager for obtaining security state
* information. If null, a default manager is instantiated.
+ * @param vulnerabilityReportJsonString A JSON string containing vulnerability data to initialize a
+ * [VulnerabilityReport] object.
+ *
+ * If you only care about the Device SPL, this parameter is optional. If you need access to
+ * Published SPL and Available SPL, you must provide this JSON string, either here in the
+ * constructor, or later using [loadVulnerabilityReport].
+ *
* @constructor Creates an instance of SecurityPatchState.
*/
public open class SecurityPatchState
@JvmOverloads
constructor(
private val context: Context,
- private val systemModules: List<String> = listOf(),
- private val customSecurityStateManager: SecurityStateManager? = null
+ private val systemModulePackageNames: List<String> = DEFAULT_SYSTEM_MODULES,
+ private val customSecurityStateManagerCompat: SecurityStateManagerCompat? = null,
+ vulnerabilityReportJsonString: String? = null
) {
- private val securityStateManager =
- customSecurityStateManager ?: SecurityStateManager(context = context)
+ init {
+ if (vulnerabilityReportJsonString != null) {
+ loadVulnerabilityReport(vulnerabilityReportJsonString)
+ }
+ }
+
+ private val securityStateManagerCompat =
+ customSecurityStateManagerCompat ?: SecurityStateManagerCompat(context = context)
private var vulnerabilityReport: VulnerabilityReport? = null
public companion object {
+ /** Default list of Android Mainline system modules. */
@JvmField
public val DEFAULT_SYSTEM_MODULES: List<String> =
listOf(
@@ -102,6 +125,63 @@
/** Disabled until Android provides sufficient guidelines for the usage of Vendor SPL. */
internal var USE_VENDOR_SPL = false
+
+ /**
+ * Retrieves the specific security patch level for a given component based on a security
+ * patch level string. This method determines the type of [SecurityPatchLevel] to construct
+ * based on the component type, interpreting the string as a date for date-based components
+ * or as a version number for versioned components.
+ *
+ * @param component The component indicating which type of component's patch level is being
+ * requested.
+ * @param securityPatchLevel The string representation of the security patch level, which
+ * could be a date or a version number.
+ * @return A [SecurityPatchLevel] instance corresponding to the specified component and
+ * patch level string.
+ * @throws IllegalArgumentException If the input string is not in a valid format for the
+ * specified component type, or if the component requires a specific format that the
+ * string does not meet.
+ */
+ @JvmStatic
+ public fun getComponentSecurityPatchLevel(
+ @Component component: String,
+ securityPatchLevel: String
+ ): SecurityPatchLevel {
+ val exception = IllegalArgumentException("Unknown component: $component")
+ return when (component) {
+ COMPONENT_SYSTEM,
+ COMPONENT_SYSTEM_MODULES,
+ COMPONENT_VENDOR -> {
+ if (component == COMPONENT_VENDOR && !USE_VENDOR_SPL) {
+ throw exception
+ }
+ // These components are expected to use DateBasedSpl
+ DateBasedSecurityPatchLevel.fromString(securityPatchLevel)
+ }
+ COMPONENT_KERNEL -> {
+ // These components are expected to use VersionedSpl
+ VersionedSecurityPatchLevel.fromString(securityPatchLevel)
+ }
+ else -> throw exception
+ }
+ }
+
+ /**
+ * Constructs a URL for fetching vulnerability reports based on the device's Android
+ * version.
+ *
+ * @param serverUrl The base URL of the server where vulnerability reports are stored.
+ * @return A fully constructed URL pointing to the specific vulnerability report for this
+ * device.
+ */
+ @JvmStatic
+ @RequiresApi(26)
+ public fun getVulnerabilityReportUrl(
+ serverUrl: Uri = Uri.parse(DEFAULT_VULNERABILITY_REPORTS_URL)
+ ): Uri {
+ val newEndpoint = "v1/android_sdk_${Build.VERSION.SDK_INT}.json"
+ return serverUrl.buildUpon().appendEncodedPath(newEndpoint).build()
+ }
}
/** Annotation for defining the component to use. */
@@ -162,6 +242,13 @@
public companion object {
private val DATE_FORMATS = listOf("yyyy-MM", "yyyy-MM-dd")
+ /**
+ * Creates a new [DateBasedSecurityPatchLevel] from a string representation of the date.
+ *
+ * @param value The date string in the format of [DATE_FORMATS].
+ * @return A new [DateBasedSecurityPatchLevel] representing the date.
+ * @throws IllegalArgumentException if the date string is not in the correct format.
+ */
@JvmStatic
public fun fromString(value: String): DateBasedSecurityPatchLevel {
var date: Date? = null
@@ -227,6 +314,14 @@
) : SecurityPatchLevel() {
public companion object {
+ /**
+ * Creates a new [VersionedSecurityPatchLevel] from a string representation of the
+ * version.
+ *
+ * @param value The version string in the format of "major.minor.build.patch".
+ * @return A new [VersionedSecurityPatchLevel] representing the version.
+ * @throws IllegalArgumentException if the version string is not in the correct format.
+ */
@JvmStatic
public fun fromString(value: String): VersionedSecurityPatchLevel {
val parts = value.split(".")
@@ -329,8 +424,7 @@
* @return A list of strings representing system module identifiers.
*/
internal fun getSystemModules(): List<String> {
- // Use the provided systemModules if not empty; otherwise, use defaultSystemModules
- return systemModules.ifEmpty { DEFAULT_SYSTEM_MODULES }
+ return systemModulePackageNames.ifEmpty { DEFAULT_SYSTEM_MODULES }
}
/**
@@ -338,16 +432,10 @@
* of the input JSON and constructs a [VulnerabilityReport] object, preparing the class to
* provide published and available security state information.
*
- * The recommended pattern of usage:
- * - create SecurityPatchState object
- * - call getVulnerabilityReportUrl()
- * - download JSON file containing vulnerability report data
- * - call loadVulnerabilityReport()
- * - call getPublishedSecurityPatchLevel() or other APIs
- *
* @param jsonString The JSON string containing the vulnerability data.
* @throws IllegalArgumentException if the JSON input is malformed or contains invalid data.
*/
+ @WorkerThread
public fun loadVulnerabilityReport(jsonString: String) {
val result: VulnerabilityReport
@@ -426,27 +514,6 @@
vulnerabilityReport = result
}
- /**
- * Constructs a URL for fetching vulnerability reports based on the device's Android version.
- *
- * @param serverUrl The base URL of the server where vulnerability reports are stored.
- * @return A fully constructed URL pointing to the specific vulnerability report for this
- * device.
- * @throws IllegalArgumentException if the Android SDK version is unsupported.
- */
- @RequiresApi(26)
- public fun getVulnerabilityReportUrl(serverUrl: Uri): Uri {
- val androidSdk = securityStateManager.getAndroidSdkInt()
- if (androidSdk < 26) {
- throw IllegalArgumentException(
- "Unsupported SDK version (must be > 25), found $androidSdk."
- )
- }
-
- val newEndpoint = "v1/android_sdk_$androidSdk.json"
- return serverUrl.buildUpon().appendEncodedPath(newEndpoint).build()
- }
-
private fun getMaxComponentSecurityPatchLevel(
@Component component: String
): DateBasedSecurityPatchLevel? {
@@ -488,7 +555,7 @@
try {
packageSpl =
DateBasedSecurityPatchLevel.fromString(
- securityStateManager.getPackageVersion(module)
+ securityStateManagerCompat.getPackageVersion(module)
)
} catch (e: Exception) {
// Prevent malformed package versions from interrupting the loop.
@@ -541,7 +608,8 @@
* @throws IllegalArgumentException if the component name is unrecognized.
*/
public open fun getDeviceSecurityPatchLevel(@Component component: String): SecurityPatchLevel {
- val globalSecurityState = securityStateManager.getGlobalSecurityState(getSystemModules()[0])
+ val globalSecurityState =
+ securityStateManagerCompat.getGlobalSecurityState(getSystemModules()[0])
return when (component) {
COMPONENT_SYSTEM_MODULES -> {
@@ -700,45 +768,6 @@
}
/**
- * Retrieves the specific security patch level for a given component based on a security patch
- * level string. This method determines the type of [SecurityPatchLevel] to construct based on
- * the component type, interpreting the string as a date for date-based components or as a
- * version number for versioned components.
- *
- * @param component The component indicating which type of component's patch level is being
- * requested.
- * @param securityPatchLevel The string representation of the security patch level, which could
- * be a date or a version number.
- * @return A [SecurityPatchLevel] instance corresponding to the specified component and patch
- * level string.
- * @throws IllegalArgumentException If the input string is not in a valid format for the
- * specified component type, or if the component requires a specific format that the string
- * does not meet.
- */
- public open fun getComponentSecurityPatchLevel(
- @Component component: String,
- securityPatchLevel: String
- ): SecurityPatchLevel {
- val exception = IllegalArgumentException("Unknown component: $component")
- return when (component) {
- COMPONENT_SYSTEM,
- COMPONENT_SYSTEM_MODULES,
- COMPONENT_VENDOR -> {
- if (component == COMPONENT_VENDOR && !USE_VENDOR_SPL) {
- throw exception
- }
- // These components are expected to use DateBasedSpl
- DateBasedSecurityPatchLevel.fromString(securityPatchLevel)
- }
- COMPONENT_KERNEL -> {
- // These components are expected to use VersionedSpl
- VersionedSecurityPatchLevel.fromString(securityPatchLevel)
- }
- else -> throw exception
- }
- }
-
- /**
* Checks if all components of the device have their security patch levels up to date with the
* published security patch levels. This method compares the device's current security patch
* level against the latest published levels for each component.
diff --git a/security/security-state/src/main/java/androidx/security/state/SecurityStateManager.kt b/security/security-state/src/main/java/androidx/security/state/SecurityStateManagerCompat.kt
similarity index 88%
rename from security/security-state/src/main/java/androidx/security/state/SecurityStateManager.kt
rename to security/security-state/src/main/java/androidx/security/state/SecurityStateManagerCompat.kt
index 2676d6c..d5abdca 100644
--- a/security/security-state/src/main/java/androidx/security/state/SecurityStateManager.kt
+++ b/security/security-state/src/main/java/androidx/security/state/SecurityStateManagerCompat.kt
@@ -40,7 +40,7 @@
* security-related information, which is crucial for maintaining the security integrity of the
* device.
*/
-public open class SecurityStateManager(private val context: Context) {
+public open class SecurityStateManagerCompat(private val context: Context) {
public companion object {
private const val TAG = "SecurityStateManager"
@@ -76,26 +76,24 @@
* and module information into a Bundle. This method can optionally use Google's module metadata
* providers to enhance the data returned.
*
- * @param moduleMetadataProvider Specifies package name for system modules metadata.
+ * @param moduleMetadataProviderPackageName Specifies package name for system modules metadata.
* @return A Bundle containing keys and values representing the security state of the system,
* vendor, and kernel.
*/
- @SuppressLint("NewApi") // Lint does not detect version check below.
- public open fun getGlobalSecurityState(moduleMetadataProvider: String? = null): Bundle {
- if (getAndroidSdkInt() >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+ public open fun getGlobalSecurityState(
+ moduleMetadataProviderPackageName: String = ANDROID_MODULE_METADATA_PROVIDER
+ ): Bundle {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
return getGlobalSecurityStateFromService()
}
return Bundle().apply {
- if (getAndroidSdkInt() >= Build.VERSION_CODES.M) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
putString(KEY_SYSTEM_SPL, Build.VERSION.SECURITY_PATCH)
if (USE_VENDOR_SPL) {
putString(KEY_VENDOR_SPL, getVendorSpl())
}
}
- if (getAndroidSdkInt() >= Build.VERSION_CODES.Q) {
- val moduleMetadataProviderPackageName =
- moduleMetadataProvider ?: ANDROID_MODULE_METADATA_PROVIDER
-
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if (moduleMetadataProviderPackageName.isNotEmpty()) {
putString(
moduleMetadataProviderPackageName,
@@ -107,7 +105,9 @@
if (kernelVersion.isNotEmpty()) {
putString(KEY_KERNEL_VERSION, kernelVersion)
}
- addWebViewPackages(this)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ addWebViewPackages(this)
+ }
}
}
@@ -159,9 +159,6 @@
*/
@RequiresApi(26)
private fun addWebViewPackages(bundle: Bundle) {
- if (getAndroidSdkInt() < Build.VERSION_CODES.O) {
- return
- }
val packageName = getCurrentWebViewPackageName()
if (packageName.isNotEmpty()) {
bundle.putString(packageName, getPackageVersion(packageName))
@@ -187,15 +184,6 @@
}
/**
- * Retrieves the SDK version of the current Android system.
- *
- * @return the SDK version as an integer.
- */
- internal open fun getAndroidSdkInt(): Int {
- return Build.VERSION.SDK_INT
- }
-
- /**
* Safely retrieves the current security patch level of the device's operating system. This
* method ensures compatibility by checking the Android version before attempting to access APIs
* that are not available on older versions.
@@ -203,9 +191,8 @@
* @return A string representing the current security patch level, or empty string if it cannot
* be retrieved.
*/
- @SuppressLint("NewApi") // Lint does not detect version check below.
internal fun getSecurityPatchLevelSafe(): String {
- return if (getAndroidSdkInt() >= Build.VERSION_CODES.M) {
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Build.VERSION.SECURITY_PATCH
} else {
""
diff --git a/security/security-state/src/test/java/androidx/security/state/SecurityPatchStateTest.kt b/security/security-state/src/test/java/androidx/security/state/SecurityPatchStateTest.kt
index 440fd0f6..a09f5b7 100644
--- a/security/security-state/src/test/java/androidx/security/state/SecurityPatchStateTest.kt
+++ b/security/security-state/src/test/java/androidx/security/state/SecurityPatchStateTest.kt
@@ -21,6 +21,7 @@
import android.os.Build
import android.os.Bundle
import androidx.annotation.RequiresApi
+import androidx.security.state.SecurityPatchState.Companion.getComponentSecurityPatchLevel
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
@@ -39,12 +40,13 @@
class SecurityPatchStateTest {
private val mockContext: Context = mock<Context>()
- private val mockSecurityStateManager: SecurityStateManager = mock<SecurityStateManager> {}
+ private val mockSecurityStateManagerCompat: SecurityStateManagerCompat =
+ mock<SecurityStateManagerCompat> {}
private lateinit var securityState: SecurityPatchState
@Before
fun setup() {
- securityState = SecurityPatchState(mockContext, listOf(), mockSecurityStateManager)
+ securityState = SecurityPatchState(mockContext, listOf(), mockSecurityStateManagerCompat)
}
@Test
@@ -54,11 +56,7 @@
@Test
fun testGetComponentSecurityPatchLevel_withSystemComponent_returnsDateBasedSpl() {
- val spl =
- securityState.getComponentSecurityPatchLevel(
- SecurityPatchState.COMPONENT_SYSTEM,
- "2022-01-01"
- )
+ val spl = getComponentSecurityPatchLevel(SecurityPatchState.COMPONENT_SYSTEM, "2022-01-01")
assertTrue(spl is SecurityPatchState.DateBasedSecurityPatchLevel)
assertEquals("2022-01-01", spl.toString())
}
@@ -66,11 +64,7 @@
@Test
fun testGetComponentSecurityPatchLevel_withVendorComponent_whenVendorIsEnabled_returnsDateBasedSpl() {
SecurityPatchState.Companion.USE_VENDOR_SPL = true
- val spl =
- securityState.getComponentSecurityPatchLevel(
- SecurityPatchState.COMPONENT_VENDOR,
- "2022-01-01"
- )
+ val spl = getComponentSecurityPatchLevel(SecurityPatchState.COMPONENT_VENDOR, "2022-01-01")
assertTrue(spl is SecurityPatchState.DateBasedSecurityPatchLevel)
assertEquals("2022-01-01", spl.toString())
}
@@ -79,48 +73,28 @@
fun testGetComponentSecurityPatchLevel_withVendorComponent_whenVendorIsDisabled_throwsException() {
SecurityPatchState.Companion.USE_VENDOR_SPL = false
- securityState.getComponentSecurityPatchLevel(
- SecurityPatchState.COMPONENT_VENDOR,
- "2022-01-01"
- )
+ getComponentSecurityPatchLevel(SecurityPatchState.COMPONENT_VENDOR, "2022-01-01")
}
@Test
fun testGetComponentSecurityPatchLevel_withKernelComponent_returnsVersionedSpl() {
- val spl =
- securityState.getComponentSecurityPatchLevel(
- SecurityPatchState.COMPONENT_KERNEL,
- "1.2.3.4"
- )
+ val spl = getComponentSecurityPatchLevel(SecurityPatchState.COMPONENT_KERNEL, "1.2.3.4")
assertTrue(spl is SecurityPatchState.VersionedSecurityPatchLevel)
assertEquals("1.2.3.4", spl.toString())
}
@Test(expected = IllegalArgumentException::class)
fun testGetComponentSecurityPatchLevel_withInvalidDateBasedInput_throwsException() {
- securityState.getComponentSecurityPatchLevel(
- SecurityPatchState.COMPONENT_SYSTEM,
- "invalid-date"
- )
+ getComponentSecurityPatchLevel(SecurityPatchState.COMPONENT_SYSTEM, "invalid-date")
}
@Test(expected = IllegalArgumentException::class)
fun testGetComponentSecurityPatchLevel_withInvalidVersionedInput_throwsException() {
- securityState.getComponentSecurityPatchLevel(
- SecurityPatchState.COMPONENT_KERNEL,
- "invalid-version"
- )
- }
-
- @RequiresApi(Build.VERSION_CODES.O)
- @Config(maxSdk = Build.VERSION_CODES.N_MR1)
- @Test(expected = IllegalArgumentException::class)
- fun testGetVulnerabilityReportUrl_withUnsupportedSdk_throwsException() {
- securityState.getVulnerabilityReportUrl(Uri.parse("https://example.com"))
+ getComponentSecurityPatchLevel(SecurityPatchState.COMPONENT_KERNEL, "invalid-version")
}
@Test
- fun testParseVulnerabilityReport_validJson_returnsCorrectData() {
+ fun testLoadVulnerabilityReport_validJson_returnsCorrectData() {
val jsonString =
"""
{
@@ -159,7 +133,7 @@
}
@Test(expected = IllegalArgumentException::class)
- fun testParseVulnerabilityReport_invalidAsb_throwsIllegalArgumentException() {
+ fun testLoadVulnerabilityReport_invalidAsb_throwsIllegalArgumentException() {
val jsonString =
"""
{
@@ -181,21 +155,20 @@
}
@Test(expected = IllegalArgumentException::class)
- fun testParseVulnerabilityReport_invalidJson_throwsIllegalArgumentException() {
+ fun testLoadVulnerabilityReport_invalidJson_throwsIllegalArgumentException() {
val invalidJson = "{ invalid json }"
securityState.loadVulnerabilityReport(invalidJson)
}
@RequiresApi(Build.VERSION_CODES.O)
+ @Config(sdk = [Build.VERSION_CODES.UPSIDE_DOWN_CAKE])
@Test
fun testGetVulnerabilityReportUrl_validSdkVersion_returnsCorrectUrl() {
val sdkVersion = 34 // Android 14
val baseUrl = SecurityPatchState.DEFAULT_VULNERABILITY_REPORTS_URL
- val expectedUrl = "$baseUrl/v1/android_sdk_34.json"
+ val expectedUrl = "$baseUrl/v1/android_sdk_$sdkVersion.json"
- doReturn(sdkVersion).`when`(mockSecurityStateManager).getAndroidSdkInt()
-
- val actualUrl = securityState.getVulnerabilityReportUrl(Uri.parse(baseUrl)).toString()
+ val actualUrl = SecurityPatchState.getVulnerabilityReportUrl(Uri.parse(baseUrl)).toString()
assertEquals(expectedUrl, actualUrl)
}
@@ -205,7 +178,8 @@
val bundle = Bundle()
bundle.putString("system_spl", systemSpl)
- `when`(mockSecurityStateManager.getGlobalSecurityState(anyString())).thenReturn(bundle)
+ `when`(mockSecurityStateManagerCompat.getGlobalSecurityState(anyString()))
+ .thenReturn(bundle)
val spl =
securityState.getDeviceSecurityPatchLevel(SecurityPatchState.COMPONENT_SYSTEM)
@@ -219,8 +193,8 @@
fun testGetDeviceSpl_noSplAvailable_throwsIllegalStateException() {
val bundle = Bundle()
// SPL not set in the bundle for the system component
- doReturn("").`when`(mockSecurityStateManager).getPackageVersion(Mockito.anyString())
- doReturn(bundle).`when`(mockSecurityStateManager).getGlobalSecurityState(anyString())
+ doReturn("").`when`(mockSecurityStateManagerCompat).getPackageVersion(Mockito.anyString())
+ doReturn(bundle).`when`(mockSecurityStateManagerCompat).getGlobalSecurityState(anyString())
securityState.getDeviceSecurityPatchLevel(SecurityPatchState.COMPONENT_SYSTEM)
}
@@ -231,6 +205,18 @@
}
@Test
+ fun testGetPublishedSpl_doesNotThrowWhenVulnerabilityReportLoadedFromConstructor() {
+ securityState =
+ SecurityPatchState(
+ mockContext,
+ listOf(),
+ mockSecurityStateManagerCompat,
+ vulnerabilityReportJsonString = generateMockReport("system", "2023-01-01")
+ )
+ securityState.getPublishedSecurityPatchLevel(SecurityPatchState.COMPONENT_SYSTEM)
+ }
+
+ @Test
fun testGetDeviceSpl_ReturnsCorrectSplForUnpatchedSystemModules() {
val jsonInput =
"""
@@ -262,15 +248,19 @@
securityState.loadVulnerabilityReport(jsonInput)
- `when`(mockSecurityStateManager.getPackageVersion("com.google.android.modulemetadata"))
+ `when`(
+ mockSecurityStateManagerCompat.getPackageVersion(
+ "com.google.android.modulemetadata"
+ )
+ )
.thenReturn("2022-01-01")
- `when`(mockSecurityStateManager.getPackageVersion("com.google.mainline.telemetry"))
+ `when`(mockSecurityStateManagerCompat.getPackageVersion("com.google.mainline.telemetry"))
.thenReturn("2023-05-01")
- `when`(mockSecurityStateManager.getPackageVersion("com.google.mainline.adservices"))
+ `when`(mockSecurityStateManagerCompat.getPackageVersion("com.google.mainline.adservices"))
.thenReturn("2022-05-01")
- `when`(mockSecurityStateManager.getPackageVersion("com.google.mainline.go.primary"))
+ `when`(mockSecurityStateManagerCompat.getPackageVersion("com.google.mainline.go.primary"))
.thenReturn("2021-05-01")
- `when`(mockSecurityStateManager.getPackageVersion("com.google.mainline.go.telemetry"))
+ `when`(mockSecurityStateManagerCompat.getPackageVersion("com.google.mainline.go.telemetry"))
.thenReturn("2024-05-01")
val spl =
@@ -779,8 +769,9 @@
bundle.putString("kernel_version", "5.4.123")
bundle.putString("com.google.android.modulemetadata", "2023-10-05")
- `when`(mockSecurityStateManager.getGlobalSecurityState(anyString())).thenReturn(bundle)
- doReturn("2023-10-05").`when`(mockSecurityStateManager).getPackageVersion(anyString())
+ `when`(mockSecurityStateManagerCompat.getGlobalSecurityState(anyString()))
+ .thenReturn(bundle)
+ doReturn("2023-10-05").`when`(mockSecurityStateManagerCompat).getPackageVersion(anyString())
val jsonInput =
"""
@@ -824,8 +815,9 @@
bundle.putString("kernel_version", "5.4.123")
bundle.putString("com.google.android.modulemetadata", "2023-10-05")
- `when`(mockSecurityStateManager.getGlobalSecurityState(anyString())).thenReturn(bundle)
- doReturn("2023-10-05").`when`(mockSecurityStateManager).getPackageVersion(anyString())
+ `when`(mockSecurityStateManagerCompat.getGlobalSecurityState(anyString()))
+ .thenReturn(bundle)
+ doReturn("2023-10-05").`when`(mockSecurityStateManagerCompat).getPackageVersion(anyString())
val jsonInput =
"""
@@ -869,8 +861,9 @@
bundle.putString("kernel_version", "5.4.123")
bundle.putString("com.google.android.modulemetadata", "2023-10-05")
- `when`(mockSecurityStateManager.getGlobalSecurityState(anyString())).thenReturn(bundle)
- doReturn("2023-10-05").`when`(mockSecurityStateManager).getPackageVersion(anyString())
+ `when`(mockSecurityStateManagerCompat.getGlobalSecurityState(anyString()))
+ .thenReturn(bundle)
+ doReturn("2023-10-05").`when`(mockSecurityStateManagerCompat).getPackageVersion(anyString())
val jsonInput =
"""
@@ -911,8 +904,9 @@
bundle.putString("system_spl", "2022-01-01")
bundle.putString("com.google.android.modulemetadata", "2023-10-05")
- `when`(mockSecurityStateManager.getGlobalSecurityState(anyString())).thenReturn(bundle)
- doReturn("2023-10-05").`when`(mockSecurityStateManager).getPackageVersion(anyString())
+ `when`(mockSecurityStateManagerCompat.getGlobalSecurityState(anyString()))
+ .thenReturn(bundle)
+ doReturn("2023-10-05").`when`(mockSecurityStateManagerCompat).getPackageVersion(anyString())
val jsonInput =
"""
@@ -984,8 +978,11 @@
bundle.putString("vendor_spl", systemSpl)
bundle.putString("com.google.android.modulemetadata", systemSpl)
- `when`(mockSecurityStateManager.getGlobalSecurityState(anyString())).thenReturn(bundle)
- doReturn(systemSpl).`when`(mockSecurityStateManager).getPackageVersion(Mockito.anyString())
+ `when`(mockSecurityStateManagerCompat.getGlobalSecurityState(anyString()))
+ .thenReturn(bundle)
+ doReturn(systemSpl)
+ .`when`(mockSecurityStateManagerCompat)
+ .getPackageVersion(Mockito.anyString())
assertTrue(securityState.areCvesPatched(listOf("CVE-2023-0001", "CVE-2023-0002")))
}
@@ -1027,8 +1024,11 @@
bundle.putString("vendor_spl", systemSpl)
bundle.putString("com.google.android.modulemetadata", systemSpl)
- `when`(mockSecurityStateManager.getGlobalSecurityState(anyString())).thenReturn(bundle)
- doReturn(systemSpl).`when`(mockSecurityStateManager).getPackageVersion(Mockito.anyString())
+ `when`(mockSecurityStateManagerCompat.getGlobalSecurityState(anyString()))
+ .thenReturn(bundle)
+ doReturn(systemSpl)
+ .`when`(mockSecurityStateManagerCompat)
+ .getPackageVersion(Mockito.anyString())
assertFalse(
securityState.areCvesPatched(listOf("CVE-2023-0010", "CVE-2023-0001", "CVE-2023-0002"))
@@ -1072,8 +1072,11 @@
bundle.putString("vendor_spl", systemSpl)
bundle.putString("com.google.android.modulemetadata", systemSpl)
- `when`(mockSecurityStateManager.getGlobalSecurityState(anyString())).thenReturn(bundle)
- doReturn(systemSpl).`when`(mockSecurityStateManager).getPackageVersion(Mockito.anyString())
+ `when`(mockSecurityStateManagerCompat.getGlobalSecurityState(anyString()))
+ .thenReturn(bundle)
+ doReturn(systemSpl)
+ .`when`(mockSecurityStateManagerCompat)
+ .getPackageVersion(Mockito.anyString())
assertFalse(
securityState.areCvesPatched(listOf("CVE-2024-1010", "CVE-2023-0001", "CVE-2023-0002"))
@@ -1118,8 +1121,11 @@
bundle.putString("vendor_spl", systemSpl)
bundle.putString("com.google.android.modulemetadata", systemSpl)
- `when`(mockSecurityStateManager.getGlobalSecurityState(anyString())).thenReturn(bundle)
- doReturn(systemSpl).`when`(mockSecurityStateManager).getPackageVersion(Mockito.anyString())
+ `when`(mockSecurityStateManagerCompat.getGlobalSecurityState(anyString()))
+ .thenReturn(bundle)
+ doReturn(systemSpl)
+ .`when`(mockSecurityStateManagerCompat)
+ .getPackageVersion(Mockito.anyString())
assertTrue(securityState.areCvesPatched(listOf("CVE-2023-0010")))
}
@@ -1162,8 +1168,11 @@
bundle.putString("vendor_spl", systemSpl)
bundle.putString("com.google.android.modulemetadata", systemSpl)
- `when`(mockSecurityStateManager.getGlobalSecurityState(anyString())).thenReturn(bundle)
- doReturn(systemSpl).`when`(mockSecurityStateManager).getPackageVersion(Mockito.anyString())
+ `when`(mockSecurityStateManagerCompat.getGlobalSecurityState(anyString()))
+ .thenReturn(bundle)
+ doReturn(systemSpl)
+ .`when`(mockSecurityStateManagerCompat)
+ .getPackageVersion(Mockito.anyString())
assertFalse(securityState.areCvesPatched(listOf("CVE-2023-0010")))
}
diff --git a/security/security-state/src/test/java/androidx/security/state/SecurityStateManagerTest.kt b/security/security-state/src/test/java/androidx/security/state/SecurityStateManagerCompatTest.kt
similarity index 81%
rename from security/security-state/src/test/java/androidx/security/state/SecurityStateManagerTest.kt
rename to security/security-state/src/test/java/androidx/security/state/SecurityStateManagerCompatTest.kt
index 19c2f1d..961007a 100644
--- a/security/security-state/src/test/java/androidx/security/state/SecurityStateManagerTest.kt
+++ b/security/security-state/src/test/java/androidx/security/state/SecurityStateManagerCompatTest.kt
@@ -34,15 +34,15 @@
import org.robolectric.annotation.Config
@RunWith(JUnit4::class)
-class SecurityStateManagerTest {
+class SecurityStateManagerCompatTest {
private val packageManager: PackageManager = mock<PackageManager>()
private val context: Context = mock<Context>() { on { packageManager } doReturn packageManager }
- private lateinit var securityStateManager: SecurityStateManager
+ private lateinit var securityStateManagerCompat: SecurityStateManagerCompat
@Before
fun setUp() {
- securityStateManager = SecurityStateManager(context)
+ securityStateManagerCompat = SecurityStateManagerCompat(context)
}
@Config(minSdk = Build.VERSION_CODES.Q)
@@ -56,7 +56,7 @@
.thenReturn(PackageInfo().apply { versionName = "" })
val result =
- securityStateManager.getGlobalSecurityState("com.google.android.modulemetadata")
+ securityStateManagerCompat.getGlobalSecurityState("com.google.android.modulemetadata")
assertEquals(
expectedBundle.getString("com.google.android.modulemetadata"),
result.getString("com.google.android.modulemetadata")
@@ -68,7 +68,7 @@
Mockito.`when`(packageManager.getPackageInfo(Mockito.anyString(), Mockito.eq(0)))
.thenThrow(PackageManager.NameNotFoundException())
- val result = securityStateManager.getPackageVersion("non.existent.package")
+ val result = securityStateManagerCompat.getPackageVersion("non.existent.package")
assertTrue(result.isEmpty())
}
@@ -77,25 +77,25 @@
// This method would normally require reading from the file system,
// but we can mock this by pretending the expected output of the file read is known.
val originalKernelVersionMethod =
- securityStateManager::class.java.getDeclaredMethod("getKernelVersion")
+ securityStateManagerCompat::class.java.getDeclaredMethod("getKernelVersion")
originalKernelVersionMethod.isAccessible = true
- val kernelVersion = originalKernelVersionMethod.invoke(securityStateManager) as String
+ val kernelVersion = originalKernelVersionMethod.invoke(securityStateManagerCompat) as String
assertNotNull(kernelVersion)
}
@Test
fun testGetVendorSpl() {
val originalVendorSplMethod =
- securityStateManager::class.java.getDeclaredMethod("getVendorSpl")
+ securityStateManagerCompat::class.java.getDeclaredMethod("getVendorSpl")
originalVendorSplMethod.isAccessible = true
- val vendorSpl = originalVendorSplMethod.invoke(securityStateManager) as String
+ val vendorSpl = originalVendorSplMethod.invoke(securityStateManagerCompat) as String
assertNotNull(vendorSpl)
}
@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
@Test
fun testGetSecurityPatchLevelSafe_API_Level_Below_M() {
- val result = securityStateManager.getSecurityPatchLevelSafe()
+ val result = securityStateManagerCompat.getSecurityPatchLevelSafe()
assertEquals("", result)
}
}
diff --git a/settings.gradle b/settings.gradle
index 22a48cf..6597c4d 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1159,6 +1159,8 @@
includeProject(":work:work-rxjava3", [BuildType.MAIN])
includeProject(":work:work-testing", [BuildType.MAIN])
includeProject(":xr:arcore:arcore", [BuildType.XR])
+includeProject(":xr:arcore:integration-tests:whitebox", [BuildType.XR])
+includeProject(":xr:assets", [BuildType.XR])
includeProject(":xr:compose:compose", [BuildType.XR])
includeProject(":xr:compose:compose-testing", [BuildType.XR])
includeProject(":xr:compose:material3:material3", [BuildType.XR])
diff --git a/sqlite/sqlite-bundled/api/api_lint.ignore b/sqlite/sqlite-bundled/api/api_lint.ignore
deleted file mode 100644
index 1ad4322..0000000
--- a/sqlite/sqlite-bundled/api/api_lint.ignore
+++ /dev/null
@@ -1,5 +0,0 @@
-// Baseline format: 1.0
-AcronymName: androidx.sqlite.driver.bundled.BundledSQLite:
- Acronyms should not be capitalized in class names: was `BundledSQLite`, should this be `BundledSqLite`?
-AcronymName: androidx.sqlite.driver.bundled.BundledSQLiteDriver:
- Acronyms should not be capitalized in class names: was `BundledSQLiteDriver`, should this be `BundledSqLiteDriver`?
diff --git a/sqlite/sqlite-framework/api/api_lint.ignore b/sqlite/sqlite-framework/api/api_lint.ignore
deleted file mode 100644
index 03491a3..0000000
--- a/sqlite/sqlite-framework/api/api_lint.ignore
+++ /dev/null
@@ -1,5 +0,0 @@
-// Baseline format: 1.0
-AcronymName: androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory:
- Acronyms should not be capitalized in class names: was `FrameworkSQLiteOpenHelperFactory`, should this be `FrameworkSqLiteOpenHelperFactory`?
-AcronymName: androidx.sqlite.driver.AndroidSQLiteDriver:
- Acronyms should not be capitalized in class names: was `AndroidSQLiteDriver`, should this be `AndroidSqLiteDriver`?
diff --git a/sqlite/sqlite-ktx/api/api_lint.ignore b/sqlite/sqlite-ktx/api/api_lint.ignore
deleted file mode 100644
index 3948bf5..0000000
--- a/sqlite/sqlite-ktx/api/api_lint.ignore
+++ /dev/null
@@ -1,3 +0,0 @@
-// Baseline format: 1.0
-AcronymName: androidx.sqlite.db.SupportSQLiteDatabaseKt:
- Acronyms should not be capitalized in class names: was `SupportSQLiteDatabaseKt`, should this be `SupportSqLiteDatabaseKt`?
diff --git a/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SimpleSQLiteQuery.android.kt b/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SimpleSQLiteQuery.android.kt
index 1939a45..4bf9c1b 100644
--- a/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SimpleSQLiteQuery.android.kt
+++ b/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SimpleSQLiteQuery.android.kt
@@ -23,7 +23,6 @@
* @param bindArgs The bind argument value that will replace the placeholders in the query.
* @constructor Creates an SQL query with the sql string and the bind arguments.
*/
-@Suppress("AcronymName") // SQL is a known term and should remain capitalized
public class SimpleSQLiteQuery(
private val query: String,
@Suppress("ArrayReturn") // Due to legacy API
@@ -45,7 +44,6 @@
*
* @param [statement] The SQL query to execute. Cannot include bind parameters.
*/
- @Suppress("AcronymName") // SQL is a known term and should remain capitalized
override fun bindTo(statement: SupportSQLiteProgram) {
bind(statement, bindArgs)
}
@@ -62,7 +60,6 @@
*/
@JvmStatic
public fun bind(
- @Suppress("AcronymName") // SQL is a known term and should remain capitalized
statement: SupportSQLiteProgram,
@Suppress("ArrayReturn") // Due to legacy API
bindArgs: Array<out Any?>?
@@ -78,12 +75,7 @@
}
}
- private fun bind(
- @Suppress("AcronymName") // SQL is a known term and should remain capitalized
- statement: SupportSQLiteProgram,
- index: Int,
- arg: Any?
- ) {
+ private fun bind(statement: SupportSQLiteProgram, index: Int, arg: Any?) {
// extracted from android.database.sqlite.SQLiteConnection
if (arg == null) {
statement.bindNull(index)
diff --git a/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteDatabase.android.kt b/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteDatabase.android.kt
index 5c6d9dd..ba6f65c 100644
--- a/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteDatabase.android.kt
+++ b/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteDatabase.android.kt
@@ -29,7 +29,6 @@
* A database abstraction which removes the framework dependency and allows swapping underlying sql
* versions. It mimics the behavior of [android.database.sqlite.SQLiteDatabase]
*/
-@Suppress("AcronymName") // SQL is a known term and should remain capitalized
public interface SupportSQLiteDatabase : Closeable {
/**
* Compiles the given SQL statement.
@@ -250,7 +249,6 @@
public fun yieldIfContendedSafely(sleepAfterYieldDelayMillis: Long): Boolean
/** Is true if [execPerConnectionSQL] is supported by the implementation. */
- @get:Suppress("AcronymName") // To keep consistency with framework method name.
public val isExecPerConnectionSQLSupported: Boolean
get() = false
@@ -275,7 +273,6 @@
* @throws UnsupportedOperationException if this operation is not supported. To check if it
* supported use [isExecPerConnectionSQLSupported]
*/
- @Suppress("AcronymName") // To keep consistency with framework method name.
public fun execPerConnectionSQL(
sql: String,
@SuppressLint("ArrayReturn") bindArgs: Array<out Any?>?
@@ -425,9 +422,7 @@
* not supported.
* @throws SQLException if the SQL string is invalid
*/
- @Suppress("AcronymName") // SQL is a known term and should remain capitalized
- @Throws(SQLException::class)
- public fun execSQL(sql: String)
+ @Throws(SQLException::class) public fun execSQL(sql: String)
/**
* Execute a single SQL statement that does not return any data.
@@ -441,9 +436,7 @@
* @param bindArgs only byte[], String, Long and Double are supported in selectionArgs.
* @throws SQLException if the SQL string is invalid
*/
- @Suppress("AcronymName") // SQL is a known term and should remain capitalized
- @Throws(SQLException::class)
- public fun execSQL(sql: String, bindArgs: Array<out Any?>)
+ @Throws(SQLException::class) public fun execSQL(sql: String, bindArgs: Array<out Any?>)
/** Is true if the database is opened as read only. */
public val isReadOnly: Boolean
diff --git a/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteOpenHelper.android.kt b/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteOpenHelper.android.kt
index 2be3a94..c597f84 100644
--- a/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteOpenHelper.android.kt
+++ b/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteOpenHelper.android.kt
@@ -31,7 +31,6 @@
* that class requires overriding certain methods, support implementation uses [Factory.create] to
* create this and [Callback] to implement the methods that should be overridden.
*/
-@Suppress("AcronymName") // SQL is a known term and should remain capitalized
public interface SupportSQLiteOpenHelper : Closeable {
/**
* Return the name of the SQLite database being opened, as given to the constructor. `null`
diff --git a/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteProgram.android.kt b/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteProgram.android.kt
index 572879d..37965b1 100644
--- a/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteProgram.android.kt
+++ b/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteProgram.android.kt
@@ -18,7 +18,6 @@
import java.io.Closeable
/** An interface to map the behavior of [android.database.sqlite.SQLiteProgram]. */
-@Suppress("AcronymName") // SQL is a known term and should remain capitalized
public interface SupportSQLiteProgram : Closeable {
/**
* Bind a NULL value to this statement. The value remains bound until [.clearBindings] is
diff --git a/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteQuery.android.kt b/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteQuery.android.kt
index 3db42f5..657b85a3 100644
--- a/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteQuery.android.kt
+++ b/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteQuery.android.kt
@@ -19,7 +19,6 @@
* A query with typed bindings. It is better to use this API instead of
* [android.database.sqlite.SQLiteDatabase.rawQuery] because it allows binding type safe parameters.
*/
-@Suppress("AcronymName") // SQL is a known term and should remain capitalized
public interface SupportSQLiteQuery {
/** The SQL query. This query can have placeholders(?) for bind arguments. */
public val sql: String
diff --git a/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteQueryBuilder.android.kt b/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteQueryBuilder.android.kt
index 770820c..49ec1f3 100644
--- a/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteQueryBuilder.android.kt
+++ b/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteQueryBuilder.android.kt
@@ -18,7 +18,6 @@
import java.util.regex.Pattern
/** A simple query builder to create SQL SELECT queries. */
-@Suppress("AcronymName") // SQL is a known term and should remain capitalized
public class SupportSQLiteQueryBuilder private constructor(private val table: String) {
private var distinct = false
private var columns: Array<out String>? = null
diff --git a/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteStatement.android.kt b/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteStatement.android.kt
index a5c36ae..bf1bca3 100644
--- a/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteStatement.android.kt
+++ b/sqlite/sqlite/src/androidMain/kotlin/androidx/sqlite/db/SupportSQLiteStatement.android.kt
@@ -16,7 +16,6 @@
package androidx.sqlite.db
/** An interface to map the behavior of [android.database.sqlite.SQLiteStatement]. */
-@Suppress("AcronymName") // SQL is a known term and should remain capitalized
public interface SupportSQLiteStatement : SupportSQLiteProgram {
/**
* Execute this SQL statement, if it is not a SELECT / INSERT / DELETE / UPDATE, for example
diff --git a/sqlite/sqlite/src/commonMain/kotlin/androidx/sqlite/SQLite.kt b/sqlite/sqlite/src/commonMain/kotlin/androidx/sqlite/SQLite.kt
index 9b63cba..653b597 100644
--- a/sqlite/sqlite/src/commonMain/kotlin/androidx/sqlite/SQLite.kt
+++ b/sqlite/sqlite/src/commonMain/kotlin/androidx/sqlite/SQLite.kt
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-@file:Suppress("AcronymName") // SQL is a known term and should remain capitalized
@file:JvmName("SQLite")
package androidx.sqlite
@@ -53,13 +52,11 @@
public annotation class DataType
/** Executes a single SQL statement that returns no values. */
-@Suppress("AcronymName") // SQL is a known term and should remain capitalized
public fun SQLiteConnection.execSQL(sql: String) {
prepare(sql).use { it.step() }
}
/** Throws a [SQLiteException] with its message formed by the given [errorCode] amd [errorMsg]. */
-@Suppress("AcronymName") // SQL is a known term and should remain capitalized
public fun throwSQLiteException(errorCode: Int, errorMsg: String?): Nothing {
val message = buildString {
append("Error code: $errorCode")
diff --git a/sqlite/sqlite/src/commonMain/kotlin/androidx/sqlite/SQLiteConnection.kt b/sqlite/sqlite/src/commonMain/kotlin/androidx/sqlite/SQLiteConnection.kt
index 14b76d4..e988034 100644
--- a/sqlite/sqlite/src/commonMain/kotlin/androidx/sqlite/SQLiteConnection.kt
+++ b/sqlite/sqlite/src/commonMain/kotlin/androidx/sqlite/SQLiteConnection.kt
@@ -24,7 +24,7 @@
*
* See also [Database Connection](https://www.sqlite.org/c3ref/sqlite3.html)
*/
-@Suppress("NotCloseable", "AcronymName") // SQL is a known term and should remain capitalized
+@Suppress("NotCloseable")
public interface SQLiteConnection : AutoCloseable {
/**
* Prepares a new SQL statement.
diff --git a/sqlite/sqlite/src/commonMain/kotlin/androidx/sqlite/SQLiteDriver.kt b/sqlite/sqlite/src/commonMain/kotlin/androidx/sqlite/SQLiteDriver.kt
index 5cb23bf..a1f3e50 100644
--- a/sqlite/sqlite/src/commonMain/kotlin/androidx/sqlite/SQLiteDriver.kt
+++ b/sqlite/sqlite/src/commonMain/kotlin/androidx/sqlite/SQLiteDriver.kt
@@ -17,7 +17,6 @@
package androidx.sqlite
/** An interface to open database connections. */
-@Suppress("AcronymName") // SQL is a known term and should remain capitalized
public interface SQLiteDriver {
/**
* Opens a new database connection.
diff --git a/sqlite/sqlite/src/commonMain/kotlin/androidx/sqlite/SQLiteStatement.kt b/sqlite/sqlite/src/commonMain/kotlin/androidx/sqlite/SQLiteStatement.kt
index 7f42c8b..8b319b9 100644
--- a/sqlite/sqlite/src/commonMain/kotlin/androidx/sqlite/SQLiteStatement.kt
+++ b/sqlite/sqlite/src/commonMain/kotlin/androidx/sqlite/SQLiteStatement.kt
@@ -26,7 +26,7 @@
*
* See also [Prepared Statement](https://www.sqlite.org/c3ref/stmt.html)
*/
-@Suppress("NotCloseable", "AcronymName") // SQL is a known term and should remain capitalized
+@Suppress("NotCloseable")
public interface SQLiteStatement : AutoCloseable {
/**
* Binds a ByteArray value to this statement at an index.
diff --git a/tvprovider/tvprovider/build.gradle b/tvprovider/tvprovider/build.gradle
index b3db8a6..cfe957e 100644
--- a/tvprovider/tvprovider/build.gradle
+++ b/tvprovider/tvprovider/build.gradle
@@ -13,6 +13,7 @@
}
dependencies {
+ api(libs.jspecify)
api("androidx.annotation:annotation:1.8.1")
api("androidx.core:core:1.1.0")
@@ -33,6 +34,4 @@
inceptionYear = "2017"
description = "Android Support Library for TV Provider"
failOnDeprecationWarnings = false
- // TODO: b/326456246
- optOutJSpecify = true
}
diff --git a/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/BaseProgram.java b/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/BaseProgram.java
index f387eab..c61fda7 100644
--- a/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/BaseProgram.java
+++ b/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/BaseProgram.java
@@ -24,14 +24,15 @@
import android.os.Build;
import androidx.annotation.IntDef;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.tvprovider.media.tv.TvContractCompat.BaseTvColumns;
import androidx.tvprovider.media.tv.TvContractCompat.ProgramColumns;
import androidx.tvprovider.media.tv.TvContractCompat.Programs;
import androidx.tvprovider.media.tv.TvContractCompat.Programs.Genres.Genre;
+import org.jspecify.annotations.NonNull;
+import org.jspecify.annotations.Nullable;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -291,8 +292,7 @@
* @return The series ID for the program.
* @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_SERIES_ID
*/
- @Nullable
- public String getSeriesId() {
+ public @Nullable String getSeriesId() {
return mValues.getAsString(Programs.COLUMN_SERIES_ID);
}
@@ -918,8 +918,7 @@
* @return This Builder object to allow for chaining of calls to builder methods.
* @see androidx.tvprovider.media.tv.TvContractCompat.Programs#COLUMN_SERIES_ID
*/
- @NonNull
- public T setSeriesId(@Nullable String seriesId) {
+ public @NonNull T setSeriesId(@Nullable String seriesId) {
mValues.put(ProgramColumns.COLUMN_SERIES_ID, seriesId);
return (T) this;
}
diff --git a/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/Channel.java b/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/Channel.java
index ec1b720..2417f7a 100644
--- a/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/Channel.java
+++ b/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/Channel.java
@@ -23,13 +23,14 @@
import android.net.Uri;
import android.os.Build;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.tvprovider.media.tv.TvContractCompat.Channels;
import androidx.tvprovider.media.tv.TvContractCompat.Channels.ServiceType;
import androidx.tvprovider.media.tv.TvContractCompat.Channels.VideoFormat;
+import org.jspecify.annotations.NonNull;
+import org.jspecify.annotations.Nullable;
+
import java.net.URISyntaxException;
import java.nio.charset.Charset;
@@ -334,8 +335,7 @@
/**
* @return The value of {@link Channels#COLUMN_GLOBAL_CONTENT_ID} for the channel.
*/
- @Nullable
- public String getGlobalContentId() {
+ public @Nullable String getGlobalContentId() {
return mValues.getAsString(Channels.COLUMN_GLOBAL_CONTENT_ID);
}
@@ -1010,8 +1010,7 @@
* @param value The value of {@link Channels#COLUMN_GLOBAL_CONTENT_ID} for the channel.
* @return This Builder object to allow for chaining of calls to builder methods.
*/
- @NonNull
- public Builder setGlobalContentId(@NonNull String value) {
+ public @NonNull Builder setGlobalContentId(@NonNull String value) {
mValues.put(Channels.COLUMN_GLOBAL_CONTENT_ID, value);
return this;
}
diff --git a/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/ChannelLogoUtils.java b/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/ChannelLogoUtils.java
index 158e499..e789155 100644
--- a/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/ChannelLogoUtils.java
+++ b/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/ChannelLogoUtils.java
@@ -26,11 +26,11 @@
import android.net.Uri;
import android.util.Log;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
-import java.io.FileNotFoundException;
+import org.jspecify.annotations.NonNull;
+import org.jspecify.annotations.Nullable;
+
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -151,10 +151,9 @@
* @see #storeChannelLogo(Context, long, Uri)
* @see #storeChannelLogo(Context, long, Bitmap)
*/
- @Nullable
@WorkerThread
@SuppressLint("WrongThread") // TODO https://issuetracker.google.com/issues/116776070
- public static Bitmap loadChannelLogo(@NonNull Context context, long channelId) {
+ public static @Nullable Bitmap loadChannelLogo(@NonNull Context context, long channelId) {
Bitmap channelLogo = null;
Uri logoUri = TvContract.buildChannelLogoUri(channelId);
try (InputStream is = context.getContentResolver().openInputStream(logoUri)) {
diff --git a/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/PreviewChannel.java b/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/PreviewChannel.java
index 26ad697..3a94489 100644
--- a/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/PreviewChannel.java
+++ b/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/PreviewChannel.java
@@ -29,12 +29,13 @@
import android.text.TextUtils;
import android.util.Log;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.annotation.WorkerThread;
import androidx.tvprovider.media.tv.TvContractCompat.Channels;
+import org.jspecify.annotations.NonNull;
+import org.jspecify.annotations.Nullable;
+
import java.io.FileNotFoundException;
import java.net.URISyntaxException;
import java.util.Objects;
@@ -397,8 +398,7 @@
* @return This Builder object to allow for chaining of calls to builder methods.
* @see TvContractCompat.Channels#COLUMN_DESCRIPTION
*/
- @NonNull
- public Builder setDescription(@Nullable CharSequence description) {
+ public @NonNull Builder setDescription(@Nullable CharSequence description) {
if (description == null) {
mValues.remove(Channels.COLUMN_DESCRIPTION);
} else {
diff --git a/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/PreviewChannelHelper.java b/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/PreviewChannelHelper.java
index 93bdf2a..a23e685 100644
--- a/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/PreviewChannelHelper.java
+++ b/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/PreviewChannelHelper.java
@@ -30,11 +30,12 @@
import android.text.format.DateUtils;
import android.util.Log;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.annotation.WorkerThread;
+import org.jspecify.annotations.NonNull;
+import org.jspecify.annotations.Nullable;
+
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -222,8 +223,7 @@
* @param channelId ID of preview channel in TvProvider
* @return PreviewChannel or null if not found
*/
- @Nullable
- public PreviewChannel getPreviewChannel(long channelId) {
+ public @Nullable PreviewChannel getPreviewChannel(long channelId) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
return null;
@@ -415,8 +415,7 @@
/**
* Retrieves a single preview program from the system content provider (aka TvProvider).
*/
- @Nullable
- public PreviewProgram getPreviewProgram(long programId) {
+ public @Nullable PreviewProgram getPreviewProgram(long programId) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
return null;
@@ -494,8 +493,7 @@
/**
* Retrieves a single WatchNext program from the system content provider (aka TvProvider).
*/
- @Nullable
- public WatchNextProgram getWatchNextProgram(long programId) {
+ public @Nullable WatchNextProgram getWatchNextProgram(long programId) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
return null;
diff --git a/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/PreviewProgram.java b/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/PreviewProgram.java
index 2d03500..682efdb 100644
--- a/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/PreviewProgram.java
+++ b/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/PreviewProgram.java
@@ -21,10 +21,11 @@
import android.database.Cursor;
import android.os.Build;
-import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
import androidx.tvprovider.media.tv.TvContractCompat.PreviewPrograms;
+import org.jspecify.annotations.NonNull;
+
import java.util.Objects;
import java.util.Set;
@@ -83,8 +84,7 @@
* query and in creating a Cursor object, which is used to iterate through the rows in the
* table.
*/
- @NonNull
- public static final String[] PROJECTION = getProjection();
+ public static final String @NonNull [] PROJECTION = getProjection();
private static final long INVALID_LONG_VALUE = -1;
private static final int INVALID_INT_VALUE = -1;
diff --git a/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/Program.java b/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/Program.java
index a9ba201..1892f75 100644
--- a/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/Program.java
+++ b/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/Program.java
@@ -21,12 +21,13 @@
import android.database.Cursor;
import android.os.Build;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.tvprovider.media.tv.TvContractCompat.Programs;
import androidx.tvprovider.media.tv.TvContractCompat.Programs.Genres.Genre;
+import org.jspecify.annotations.NonNull;
+import org.jspecify.annotations.Nullable;
+
/**
* A convenience class to access {@link TvContractCompat.Programs} entries in the system content
* provider.
@@ -141,8 +142,7 @@
*
* <p>No-op on devices prior to {@link android.os.Build.VERSION_CODES#R}.
*/
- @Nullable
- public String getGlobalContentId() {
+ public @Nullable String getGlobalContentId() {
return mValues.getAsString(Programs.COLUMN_GLOBAL_CONTENT_ID);
}
@@ -351,8 +351,7 @@
* @param eventId The value of {@link Programs#COLUMN_EVENT_ID} for the program.
* @return This Builder object to allow for chaining of calls to builder methods.
*/
- @NonNull
- public Builder setEventId(int eventId) {
+ public @NonNull Builder setEventId(int eventId) {
mValues.put(Programs.COLUMN_EVENT_ID, eventId);
return this;
}
@@ -366,8 +365,7 @@
* program.
* @return This Builder object to allow for chaining of calls to builder methods.
*/
- @NonNull
- public Builder setGlobalContentId(@Nullable String globalContentId) {
+ public @NonNull Builder setGlobalContentId(@Nullable String globalContentId) {
mValues.put(Programs.COLUMN_GLOBAL_CONTENT_ID, globalContentId);
return this;
}
diff --git a/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/TvContractCompat.java b/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/TvContractCompat.java
index 4bf7e1f..2bfb681 100644
--- a/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/TvContractCompat.java
+++ b/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/TvContractCompat.java
@@ -32,12 +32,13 @@
import android.provider.BaseColumns;
import android.text.TextUtils;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.annotation.StringDef;
import androidx.tvprovider.media.tv.TvContractCompat.Programs.Genres;
+import org.jspecify.annotations.NonNull;
+import org.jspecify.annotations.Nullable;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -1874,8 +1875,7 @@
* is not defined for the given video format.
* @see #COLUMN_VIDEO_FORMAT
*/
- @Nullable
- public static String getVideoResolution(@VideoFormat String videoFormat) {
+ public static @Nullable String getVideoResolution(@VideoFormat String videoFormat) {
return VIDEO_FORMAT_TO_RESOLUTION_MAP.get(videoFormat);
}
@@ -2636,7 +2636,7 @@
* @return an encoded genre string that can be inserted into the
* {@link #COLUMN_BROADCAST_GENRE} or {@link #COLUMN_CANONICAL_GENRE} column.
*/
- public static String encode(@NonNull @Genre String... genres) {
+ public static String encode(@Genre String @NonNull ... genres) {
if (genres == null) {
// MNC and before will throw a NPE.
return null;
diff --git a/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/WatchNextProgram.java b/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/WatchNextProgram.java
index ad00cd6..58013e8 100644
--- a/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/WatchNextProgram.java
+++ b/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/WatchNextProgram.java
@@ -22,10 +22,11 @@
import android.os.Build;
import androidx.annotation.IntDef;
-import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
import androidx.tvprovider.media.tv.TvContractCompat.WatchNextPrograms;
+import org.jspecify.annotations.NonNull;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
@@ -86,8 +87,7 @@
* query and in creating a Cursor object, which is used to iterate through the rows in the
* table.
*/
- @NonNull
- public static final String[] PROJECTION = getProjection();
+ public static final String @NonNull [] PROJECTION = getProjection();
private static final long INVALID_LONG_VALUE = -1;
diff --git a/wear/compose/compose-material3/api/current.txt b/wear/compose/compose-material3/api/current.txt
index 1fb47dc..ca1d072 100644
--- a/wear/compose/compose-material3/api/current.txt
+++ b/wear/compose/compose-material3/api/current.txt
@@ -1650,12 +1650,10 @@
}
public final class TimeTextDefaults {
- method public float getAutoTextWeight();
method public androidx.compose.foundation.layout.PaddingValues getContentPadding();
method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.TimeSource rememberTimeSource(String timeFormat);
method @androidx.compose.runtime.Composable public String timeFormat();
- method @androidx.compose.runtime.Composable public androidx.compose.ui.text.TextStyle timeTextStyle(optional long background, optional long color, optional long fontSize);
- property public final float AutoTextWeight;
+ method @androidx.compose.runtime.Composable public androidx.wear.compose.foundation.CurvedTextStyle timeTextStyle(optional long background, optional long color, optional long fontSize);
property public final androidx.compose.foundation.layout.PaddingValues ContentPadding;
property public static final float MaxSweepAngle;
property public static final String TimeFormat12Hours;
@@ -1667,14 +1665,9 @@
}
public final class TimeTextKt {
- method @androidx.compose.runtime.Composable public static void TimeText(optional androidx.compose.ui.Modifier modifier, optional androidx.wear.compose.foundation.CurvedModifier curvedModifier, optional float maxSweepAngle, optional androidx.wear.compose.material3.TimeSource timeSource, optional androidx.compose.ui.text.TextStyle timeTextStyle, optional long contentColor, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.material3.TimeTextScope,kotlin.Unit> content);
- }
-
- public abstract sealed class TimeTextScope {
- method public abstract void composable(kotlin.jvm.functions.Function0<kotlin.Unit> content);
- method public abstract void separator(optional androidx.compose.ui.text.TextStyle? style);
- method public abstract void text(String text, optional androidx.compose.ui.text.TextStyle? style, optional float weight);
- method public abstract void time();
+ method @androidx.compose.runtime.Composable public static void TimeText(optional androidx.compose.ui.Modifier modifier, optional androidx.wear.compose.foundation.CurvedModifier curvedModifier, optional float maxSweepAngle, optional androidx.wear.compose.material3.TimeSource timeSource, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional kotlin.jvm.functions.Function2<? super androidx.wear.compose.foundation.CurvedScope,? super java.lang.String,kotlin.Unit> content);
+ method public static void timeTextCurvedText(androidx.wear.compose.foundation.CurvedScope, String time, optional androidx.wear.compose.foundation.CurvedTextStyle? style);
+ method public static void timeTextSeparator(androidx.wear.compose.foundation.CurvedScope, optional androidx.wear.compose.foundation.CurvedTextStyle? curvedTextStyle, optional androidx.wear.compose.foundation.ArcPaddingValues contentArcPadding);
}
public final class TouchTargetAwareSizeKt {
diff --git a/wear/compose/compose-material3/api/restricted_current.txt b/wear/compose/compose-material3/api/restricted_current.txt
index 1fb47dc..ca1d072 100644
--- a/wear/compose/compose-material3/api/restricted_current.txt
+++ b/wear/compose/compose-material3/api/restricted_current.txt
@@ -1650,12 +1650,10 @@
}
public final class TimeTextDefaults {
- method public float getAutoTextWeight();
method public androidx.compose.foundation.layout.PaddingValues getContentPadding();
method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.TimeSource rememberTimeSource(String timeFormat);
method @androidx.compose.runtime.Composable public String timeFormat();
- method @androidx.compose.runtime.Composable public androidx.compose.ui.text.TextStyle timeTextStyle(optional long background, optional long color, optional long fontSize);
- property public final float AutoTextWeight;
+ method @androidx.compose.runtime.Composable public androidx.wear.compose.foundation.CurvedTextStyle timeTextStyle(optional long background, optional long color, optional long fontSize);
property public final androidx.compose.foundation.layout.PaddingValues ContentPadding;
property public static final float MaxSweepAngle;
property public static final String TimeFormat12Hours;
@@ -1667,14 +1665,9 @@
}
public final class TimeTextKt {
- method @androidx.compose.runtime.Composable public static void TimeText(optional androidx.compose.ui.Modifier modifier, optional androidx.wear.compose.foundation.CurvedModifier curvedModifier, optional float maxSweepAngle, optional androidx.wear.compose.material3.TimeSource timeSource, optional androidx.compose.ui.text.TextStyle timeTextStyle, optional long contentColor, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.material3.TimeTextScope,kotlin.Unit> content);
- }
-
- public abstract sealed class TimeTextScope {
- method public abstract void composable(kotlin.jvm.functions.Function0<kotlin.Unit> content);
- method public abstract void separator(optional androidx.compose.ui.text.TextStyle? style);
- method public abstract void text(String text, optional androidx.compose.ui.text.TextStyle? style, optional float weight);
- method public abstract void time();
+ method @androidx.compose.runtime.Composable public static void TimeText(optional androidx.compose.ui.Modifier modifier, optional androidx.wear.compose.foundation.CurvedModifier curvedModifier, optional float maxSweepAngle, optional androidx.wear.compose.material3.TimeSource timeSource, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional kotlin.jvm.functions.Function2<? super androidx.wear.compose.foundation.CurvedScope,? super java.lang.String,kotlin.Unit> content);
+ method public static void timeTextCurvedText(androidx.wear.compose.foundation.CurvedScope, String time, optional androidx.wear.compose.foundation.CurvedTextStyle? style);
+ method public static void timeTextSeparator(androidx.wear.compose.foundation.CurvedScope, optional androidx.wear.compose.foundation.CurvedTextStyle? curvedTextStyle, optional androidx.wear.compose.foundation.ArcPaddingValues contentArcPadding);
}
public final class TouchTargetAwareSizeKt {
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ScrollAwayDemos.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ScrollAwayDemos.kt
index ce9aa66..9b3d8c3 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ScrollAwayDemos.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/ScrollAwayDemos.kt
@@ -41,6 +41,7 @@
import androidx.wear.compose.material3.ScreenStage
import androidx.wear.compose.material3.Text
import androidx.wear.compose.material3.TimeText
+import androidx.wear.compose.material3.curvedText
import androidx.wear.compose.material3.samples.ScrollAwaySample
import androidx.wear.compose.material3.scrollAway
@@ -100,7 +101,7 @@
else ScreenStage.Idle
}
),
- content = { text("ScrollAway") }
+ content = { curvedText("ScrollAway") }
)
}
}
@@ -151,7 +152,7 @@
else ScreenStage.Idle
}
),
- content = { text("ScrollAway") }
+ content = { curvedText("ScrollAway") }
)
}
}
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/TimeTextDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/TimeTextDemo.kt
index 397f24d..d53c350 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/TimeTextDemo.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/TimeTextDemo.kt
@@ -33,6 +33,7 @@
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
+import androidx.wear.compose.foundation.curvedComposable
import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
import androidx.wear.compose.integration.demos.common.ComposableDemo
import androidx.wear.compose.material3.Button
@@ -43,13 +44,17 @@
import androidx.wear.compose.material3.Text
import androidx.wear.compose.material3.TimeText
import androidx.wear.compose.material3.TimeTextDefaults
+import androidx.wear.compose.material3.curvedText
import androidx.wear.compose.material3.samples.TimeTextClockOnly
import androidx.wear.compose.material3.samples.TimeTextWithStatus
+import androidx.wear.compose.material3.samples.TimeTextWithStatusEllipsized
+import androidx.wear.compose.material3.timeTextSeparator
val TimeTextDemos =
listOf(
ComposableDemo("Clock only") { TimeTextClockOnly() },
ComposableDemo("Clock with Status") { TimeTextWithStatus() },
+ ComposableDemo("Clock with Ellipsized Status") { TimeTextWithStatusEllipsized() },
ComposableDemo("Clock with long Status") { TimeTextWithLongStatus() },
ComposableDemo("Clock with Icon") { TimeTextWithIcon() },
ComposableDemo("Clock with custom colors") { TimeTextWithCustomColors() },
@@ -61,10 +66,10 @@
@Composable
fun TimeTextWithLongStatus() {
- TimeText {
- text("Some long leading text")
- separator()
- time()
+ TimeText { time ->
+ curvedText("Some long leading text")
+ timeTextSeparator()
+ curvedText(time)
}
}
@@ -72,12 +77,12 @@
fun TimeTextWithCustomColors() {
val customStyle = TimeTextDefaults.timeTextStyle(color = Color.Red)
- TimeText {
- text("ETA", customStyle)
- composable { Spacer(modifier = Modifier.size(4.dp)) }
- text("12:48")
- separator()
- time()
+ TimeText { time ->
+ curvedText("ETA", style = customStyle)
+ curvedComposable { Spacer(modifier = Modifier.size(4.dp)) }
+ curvedText("12:48")
+ timeTextSeparator()
+ curvedText(time)
}
}
@@ -85,19 +90,19 @@
fun TimeTextCustomSize() {
val customStyle = TimeTextDefaults.timeTextStyle(color = Color.Green, fontSize = 24.sp)
- TimeText {
- text("ETA", customStyle)
- separator()
- time()
+ TimeText { time ->
+ curvedText("ETA", style = customStyle)
+ timeTextSeparator()
+ curvedText(time)
}
}
@Composable
fun TimeTextWithIcon() {
- TimeText {
- time()
- separator()
- composable {
+ TimeText { time ->
+ curvedText(time)
+ timeTextSeparator()
+ curvedComposable {
Icon(
imageVector = Icons.Filled.Favorite,
contentDescription = "Favorite",
@@ -151,13 +156,13 @@
items(10) { Text("Some extra items ($it) to scroll", Modifier.padding(5.dp)) }
}
// Timetext later so it's on top.
- TimeText { time() }
+ TimeText { time -> curvedText(time) }
}
}
@Composable
fun TimeTextOnScreenWhiteBackground() {
- Box(Modifier.fillMaxSize().background(Color.White)) { TimeText { time() } }
+ Box(Modifier.fillMaxSize().background(Color.White)) { TimeText { time -> curvedText(time) } }
}
@Composable
@@ -166,7 +171,7 @@
MaterialTheme(
colorScheme = MaterialTheme.colorScheme.copy(background = Color.Transparent)
) {
- TimeText { time() }
+ TimeText { time -> curvedText(time) }
}
}
}
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/DatePickerBenchmark.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/DatePickerBenchmark.kt
index 51431d0..fe612b2 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/DatePickerBenchmark.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/DatePickerBenchmark.kt
@@ -84,13 +84,8 @@
device.waitForIdle()
SystemClock.sleep(500)
repeat(3) { columnIndex ->
- repeat(2) { i ->
- val endY =
- if (i % 2 == 0) {
- device.displayHeight / 10 // scroll up
- } else {
- device.displayHeight * 9 / 10 // scroll down
- }
+ repeat(20) {
+ val endY = device.displayHeight * 9 / 10 // scroll down
device.swipe(
device.displayWidth / 2,
device.displayHeight / 2,
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/PickerBenchmark.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/PickerBenchmark.kt
index 280b461..5e89683 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/PickerBenchmark.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/PickerBenchmark.kt
@@ -45,13 +45,8 @@
override val exercise: MacrobenchmarkScope.() -> Unit
get() = {
- repeat(4) { i ->
- val endY =
- if (i % 2 == 0) {
- device.displayHeight / 10 // scroll up
- } else {
- device.displayHeight * 9 / 10 // scroll down
- }
+ repeat(20) {
+ val endY = device.displayHeight * 9 / 10 // scroll down
device.swipe(
device.displayWidth / 2,
device.displayHeight / 2,
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/ScalingLazyColumnBenchmark.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/ScalingLazyColumnBenchmark.kt
new file mode 100644
index 0000000..37f0dab
--- /dev/null
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/ScalingLazyColumnBenchmark.kt
@@ -0,0 +1,97 @@
+/*
+ * 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.wear.compose.material3.macrobenchmark.common
+
+import android.os.SystemClock
+import androidx.benchmark.macro.MacrobenchmarkScope
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
+import androidx.wear.compose.foundation.lazy.rememberScalingLazyListState
+import androidx.wear.compose.material3.AppScaffold
+import androidx.wear.compose.material3.EdgeButton
+import androidx.wear.compose.material3.MaterialTheme
+import androidx.wear.compose.material3.ScreenScaffold
+import androidx.wear.compose.material3.Text
+import kotlinx.coroutines.launch
+
+val ScalingLazyColumnBenchmark =
+ object : MacrobenchmarkScreen {
+ override val content: @Composable (BoxScope.() -> Unit)
+ get() = {
+ val state = rememberScalingLazyListState()
+ val coroutineScope = rememberCoroutineScope()
+ AppScaffold {
+ ScreenScaffold(
+ state,
+ contentPadding = PaddingValues(horizontal = 10.dp, vertical = 20.dp),
+ edgeButton = {
+ EdgeButton(
+ onClick = { coroutineScope.launch { state.scrollToItem(1) } }
+ ) {
+ Text("To top")
+ }
+ }
+ ) { contentPadding ->
+ ScalingLazyColumn(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ contentPadding = contentPadding,
+ state = state,
+ modifier =
+ Modifier.background(MaterialTheme.colorScheme.background)
+ .semantics { contentDescription = CONTENT_DESCRIPTION }
+ ) {
+ items(5000) {
+ Text(
+ "Item $it",
+ color = MaterialTheme.colorScheme.onSurface,
+ style = MaterialTheme.typography.bodyLarge,
+ modifier = Modifier.fillMaxWidth().padding(10.dp)
+ )
+ }
+ }
+ }
+ }
+ }
+
+ override val exercise: MacrobenchmarkScope.() -> Unit
+ get() = {
+ repeat(20) {
+ val endY = device.displayHeight * 9 / 10 // scroll down
+
+ device.swipe(
+ device.displayWidth / 2,
+ device.displayHeight / 2,
+ device.displayWidth / 2,
+ endY,
+ 10
+ )
+ device.waitForIdle()
+ SystemClock.sleep(500)
+ }
+ }
+ }
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/TimePickerBenchmark.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/TimePickerBenchmark.kt
index 7d15ce7..aad5c48 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/TimePickerBenchmark.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/TimePickerBenchmark.kt
@@ -39,14 +39,9 @@
override val exercise: MacrobenchmarkScope.() -> Unit
get() = {
- repeat(4) { i ->
+ repeat(20) {
val startY = device.displayHeight / 2
- val endY =
- if (i % 2 == 0) {
- device.displayHeight / 10 // scroll up
- } else {
- device.displayHeight * 9 / 10 // scroll down
- }
+ val endY = device.displayHeight * 9 / 10 // scroll down
val hourX = device.displayWidth / 4
device.swipe(hourX, startY, hourX, endY, 10)
diff --git a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/TransformingLazyColumnBenchmark.kt b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/TransformingLazyColumnBenchmark.kt
index bc24b82..dae1ab0 100644
--- a/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/TransformingLazyColumnBenchmark.kt
+++ b/wear/compose/compose-material3/macrobenchmark-common/src/main/java/androidx/wear/compose/material3/macrobenchmark/common/TransformingLazyColumnBenchmark.kt
@@ -16,7 +16,7 @@
package androidx.wear.compose.material3.macrobenchmark.common
-import android.graphics.Point
+import android.os.SystemClock
import androidx.benchmark.macro.MacrobenchmarkScope
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.BoxScope
@@ -29,7 +29,6 @@
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
-import androidx.test.uiautomator.By
import androidx.wear.compose.foundation.lazy.TransformingLazyColumn
import androidx.wear.compose.foundation.lazy.rememberTransformingLazyColumnState
import androidx.wear.compose.material3.AppScaffold
@@ -89,12 +88,17 @@
override val exercise: MacrobenchmarkScope.() -> Unit
get() = {
- val list = device.findObject(By.desc(CONTENT_DESCRIPTION))
- // Setting a gesture margin is important otherwise gesture nav is triggered.
- list.setGestureMargin(device.displayWidth / 5)
- repeat(5) {
- list.drag(Point(list.visibleCenter.x, list.visibleCenter.y / 3))
+ repeat(20) {
+ val endY = device.displayHeight * 9 / 10 // scroll down
+ device.swipe(
+ device.displayWidth / 2,
+ device.displayHeight / 2,
+ device.displayWidth / 2,
+ endY,
+ 10
+ )
device.waitForIdle()
+ SystemClock.sleep(500)
}
}
}
diff --git a/wear/compose/compose-material3/macrobenchmark-target/src/main/AndroidManifest.xml b/wear/compose/compose-material3/macrobenchmark-target/src/main/AndroidManifest.xml
index 6ec1884..0127c6d 100644
--- a/wear/compose/compose-material3/macrobenchmark-target/src/main/AndroidManifest.xml
+++ b/wear/compose/compose-material3/macrobenchmark-target/src/main/AndroidManifest.xml
@@ -225,6 +225,17 @@
</activity>
<activity
+ android:name=".ScalingLazyColumnActivity"
+ android:theme="@style/AppTheme"
+ android:exported="true">
+ <intent-filter>
+ <action android:name=
+ "androidx.wear.compose.material3.macrobenchmark.target.SCALING_LAZY_COLUMN_ACTIVITY" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
+ <activity
android:name=".SliderActivity"
android:theme="@style/AppTheme"
android:exported="true">
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/test/TestEnum.java b/wear/compose/compose-material3/macrobenchmark-target/src/main/java/androidx/wear/compose/material3/macrobenchmark/target/ScalingLazyColumnActivity.kt
similarity index 65%
copy from navigation/navigation-runtime/src/androidTest/java/androidx/navigation/test/TestEnum.java
copy to wear/compose/compose-material3/macrobenchmark-target/src/main/java/androidx/wear/compose/material3/macrobenchmark/target/ScalingLazyColumnActivity.kt
index f3a5276..0fafcad 100644
--- a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/test/TestEnum.java
+++ b/wear/compose/compose-material3/macrobenchmark-target/src/main/java/androidx/wear/compose/material3/macrobenchmark/target/ScalingLazyColumnActivity.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 The Android Open Source Project
+ * 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.
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-package androidx.navigation.test;
+package androidx.wear.compose.material3.macrobenchmark.target
-public enum TestEnum {
- VALUE_ONE, VALUE_TWO
-}
+import androidx.wear.compose.material3.macrobenchmark.common.ScalingLazyColumnBenchmark
+
+class ScalingLazyColumnActivity : BenchmarkBaseActivity(ScalingLazyColumnBenchmark)
diff --git a/wear/compose/compose-material3/macrobenchmark/src/main/java/androidx/wear/compose/material3/macrobenchmark/ScalingLazyColumnBenchmarkTest.kt b/wear/compose/compose-material3/macrobenchmark/src/main/java/androidx/wear/compose/material3/macrobenchmark/ScalingLazyColumnBenchmarkTest.kt
new file mode 100644
index 0000000..d3b0172
--- /dev/null
+++ b/wear/compose/compose-material3/macrobenchmark/src/main/java/androidx/wear/compose/material3/macrobenchmark/ScalingLazyColumnBenchmarkTest.kt
@@ -0,0 +1,32 @@
+/*
+ * 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.wear.compose.material3.macrobenchmark
+
+import androidx.benchmark.macro.CompilationMode
+import androidx.test.filters.LargeTest
+import androidx.wear.compose.material3.macrobenchmark.common.ScalingLazyColumnBenchmark
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@LargeTest
+@RunWith(Parameterized::class)
+class ScalingLazyColumnBenchmarkTest(compilationMode: CompilationMode) :
+ BenchmarkTestBase(
+ compilationMode = compilationMode,
+ macrobenchmarkScreen = ScalingLazyColumnBenchmark,
+ actionSuffix = "SCALING_LAZY_COLUMN_ACTIVITY"
+ )
diff --git a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/ScrollAwaySample.kt b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/ScrollAwaySample.kt
index 225dd56f..42f1ba7 100644
--- a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/ScrollAwaySample.kt
+++ b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/ScrollAwaySample.kt
@@ -33,7 +33,9 @@
import androidx.wear.compose.material3.ScreenStage
import androidx.wear.compose.material3.Text
import androidx.wear.compose.material3.TimeText
+import androidx.wear.compose.material3.curvedText
import androidx.wear.compose.material3.scrollAway
+import androidx.wear.compose.material3.timeTextSeparator
@Sampled
@Composable
@@ -73,10 +75,10 @@
if (state.isScrollInProgress) ScreenStage.Scrolling else ScreenStage.Idle
}
),
- content = {
- text("ScrollAway")
- separator()
- time()
+ content = { time ->
+ curvedText("ScrollAway")
+ timeTextSeparator()
+ curvedText(time)
}
)
}
diff --git a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/TimeTextSample.kt b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/TimeTextSample.kt
index 020e098..47ba1e6 100644
--- a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/TimeTextSample.kt
+++ b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/TimeTextSample.kt
@@ -18,9 +18,14 @@
import androidx.annotation.Sampled
import androidx.compose.runtime.Composable
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.wear.compose.foundation.CurvedModifier
+import androidx.wear.compose.foundation.weight
import androidx.wear.compose.material3.MaterialTheme
import androidx.wear.compose.material3.TimeText
import androidx.wear.compose.material3.TimeTextDefaults
+import androidx.wear.compose.material3.curvedText
+import androidx.wear.compose.material3.timeTextSeparator
@Sampled
@Composable
@@ -34,9 +39,23 @@
fun TimeTextWithStatus() {
val primaryStyle =
TimeTextDefaults.timeTextStyle(color = MaterialTheme.colorScheme.primaryContainer)
- TimeText {
- text("ETA 12:48", style = primaryStyle)
- separator()
- time()
+ TimeText { time ->
+ curvedText("ETA 12:48", style = primaryStyle)
+ timeTextSeparator()
+ curvedText(time)
+ }
+}
+
+@Sampled
+@Composable
+fun TimeTextWithStatusEllipsized() {
+ TimeText { time ->
+ curvedText(
+ "Long status that should be ellipsized.",
+ CurvedModifier.weight(1f),
+ overflow = TextOverflow.Ellipsis
+ )
+ timeTextSeparator()
+ curvedText(time)
}
}
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ScrollAwayTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ScrollAwayTest.kt
index 90674cb..4849bdc 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ScrollAwayTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ScrollAwayTest.kt
@@ -48,6 +48,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import androidx.wear.compose.foundation.ScrollInfoProvider
+import androidx.wear.compose.foundation.curvedComposable
import androidx.wear.compose.foundation.lazy.AutoCenteringParams
import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
import androidx.wear.compose.foundation.lazy.ScalingLazyListState
@@ -207,7 +208,7 @@
)
.testTag(TIME_TEXT_TAG),
) {
- composable { Box(Modifier.size(20.dp).background(timeTextColor)) }
+ curvedComposable { Box(Modifier.size(20.dp).background(timeTextColor)) }
}
}
}
@@ -233,7 +234,7 @@
)
.testTag(TIME_TEXT_TAG)
) {
- composable { Box(Modifier.size(20.dp).background(timeTextColor)) }
+ curvedComposable { Box(Modifier.size(20.dp).background(timeTextColor)) }
}
LazyColumn(state = scrollState, modifier = Modifier.testTag(SCROLL_TAG)) {
item { ListHeader { Text("Buttons") } }
@@ -253,7 +254,6 @@
.testTag(TEST_TAG)
) {
TimeText(
- contentColor = timeTextColor,
modifier =
Modifier.scrollAway(
scrollInfoProvider = ScrollInfoProvider(scrollState),
@@ -264,7 +264,7 @@
)
.testTag(TIME_TEXT_TAG)
) {
- composable { Box(Modifier.size(20.dp).background(timeTextColor)) }
+ curvedComposable { Box(Modifier.size(20.dp).background(timeTextColor)) }
}
Column(modifier = Modifier.verticalScroll(scrollState).testTag(SCROLL_TAG)) {
ListHeader { Text("Buttons") }
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TimeTextScreenshotTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TimeTextScreenshotTest.kt
index 5c81306..526b932 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TimeTextScreenshotTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TimeTextScreenshotTest.kt
@@ -29,23 +29,25 @@
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.DeviceConfigurationOverride
import androidx.compose.ui.test.ForcedSize
-import androidx.compose.ui.test.RoundScreen
import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.compose.ui.test.then
+import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import androidx.test.filters.SdkSuppress
import androidx.test.screenshot.AndroidXScreenshotTestRule
-import com.google.testing.junit.testparameterinjector.TestParameter
-import com.google.testing.junit.testparameterinjector.TestParameterInjector
+import androidx.wear.compose.foundation.CurvedModifier
+import androidx.wear.compose.foundation.CurvedScope
+import androidx.wear.compose.foundation.curvedComposable
+import androidx.wear.compose.foundation.weight
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TestName
import org.junit.runner.RunWith
@MediumTest
-@RunWith(TestParameterInjector::class)
+@RunWith(AndroidJUnit4::class)
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
class TimeTextScreenshotTest {
@get:Rule val rule = createComposeRule()
@@ -60,60 +62,34 @@
}
@Test
- fun time_text_with_clock_only_on_round_device() = verifyScreenshot {
+ fun time_text_with_clock_only() = verifyScreenshot {
TimeText(
modifier = Modifier.testTag(TEST_TAG).background(Color.DarkGray),
timeSource = MockTimeSource,
- ) {
- time()
+ )
+ }
+
+ @Test
+ fun time_text_with_status() = verifyScreenshot {
+ TimeText(
+ modifier = Modifier.testTag(TEST_TAG).background(Color.DarkGray),
+ timeSource = MockTimeSource,
+ ) { time ->
+ curvedText("ETA 12:48")
+ timeTextSeparator()
+ curvedText(time)
}
}
@Test
- fun time_text_with_clock_only_on_non_round_device() =
- verifyScreenshot(false) {
- TimeText(
- modifier = Modifier.testTag(TEST_TAG).background(Color.DarkGray),
- timeSource = MockTimeSource,
- ) {
- time()
- }
- }
-
- @Test
- fun time_text_with_status_on_round_device() = verifyScreenshot {
+ fun time_text_with_icon() = verifyScreenshot {
TimeText(
modifier = Modifier.testTag(TEST_TAG).background(Color.DarkGray),
timeSource = MockTimeSource,
- ) {
- text("ETA 12:48")
- separator()
- time()
- }
- }
-
- @Test
- fun time_text_with_status_on_non_round_device() =
- verifyScreenshot(false) {
- TimeText(
- modifier = Modifier.testTag(TEST_TAG).background(Color.DarkGray),
- timeSource = MockTimeSource,
- ) {
- text("ETA 12:48")
- separator()
- time()
- }
- }
-
- @Test
- fun time_text_with_icon_on_round_device() = verifyScreenshot {
- TimeText(
- modifier = Modifier.testTag(TEST_TAG).background(Color.DarkGray),
- timeSource = MockTimeSource,
- ) {
- time()
- separator()
- composable {
+ ) { time ->
+ curvedText(time)
+ timeTextSeparator()
+ curvedComposable {
Icon(
imageVector = Icons.Filled.Favorite,
contentDescription = "Favorite",
@@ -124,193 +100,126 @@
}
@Test
- fun time_text_with_icon_on_non_round_device() =
- verifyScreenshot(false) {
- TimeText(
- modifier = Modifier.testTag(TEST_TAG).background(Color.DarkGray),
- timeSource = MockTimeSource,
- ) {
- time()
- separator()
- composable {
- Icon(
- imageVector = Icons.Filled.Favorite,
- contentDescription = "Favorite",
- modifier = Modifier.size(13.dp)
- )
- }
- }
- }
-
- @Test
- fun time_text_with_custom_colors_on_round_device() = verifyScreenshot {
+ fun time_text_with_custom_colors() = verifyScreenshot {
val customStyle = TimeTextDefaults.timeTextStyle(color = Color.Red)
val timeTextStyle = TimeTextDefaults.timeTextStyle(color = Color.Cyan)
val separatorStyle = TimeTextDefaults.timeTextStyle(color = Color.Yellow)
TimeText(
- contentColor = Color.Green,
- timeTextStyle = timeTextStyle,
modifier = Modifier.testTag(TEST_TAG).background(Color.DarkGray),
timeSource = MockTimeSource,
- ) {
- text("ETA", customStyle)
- composable { Spacer(modifier = Modifier.size(4.dp)) }
- text("12:48")
- separator(separatorStyle)
- time()
+ ) { time ->
+ curvedText("ETA 12:48", style = customStyle)
+ curvedComposable { Spacer(modifier = Modifier.size(4.dp)) }
+ curvedText("12:48", style = customStyle)
+ timeTextSeparator(separatorStyle)
+ curvedText(time, style = timeTextStyle)
}
}
@Test
- fun time_text_with_long_status_on_round_device() = verifyScreenshot {
+ fun time_text_with_long_status() = verifyScreenshot {
val timeTextStyle = TimeTextDefaults.timeTextStyle(color = Color.Green)
TimeText(
- contentColor = Color.Green,
- timeTextStyle = timeTextStyle,
modifier = Modifier.testTag(TEST_TAG).background(Color.DarkGray),
timeSource = MockTimeSource,
- ) {
- text("Long status that should be ellipsized.")
- composable { Spacer(modifier = Modifier.size(4.dp)) }
- time()
- }
- }
-
- @Test
- fun time_text_with_custom_colors_on_non_round_device() =
- verifyScreenshot(false) {
- val customStyle = TimeTextDefaults.timeTextStyle(color = Color.Red)
- val timeTextStyle = TimeTextDefaults.timeTextStyle(color = Color.Cyan)
- val separatorStyle = TimeTextDefaults.timeTextStyle(color = Color.Yellow)
- TimeText(
- contentColor = Color.Green,
- timeTextStyle = timeTextStyle,
- modifier = Modifier.testTag(TEST_TAG).background(Color.DarkGray),
- timeSource = MockTimeSource,
- ) {
- text("ETA", customStyle)
- composable { Spacer(modifier = Modifier.size(4.dp)) }
- text("12:48")
- separator(separatorStyle)
- time()
- }
- }
-
- @Test
- fun time_text_with_very_long_text_on_round_device() =
- verifyScreenshot(true) {
- val customStyle = TimeTextDefaults.timeTextStyle(color = Color.Red)
- val timeTextStyle = TimeTextDefaults.timeTextStyle(color = Color.Cyan)
- val separatorStyle = TimeTextDefaults.timeTextStyle(color = Color.Yellow)
- TimeText(
- contentColor = Color.Green,
- timeTextStyle = timeTextStyle,
- maxSweepAngle = 180f,
- modifier = Modifier.testTag(TEST_TAG).background(Color.DarkGray),
- timeSource = MockTimeSource,
- ) {
- text(
- "Very long text to ensure we are respecting the maxSweep parameter",
- customStyle
- )
- separator(separatorStyle)
- time()
- }
- }
-
- @Test
- fun time_text_with_very_long_text_non_round_device() =
- verifyScreenshot(false) {
- val customStyle = TimeTextDefaults.timeTextStyle(color = Color.Red)
- val timeTextStyle = TimeTextDefaults.timeTextStyle(color = Color.Cyan)
- val separatorStyle = TimeTextDefaults.timeTextStyle(color = Color.Yellow)
- TimeText(
- contentColor = Color.Green,
- timeTextStyle = timeTextStyle,
- modifier = Modifier.testTag(TEST_TAG).background(Color.DarkGray),
- timeSource = MockTimeSource,
- ) {
- text(
- "Very long text to ensure we are not taking more than one line and " +
- "leaving room for the time",
- customStyle
- )
- separator(separatorStyle)
- time()
- }
- }
-
- @Test
- fun time_text_with_very_long_text_smaller_angle_on_round_device() =
- verifyScreenshot(true) {
- val customStyle = TimeTextDefaults.timeTextStyle(color = Color.Red)
- val timeTextStyle = TimeTextDefaults.timeTextStyle(color = Color.Cyan)
- val separatorStyle = TimeTextDefaults.timeTextStyle(color = Color.Yellow)
- TimeText(
- contentColor = Color.Green,
- timeTextStyle = timeTextStyle,
- maxSweepAngle = 90f,
- modifier = Modifier.testTag(TEST_TAG).background(Color.DarkGray),
- timeSource = MockTimeSource,
- ) {
- text(
- "Very long text to ensure we are respecting the maxSweep parameter",
- customStyle
- )
- separator(separatorStyle)
- time()
- }
- }
-
- @Test
- fun time_text_long_text_before_time(@TestParameter shape: ScreenShape) =
- TimeTextWithDefaults(shape.isRound) {
- text("Very long text to ensure we are respecting the weight parameter", weight = 1f)
- separator()
- time()
- separator()
- text("More")
- }
-
- @Test
- fun time_text_long_text_after_time(@TestParameter shape: ScreenShape) =
- TimeTextWithDefaults(shape.isRound) {
- text("More")
- separator()
- time()
- separator()
- text("Very long text to ensure we are respecting the weight parameter", weight = 1f)
- }
-
- // This is to get better names, so it says 'round_device' instead of 'true'
- enum class ScreenShape(val isRound: Boolean) {
- ROUND_DEVICE(true),
- SQUARE_DEVICE(false)
- }
-
- private fun TimeTextWithDefaults(isDeviceRound: Boolean, content: TimeTextScope.() -> Unit) =
- verifyScreenshot(isDeviceRound) {
- TimeText(
- contentColor = Color.Green,
- maxSweepAngle = 180f,
- modifier = Modifier.testTag(TEST_TAG).background(Color.DarkGray),
- timeSource = MockTimeSource,
- content = content
+ ) { time ->
+ curvedText(
+ "Long status that should be ellipsized.",
+ CurvedModifier.weight(1f),
+ overflow = TextOverflow.Ellipsis,
+ style = timeTextStyle
)
+ curvedComposable { Spacer(modifier = Modifier.size(4.dp)) }
+ curvedText(time, style = timeTextStyle)
}
+ }
- private fun verifyScreenshot(isDeviceRound: Boolean = true, content: @Composable () -> Unit) {
+ @Test
+ fun time_text_with_very_long_text() = verifyScreenshot {
+ val customStyle = TimeTextDefaults.timeTextStyle(color = Color.Red)
+ val timeTextStyle = TimeTextDefaults.timeTextStyle(color = Color.Cyan)
+ val separatorStyle = TimeTextDefaults.timeTextStyle(color = Color.Yellow)
+ TimeText(
+ maxSweepAngle = 180f,
+ modifier = Modifier.testTag(TEST_TAG).background(Color.DarkGray),
+ timeSource = MockTimeSource,
+ ) { time ->
+ curvedText(
+ "Very long text to ensure we are respecting the maxSweep parameter",
+ CurvedModifier.weight(1f),
+ overflow = TextOverflow.Ellipsis,
+ maxSweepAngle = 180f,
+ style = customStyle
+ )
+ timeTextSeparator(separatorStyle)
+ curvedText(time, style = timeTextStyle)
+ }
+ }
+
+ @Test
+ fun time_text_with_very_long_text_smaller_angle() = verifyScreenshot {
+ val customStyle = TimeTextDefaults.timeTextStyle(color = Color.Red)
+ val timeTextStyle = TimeTextDefaults.timeTextStyle(color = Color.Cyan)
+ val separatorStyle = TimeTextDefaults.timeTextStyle(color = Color.Yellow)
+ TimeText(
+ maxSweepAngle = 90f,
+ modifier = Modifier.testTag(TEST_TAG).background(Color.DarkGray),
+ timeSource = MockTimeSource,
+ ) { time ->
+ curvedText(
+ "Very long text to ensure we are respecting the maxSweep parameter",
+ CurvedModifier.weight(1f),
+ overflow = TextOverflow.Ellipsis,
+ style = customStyle
+ )
+ timeTextSeparator(separatorStyle)
+ curvedText(time, style = timeTextStyle)
+ }
+ }
+
+ @Test
+ fun time_text_long_text_before_time() = TimeTextWithDefaults { time ->
+ curvedText(
+ "Very long text to ensure we are respecting the weight parameter",
+ CurvedModifier.weight(1f),
+ overflow = TextOverflow.Ellipsis
+ )
+ timeTextSeparator()
+ curvedText(time)
+ timeTextSeparator()
+ curvedText("More")
+ }
+
+ @Test
+ fun time_text_long_text_after_time() = TimeTextWithDefaults { time ->
+ curvedText("More")
+ timeTextSeparator()
+ curvedText(time)
+ timeTextSeparator()
+ curvedText(
+ "Very long text to ensure we are respecting the weight parameter",
+ CurvedModifier.weight(1f),
+ overflow = TextOverflow.Ellipsis
+ )
+ }
+
+ private fun TimeTextWithDefaults(content: CurvedScope.(String) -> Unit) = verifyScreenshot {
+ TimeText(
+ maxSweepAngle = 180f,
+ modifier = Modifier.testTag(TEST_TAG).background(Color.DarkGray),
+ timeSource = MockTimeSource,
+ content = content
+ )
+ }
+
+ private fun verifyScreenshot(content: @Composable () -> Unit) {
rule.verifyScreenshot(
- // Valid characters for golden identifiers are [A-Za-z0-9_-]
- // TestParameterInjector adds '[' + parameter_values + ']' to the test name.
- methodName = testName.methodName.replace("[", "_").replace("]", ""),
+ methodName = testName.methodName,
screenshotRule = screenshotRule,
content = {
val screenSize = LocalContext.current.resources.configuration.smallestScreenWidthDp
DeviceConfigurationOverride(
- DeviceConfigurationOverride.ForcedSize(
- DpSize(screenSize.dp, screenSize.dp)
- ) then DeviceConfigurationOverride.RoundScreen(isDeviceRound)
+ DeviceConfigurationOverride.ForcedSize(DpSize(screenSize.dp, screenSize.dp))
) {
content()
}
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TimeTextTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TimeTextTest.kt
index f932e4a..e8a7528 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TimeTextTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TimeTextTest.kt
@@ -21,20 +21,13 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.testTag
-import androidx.compose.ui.test.DeviceConfigurationOverride
-import androidx.compose.ui.test.RoundScreen
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithTag
-import androidx.compose.ui.test.onNodeWithText
-import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.font.FontFamily
-import androidx.compose.ui.text.font.FontStyle
-import androidx.compose.ui.unit.sp
import androidx.test.filters.SdkSuppress
+import androidx.wear.compose.foundation.curvedComposable
import java.util.Calendar
import java.util.Locale
import java.util.TimeZone
@@ -47,101 +40,54 @@
@Test
fun supports_testtag() {
- rule.setContentWithTheme { TimeText(modifier = Modifier.testTag(TEST_TAG)) { time() } }
+ rule.setContentWithTheme { TimeText(modifier = Modifier.testTag(TEST_TAG)) }
rule.onNodeWithTag(TEST_TAG).assertExists()
}
@Test
- fun shows_time_by_default_on_non_round_device() {
+ fun shows_time_by_default() {
val timeText = "time"
rule.setContentWithTheme {
- DeviceConfigurationOverride(DeviceConfigurationOverride.RoundScreen(false)) {
- TimeText(
- timeSource =
- object : TimeSource {
- @Composable override fun currentTime(): String = timeText
- },
- )
- }
+ TimeText(
+ timeSource =
+ object : TimeSource {
+ @Composable override fun currentTime(): String = timeText
+ },
+ )
}
// Note that onNodeWithText doesn't work for curved text, so only testing for non-round.
- rule.onNodeWithText(timeText).assertIsDisplayed()
+ rule.onNodeWithContentDescription(timeText).assertIsDisplayed()
}
@Test
- fun updates_clock_when_source_changes_on_non_round_device() {
+ fun updates_clock_when_source_changes() {
val timeState = mutableStateOf("Unchanged")
rule.setContentWithTheme {
- DeviceConfigurationOverride(DeviceConfigurationOverride.RoundScreen(false)) {
- TimeText(
- modifier = Modifier.testTag(TEST_TAG),
- timeSource =
- object : TimeSource {
- @Composable override fun currentTime(): String = timeState.value
- },
- ) {
- time()
- }
- }
+ TimeText(
+ modifier = Modifier.testTag(TEST_TAG),
+ timeSource =
+ object : TimeSource {
+ @Composable override fun currentTime(): String = timeState.value
+ },
+ )
}
timeState.value = "Changed"
- rule.onNodeWithText("Changed").assertIsDisplayed()
- }
-
- @Test
- fun updates_clock_when_source_changes_on_round_device() {
- val timeState = mutableStateOf("Unchanged")
-
- rule.setContentWithTheme {
- DeviceConfigurationOverride(DeviceConfigurationOverride.RoundScreen(true)) {
- TimeText(
- modifier = Modifier.testTag(TEST_TAG),
- timeSource =
- object : TimeSource {
- @Composable override fun currentTime(): String = timeState.value
- },
- ) {
- time()
- }
- }
- }
- timeState.value = "Changed"
- rule.waitForIdle()
rule.onNodeWithContentDescription("Changed").assertIsDisplayed()
}
@Test
- fun checks_status_displayed_on_non_round_device() {
+ fun checks_status_displayed() {
val statusText = "Status"
- rule.setContentWithTheme {
- DeviceConfigurationOverride(DeviceConfigurationOverride.RoundScreen(false)) {
- TimeText {
- text(statusText)
- separator()
- time()
- }
- }
- }
-
- rule.onNodeWithText(statusText).assertIsDisplayed()
- }
-
- @Test
- fun checks_status_displayed_on_round_device() {
- val statusText = "Status"
-
- rule.setContentWithTheme {
- DeviceConfigurationOverride(DeviceConfigurationOverride.RoundScreen(true)) {
- TimeText {
- text(statusText)
- separator()
- time()
- }
+ rule.setContentWithTheme() {
+ TimeText { time ->
+ curvedText(statusText)
+ timeTextSeparator()
+ curvedText(time)
}
}
@@ -149,54 +95,26 @@
}
@Test
- fun checks_separator_displayed_on_non_round_device() {
+ fun checks_separator_displayed() {
val statusText = "Status"
val separatorText = "·"
- rule.setContentWithTheme {
- DeviceConfigurationOverride(DeviceConfigurationOverride.RoundScreen(false)) {
- TimeText {
- text(statusText)
- separator()
- time()
- }
- }
- }
-
- rule.onNodeWithText(separatorText).assertIsDisplayed()
- }
-
- @Test
- fun checks_separator_displayed_on_round_device() {
- val statusText = "Status"
- val separatorText = "·"
-
- rule.setContentWithTheme {
- DeviceConfigurationOverride(DeviceConfigurationOverride.RoundScreen(true)) {
- TimeText {
- text(statusText)
- separator()
- time()
- }
- }
- }
+ rule.setContentWithTheme { BasicTimeTextWithStatus(statusText) }
rule.onNodeWithContentDescription(separatorText).assertIsDisplayed()
}
@Test
- fun checks_composable_displayed_on_non_round_device() {
+ fun checks_composable_displayed() {
rule.setContentWithTheme {
- DeviceConfigurationOverride(DeviceConfigurationOverride.RoundScreen(false)) {
- TimeText {
- time()
- separator()
- composable {
- Text(
- modifier = Modifier.testTag(TEST_TAG),
- text = "Compose",
- )
- }
+ TimeText { time ->
+ curvedText(time)
+ timeTextSeparator()
+ curvedComposable {
+ Text(
+ modifier = Modifier.testTag(TEST_TAG),
+ text = "Compose",
+ )
}
}
}
@@ -205,178 +123,6 @@
}
@Test
- fun checks_composable_displayed_on_round_device() {
- rule.setContentWithTheme {
- DeviceConfigurationOverride(DeviceConfigurationOverride.RoundScreen(true)) {
- TimeText {
- time()
- separator()
- composable {
- Text(
- modifier = Modifier.testTag(TEST_TAG),
- text = "Compose",
- )
- }
- }
- }
- }
-
- rule.onNodeWithTag(TEST_TAG).assertIsDisplayed()
- }
-
- @Test
- fun changes_timeTextStyle_on_non_round_device() {
- val timeText = "testTime"
-
- val testTextStyle =
- TextStyle(color = Color.Green, background = Color.Black, fontSize = 20.sp)
- rule.setContentWithTheme {
- DeviceConfigurationOverride(DeviceConfigurationOverride.RoundScreen(false)) {
- TimeText(
- timeSource =
- object : TimeSource {
- @Composable override fun currentTime(): String = timeText
- },
- timeTextStyle = testTextStyle
- ) {
- time()
- }
- }
- }
- val actualStyle = rule.textStyleOf(timeText)
- Assert.assertEquals(testTextStyle.color, actualStyle.color)
- Assert.assertEquals(testTextStyle.background, actualStyle.background)
- Assert.assertEquals(testTextStyle.fontSize, actualStyle.fontSize)
- }
-
- @Test
- fun changes_material_theme_on_non_round_device_except_color() {
- val timeText = "testTime"
-
- val testTextStyle =
- TextStyle(
- color = Color.Green,
- background = Color.Black,
- fontStyle = FontStyle.Italic,
- fontSize = 25.sp,
- fontFamily = FontFamily.SansSerif
- )
- rule.setContent {
- MaterialTheme(typography = MaterialTheme.typography.copy(arcMedium = testTextStyle)) {
- DeviceConfigurationOverride(DeviceConfigurationOverride.RoundScreen(false)) {
- TimeText(
- timeSource =
- object : TimeSource {
- @Composable override fun currentTime(): String = timeText
- },
- ) {
- time()
- }
- }
- }
- }
- val actualStyle = rule.textStyleOf(timeText)
- Assert.assertEquals(testTextStyle.background, actualStyle.background)
- Assert.assertEquals(testTextStyle.fontSize, actualStyle.fontSize)
- Assert.assertEquals(testTextStyle.fontStyle, actualStyle.fontStyle)
- Assert.assertEquals(testTextStyle.fontFamily, actualStyle.fontFamily)
- Assert.assertNotEquals(testTextStyle.color, actualStyle.color)
- }
-
- @Test
- fun color_remains_onBackground_when_material_theme_changed_on_non_round_device() {
- val timeText = "testTime"
- var onBackgroundColor = Color.Unspecified
-
- val testTextStyle =
- TextStyle(
- color = Color.Green,
- background = Color.Black,
- fontStyle = FontStyle.Italic,
- fontSize = 25.sp,
- fontFamily = FontFamily.SansSerif
- )
- rule.setContent {
- MaterialTheme(typography = MaterialTheme.typography.copy(labelSmall = testTextStyle)) {
- onBackgroundColor = MaterialTheme.colorScheme.onBackground
- DeviceConfigurationOverride(DeviceConfigurationOverride.RoundScreen(false)) {
- TimeText(
- timeSource =
- object : TimeSource {
- @Composable override fun currentTime(): String = timeText
- },
- ) {
- time()
- }
- }
- }
- }
- val actualStyle = rule.textStyleOf(timeText)
- Assert.assertEquals(onBackgroundColor, actualStyle.color)
- }
-
- @Test
- fun has_correct_default_leading_text_color_on_non_round_device() {
- val leadingText = "leadingText"
- var primaryColor = Color.Unspecified
-
- rule.setContentWithTheme {
- primaryColor = MaterialTheme.colorScheme.primary
- DeviceConfigurationOverride(DeviceConfigurationOverride.RoundScreen(false)) {
- TimeText {
- text(leadingText)
- separator()
- time()
- }
- }
- }
- val actualStyle = rule.textStyleOf(leadingText)
- Assert.assertEquals(primaryColor, actualStyle.color)
- }
-
- @Test
- fun supports_custom_leading_text_color_on_non_round_device() {
- val leadingText = "leadingText"
- val customColor = Color.Green
-
- rule.setContentWithTheme {
- DeviceConfigurationOverride(DeviceConfigurationOverride.RoundScreen(false)) {
- TimeText(
- contentColor = customColor,
- ) {
- text(leadingText)
- separator()
- time()
- }
- }
- }
- val actualStyle = rule.textStyleOf(leadingText)
- Assert.assertEquals(customColor, actualStyle.color)
- }
-
- @Test
- fun supports_custom_text_style_on_non_round_device() {
- val leadingText = "leadingText"
-
- val timeTextStyle = TextStyle(background = Color.Blue, fontSize = 14.sp)
- val contentTextStyle =
- TextStyle(color = Color.Green, background = Color.Black, fontSize = 20.sp)
- rule.setContentWithTheme {
- DeviceConfigurationOverride(DeviceConfigurationOverride.RoundScreen(false)) {
- TimeText(contentColor = Color.Red, timeTextStyle = timeTextStyle) {
- text(leadingText, contentTextStyle)
- separator()
- time()
- }
- }
- }
- val actualStyle = rule.textStyleOf(leadingText)
- Assert.assertEquals(contentTextStyle.color, actualStyle.color)
- Assert.assertEquals(contentTextStyle.background, actualStyle.background)
- Assert.assertEquals(contentTextStyle.fontSize, actualStyle.fontSize)
- }
-
- @Test
fun formats_current_time() {
val currentTimeInMillis = 1631544258000L // 2021-09-13 14:44:18
val format = "HH:mm:ss"
@@ -418,4 +164,13 @@
}
Assert.assertEquals(expectedTime, actualTime)
}
+
+ @Composable
+ private fun BasicTimeTextWithStatus(statusText: String) {
+ TimeText { time ->
+ curvedText(statusText)
+ timeTextSeparator()
+ curvedText(time)
+ }
+ }
}
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/AppScaffold.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/AppScaffold.kt
index edffe7e..51fc42b 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/AppScaffold.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/AppScaffold.kt
@@ -46,7 +46,7 @@
@Composable
public fun AppScaffold(
modifier: Modifier = Modifier,
- timeText: @Composable () -> Unit = { TimeText { time() } },
+ timeText: @Composable () -> Unit = { TimeText() },
content: @Composable BoxScope.() -> Unit,
) {
CompositionLocalProvider(
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TimeText.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TimeText.kt
index f95bd3c..351a47c 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TimeText.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TimeText.kt
@@ -22,17 +22,8 @@
import android.content.IntentFilter
import android.text.format.DateFormat
import androidx.annotation.VisibleForTesting
-import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.RowScope
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.shape.CircleShape
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf
@@ -42,17 +33,14 @@
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.dp
-import androidx.compose.ui.util.fastForEach
import androidx.wear.compose.foundation.ArcPaddingValues
import androidx.wear.compose.foundation.CurvedAlignment
import androidx.wear.compose.foundation.CurvedDirection
@@ -61,35 +49,29 @@
import androidx.wear.compose.foundation.CurvedScope
import androidx.wear.compose.foundation.CurvedTextStyle
import androidx.wear.compose.foundation.background
-import androidx.wear.compose.foundation.curvedComposable
+import androidx.wear.compose.foundation.basicCurvedText
import androidx.wear.compose.foundation.curvedRow
import androidx.wear.compose.foundation.padding
import androidx.wear.compose.foundation.sizeIn
-import androidx.wear.compose.foundation.weight
-import androidx.wear.compose.material3.TimeTextDefaults.CurvedTextSeparator
-import androidx.wear.compose.material3.TimeTextDefaults.TextSeparator
import androidx.wear.compose.material3.TimeTextDefaults.timeFormat
+import androidx.wear.compose.material3.TimeTextDefaults.timeTextStyle
import androidx.wear.compose.materialcore.currentTimeMillis
import androidx.wear.compose.materialcore.is24HourFormat
-import androidx.wear.compose.materialcore.isRoundDevice
import java.util.Calendar
import java.util.Locale
/**
- * Layout to show the current time and a label at the top of the screen. If device has a round
- * screen, then the time will be curved along the top edge of the screen, if rectangular - then the
- * text and the time will be straight.
+ * Layout to show the current time and a label, they will be drawn in a curve, following the top
+ * edge of the screen.
*
* Note that Wear Material UX guidance recommends that time text should not be larger than
- * [TimeTextDefaults.MaxSweepAngle] of the screen edge on round devices, which is enforced by
- * default. It is recommended that additional content, if any, is limited to short status messages
- * before the [TimeTextScope.time] using the MaterialTheme.colorScheme.primary color.
+ * [TimeTextDefaults.MaxSweepAngle] of the screen edge, which is enforced by default. It is
+ * recommended that additional content, if any, is limited to short status messages before the time
+ * using the MaterialTheme.colorScheme.primary color.
*
* For more information, see the
* [Curved Text](https://developer.android.com/training/wearables/components/curved-text) guide.
*
- * Different components of [TimeText] can be added through methods of [TimeTextScope].
- *
* A simple [TimeText] which shows the current time:
*
* @sample androidx.wear.compose.material3.samples.TimeTextClockOnly
@@ -97,15 +79,19 @@
* A [TimeText] with a short app status message shown:
*
* @sample androidx.wear.compose.material3.samples.TimeTextWithStatus
+ *
+ * A [TimeText] with a long status message, that needs ellipsizing:
+ *
+ * @sample androidx.wear.compose.material3.samples.TimeTextWithStatusEllipsized
* @param modifier The modifier to be applied to the component.
* @param curvedModifier The [CurvedModifier] used to restrict the arc in which [TimeText] is drawn.
* @param maxSweepAngle The default maximum sweep angle in degrees.
* @param timeSource [TimeSource] which retrieves the current time and formats it.
- * @param timeTextStyle [TextStyle] for the time text itself.
- * @param contentColor [Color] of content of displayed through [TimeTextScope.text] and
- * [TimeTextScope.composable].
* @param contentPadding The spacing values between the container and the content.
- * @param content The content of the [TimeText] - displays the current time by default.
+ * @param content The content of the [TimeText] - displays the current time by default. This lambda
+ * receives the current time as a String and should display it using curvedText. Note that if long
+ * curved text is included here, it should specify [CurvedModifier.weight] on it so that the space
+ * available is suitably allocated.
*/
@Composable
public fun TimeText(
@@ -113,91 +99,31 @@
curvedModifier: CurvedModifier = CurvedModifier,
maxSweepAngle: Float = TimeTextDefaults.MaxSweepAngle,
timeSource: TimeSource = TimeTextDefaults.rememberTimeSource(timeFormat()),
- timeTextStyle: TextStyle = TimeTextDefaults.timeTextStyle(),
- contentColor: Color = MaterialTheme.colorScheme.primary,
contentPadding: PaddingValues = TimeTextDefaults.ContentPadding,
- content: TimeTextScope.() -> Unit = { time() }
+ content: CurvedScope.(String) -> Unit = { time -> timeTextCurvedText(time) }
) {
- val timeText = timeSource.currentTime()
+ val currentTime = timeSource.currentTime()
val backgroundColor = CurvedTextDefaults.backgroundColor()
- if (isRoundDevice()) {
- CurvedLayout(modifier = modifier) {
- curvedRow(
- modifier =
- curvedModifier
- .sizeIn(maxSweepDegrees = maxSweepAngle)
- .padding(contentPadding.toArcPadding())
- .background(backgroundColor, StrokeCap.Round),
- radialAlignment = CurvedAlignment.Radial.Center
- ) {
- CurvedTimeTextScope(timeText, timeTextStyle, maxSweepAngle, contentColor).apply {
- content()
- Show()
- }
- }
- }
- } else {
- Box(modifier.fillMaxSize()) {
- Row(
- modifier =
- Modifier.align(Alignment.TopCenter)
- .background(backgroundColor, CircleShape)
- .padding(contentPadding),
- verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = Arrangement.Center
- ) {
- LinearTimeTextScope(timeText, timeTextStyle, contentColor).apply {
- content()
- Show()
- }
- }
+ CurvedLayout(modifier = modifier) {
+ curvedRow(
+ modifier =
+ curvedModifier
+ .sizeIn(maxSweepDegrees = maxSweepAngle)
+ .padding(contentPadding.toArcPadding())
+ .background(backgroundColor, StrokeCap.Round),
+ radialAlignment = CurvedAlignment.Radial.Center
+ ) {
+ content(currentTime)
}
}
}
-/** Receiver scope which is used by [TimeText]. */
-public sealed class TimeTextScope {
- /**
- * Adds a composable [Text] for non-round devices and [curvedText] for round devices to
- * [TimeText] content. Typically used to add a short status message ahead of the time text.
- *
- * @param text The text to display.
- * @param style configuration for the [text] such as color, font etc.
- * @param weight Size the text's width proportional to its weight relative to other weighted
- * sibling elements in the TimeText. Specify NaN to make this text not have a weight
- * specified. The default value, [TimeTextDefaults.AutoTextWeight], makes this text have
- * weight 1f if it's the only one, and not have weight if there are two or more.
- */
- public abstract fun text(
- text: String,
- style: TextStyle? = null,
- weight: Float = TimeTextDefaults.AutoTextWeight
- )
-
- /** Adds a text displaying current time. */
- public abstract fun time()
-
- /**
- * Adds a separator in [TimeText].
- *
- * @param style configuration for the [separator] such as color, font etc.
- */
- public abstract fun separator(style: TextStyle? = null)
-
- /**
- * Adds a composable in content of [TimeText]. This can be used to display non-text information
- * such as an icon.
- *
- * @param content Slot for the [composable] to be displayed.
- */
- public abstract fun composable(content: @Composable () -> Unit)
-}
-
/** Contains the default values used by [TimeText]. */
public object TimeTextDefaults {
/** The default padding from the edge of the screen. */
private val Padding = PaddingDefaults.edgePadding
+
/** Default format for 24h clock. */
public const val TimeFormat24Hours: String = "HH:mm"
@@ -228,8 +154,8 @@
}
/**
- * Creates a [TextStyle] with default parameters used for showing time on square screens. By
- * default a copy of MaterialTheme.typography.arcMedium style is created.
+ * Creates a [CurvedTextStyle] with default parameters used for showing time. By default a copy
+ * of MaterialTheme.typography.arcMedium style is created.
*
* @param background The background color.
* @param color The main color.
@@ -240,9 +166,11 @@
background: Color = Color.Unspecified,
color: Color = MaterialTheme.colorScheme.onBackground,
fontSize: TextUnit = TextUnit.Unspecified,
- ): TextStyle =
- MaterialTheme.typography.arcMedium +
- TextStyle(color = color, background = background, fontSize = fontSize)
+ ): CurvedTextStyle =
+ CurvedTextStyle(
+ MaterialTheme.typography.arcMedium +
+ TextStyle(color = color, background = background, fontSize = fontSize)
+ )
/**
* Creates a default implementation of [TimeSource] and remembers it. Once the system time
@@ -260,48 +188,37 @@
@Composable
public fun rememberTimeSource(timeFormat: String): TimeSource =
remember(timeFormat) { DefaultTimeSource(timeFormat) }
+}
- /**
- * A default implementation of Separator shown between any text/composable and the time on
- * non-round screens.
- *
- * @param modifier A default modifier for the separator.
- * @param textStyle A [TextStyle] for the separator.
- * @param contentPadding The spacing values between the container and the separator.
- */
- @Composable
- internal fun TextSeparator(
- modifier: Modifier = Modifier,
- textStyle: TextStyle = timeTextStyle(),
- contentPadding: PaddingValues = PaddingValues(horizontal = 4.dp)
+/**
+ * Default curved text to use in a [TimeText], for displaying the time
+ *
+ * @param time The time to display.
+ * @param style A [CurvedTextStyle] to override the style used.
+ */
+public fun CurvedScope.timeTextCurvedText(time: String, style: CurvedTextStyle? = null) {
+ basicCurvedText(
+ time,
) {
- Text(text = "·", style = textStyle, modifier = modifier.padding(contentPadding))
+ style?.let { timeTextStyle() + it } ?: timeTextStyle()
}
+}
- /**
- * A default implementation of Separator shown between any text/composable and the time on round
- * screens.
- *
- * @param curvedTextStyle A [CurvedTextStyle] for the separator.
- * @param contentArcPadding [ArcPaddingValues] for the separator text.
- */
- internal fun CurvedScope.CurvedTextSeparator(
- curvedTextStyle: CurvedTextStyle? = null,
- contentArcPadding: ArcPaddingValues = ArcPaddingValues(angular = 4.dp)
- ) {
- curvedText(
- text = "·",
- style = curvedTextStyle,
- modifier = CurvedModifier.padding(contentArcPadding)
- )
- }
-
- /**
- * Weight value used to specify that the value is automatic. It will be 1f when there is one
- * text, and no weight will be used if there are 2 or more texts. For the 2+ texts case, usually
- * one of them should have weight manually specified to ensure its properly cut and ellipsized.
- */
- public val AutoTextWeight: Float = -1f
+/**
+ * A default implementation of Separator, to be shown between any text/composable and the time.
+ *
+ * @param curvedTextStyle A [CurvedTextStyle] for the separator.
+ * @param contentArcPadding [ArcPaddingValues] for the separator text.
+ */
+public fun CurvedScope.timeTextSeparator(
+ curvedTextStyle: CurvedTextStyle? = null,
+ contentArcPadding: ArcPaddingValues = ArcPaddingValues(angular = 4.dp)
+) {
+ curvedText(
+ text = "·",
+ style = curvedTextStyle,
+ modifier = CurvedModifier.padding(contentArcPadding)
+ )
}
public interface TimeSource {
@@ -314,122 +231,6 @@
@Composable public fun currentTime(): String
}
-/** Implementation of [TimeTextScope] for round devices. */
-internal class CurvedTimeTextScope(
- private val timeText: String,
- private val timeTextStyle: TextStyle,
- private val maxSweepAngle: Float,
- contentColor: Color,
-) : TimeTextScope() {
- private var textCount = 0
- private val pending = mutableListOf<CurvedScope.() -> Unit>()
- private val contentTextStyle = timeTextStyle.merge(contentColor)
-
- override fun text(text: String, style: TextStyle?, weight: Float) {
- textCount++
- pending.add {
- curvedText(
- text = text,
- overflow = TextOverflow.Ellipsis,
- maxSweepAngle = maxSweepAngle,
- style = CurvedTextStyle(style = contentTextStyle.merge(style)),
- modifier =
- if (weight.isValidWeight()) CurvedModifier.weight(weight)
- // Note that we are creating a lambda here, but textCount is actually read
- // later, during the call to Show, when the pending list is fully constructed.
- else if (weight == TimeTextDefaults.AutoTextWeight && textCount <= 1)
- CurvedModifier.weight(1f)
- else CurvedModifier
- )
- }
- }
-
- override fun time() {
- pending.add {
- curvedText(
- timeText,
- maxSweepAngle = maxSweepAngle,
- style = CurvedTextStyle(timeTextStyle)
- )
- }
- }
-
- override fun separator(style: TextStyle?) {
- pending.add { CurvedTextSeparator(CurvedTextStyle(style = timeTextStyle.merge(style))) }
- }
-
- override fun composable(content: @Composable () -> Unit) {
- pending.add {
- curvedComposable {
- CompositionLocalProvider(
- LocalContentColor provides contentTextStyle.color,
- LocalTextStyle provides contentTextStyle,
- content = content
- )
- }
- }
- }
-
- fun CurvedScope.Show() {
- pending.fastForEach { it() }
- }
-}
-
-/** Implementation of [TimeTextScope] for non-round devices. */
-internal class LinearTimeTextScope(
- private val timeText: String,
- private val timeTextStyle: TextStyle,
- contentColor: Color,
-) : TimeTextScope() {
- private var textCount = 0
- private val pending = mutableListOf<@Composable RowScope.() -> Unit>()
- private val contentTextStyle = timeTextStyle.merge(contentColor)
-
- override fun text(text: String, style: TextStyle?, weight: Float) {
- textCount++
- pending.add {
- Text(
- text = text,
- maxLines = 1,
- overflow = TextOverflow.Ellipsis,
- style = contentTextStyle.merge(style),
- modifier =
- if (weight.isValidWeight()) Modifier.weight(weight, fill = false)
- // Note that we are creating a lambda here, but textCount is actually read
- // later, during the call to Show, when the pending list is fully constructed.
- else if (weight == TimeTextDefaults.AutoTextWeight && textCount <= 1)
- Modifier.weight(1f, fill = false)
- else Modifier
- )
- }
- }
-
- override fun time() {
- pending.add { Text(timeText, style = timeTextStyle) }
- }
-
- override fun separator(style: TextStyle?) {
- pending.add { TextSeparator(textStyle = timeTextStyle.merge(style)) }
- }
-
- override fun composable(content: @Composable () -> Unit) {
- pending.add {
- CompositionLocalProvider(
- LocalContentColor provides contentTextStyle.color,
- LocalTextStyle provides contentTextStyle,
- content = content
- )
- }
- }
-
- @Composable
- fun RowScope.Show() {
- pending.fastForEach { it() }
- }
-}
-
-private fun Float.isValidWeight() = !isNaN() && this > 0f
-
internal class DefaultTimeSource(timeFormat: String) : TimeSource {
private val _timeFormat = timeFormat
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/DemoApp.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/DemoApp.kt
index a674c88..685cf6d 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/DemoApp.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/DemoApp.kt
@@ -109,7 +109,7 @@
@Composable { it: @Composable () -> Unit ->
// Only material3 demos benefit from the Material3 ScreenScaffold
if (category.materialVersion == 3) {
- val timeText = @Composable { androidx.wear.compose.material3.TimeText { time() } }
+ val timeText = @Composable { androidx.wear.compose.material3.TimeText() }
androidx.wear.compose.material3.ScreenScaffold(
scrollState = state,
timeText = remember { timeText },
diff --git a/wear/compose/integration-tests/navigation/build.gradle b/wear/compose/integration-tests/navigation/build.gradle
index 68bb715..3d052ae 100644
--- a/wear/compose/integration-tests/navigation/build.gradle
+++ b/wear/compose/integration-tests/navigation/build.gradle
@@ -43,6 +43,7 @@
dependencies {
implementation("androidx.activity:activity-compose:1.5.1")
+ implementation("androidx.compose.material:material-icons-core:1.6.7")
implementation(project(":compose:ui:ui"))
implementation(project(":compose:integration-tests:demos:common"))
implementation(project(":compose:foundation:foundation"))
diff --git a/wear/compose/integration-tests/navigation/src/main/java/androidx/wear/compose/integration/navigation/MainActivity.kt b/wear/compose/integration-tests/navigation/src/main/java/androidx/wear/compose/integration/navigation/MainActivity.kt
index 5e56425..2df366b 100644
--- a/wear/compose/integration-tests/navigation/src/main/java/androidx/wear/compose/integration/navigation/MainActivity.kt
+++ b/wear/compose/integration-tests/navigation/src/main/java/androidx/wear/compose/integration/navigation/MainActivity.kt
@@ -27,14 +27,18 @@
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.wear.compose.foundation.edgeSwipeToDismiss
+import androidx.wear.compose.foundation.lazy.TransformingLazyColumn
+import androidx.wear.compose.foundation.lazy.rememberTransformingLazyColumnState
import androidx.wear.compose.foundation.pager.rememberPagerState
import androidx.wear.compose.foundation.rememberSwipeToDismissBoxState
import androidx.wear.compose.material.CompactChip
+import androidx.wear.compose.material.ListHeader
import androidx.wear.compose.material.MaterialTheme
import androidx.wear.compose.material.Text
import androidx.wear.compose.material3.AppScaffold
@@ -52,6 +56,7 @@
setContent(parent = null) {
MaterialTheme {
+ val transformingLazyColumnState = rememberTransformingLazyColumnState()
val swipeToDismissBoxState = rememberSwipeToDismissBoxState()
val navController = rememberSwipeDismissableNavController()
SwipeDismissableNavHost(
@@ -60,26 +65,45 @@
startDestination = START
) {
composable(START) {
- Column(
- horizontalAlignment = Alignment.CenterHorizontally,
- verticalArrangement = Arrangement.Center,
- modifier = Modifier.fillMaxSize(),
+ TransformingLazyColumn(
+ state = transformingLazyColumnState,
+ modifier = Modifier.padding(horizontal = 10.dp)
) {
- Text(text = "Screen 1", color = MaterialTheme.colors.onSurface)
- CompactChip(
- onClick = { navController.navigate(SCREEN2) },
- label = { Text("Next screen") },
- )
- Spacer(modifier = Modifier.fillMaxWidth().height(4.dp))
- CompactChip(
- onClick = { navController.navigate(EDGE_SWIPE_SCREEN) },
- label = { Text("Screen with edge swipe") },
- )
- Spacer(modifier = Modifier.fillMaxWidth().height(4.dp))
- CompactChip(
- onClick = { navController.navigate(PAGER_SCAFFOLD_SCREEN) },
- label = { Text("Screen with PagerScaffold") },
- )
+ item {
+ ListHeader {
+ Text(text = "Screen 1", color = MaterialTheme.colors.onSurface)
+ }
+ }
+ item {
+ CompactChip(
+ onClick = { navController.navigate(SCREEN2) },
+ label = { Text("Next screen") },
+ )
+ }
+ item {
+ CompactChip(
+ onClick = { navController.navigate(EDGE_SWIPE_SCREEN) },
+ label = { Text("Screen with edge swipe") },
+ )
+ }
+ item {
+ CompactChip(
+ onClick = { navController.navigate(PAGER_SCAFFOLD_SCREEN) },
+ label = { Text("Screen with PagerScaffold") },
+ )
+ }
+ item {
+ CompactChip(
+ onClick = { navController.navigate(S2R_STANDARD_SCREEN) },
+ label = { Text("S2R - Standard") },
+ )
+ }
+ item {
+ CompactChip(
+ onClick = { navController.navigate(S2R_DUAL_DIRECTION_SCREEN) },
+ label = { Text("S2R - Dual Direction") },
+ )
+ }
}
}
composable(SCREEN2) {
@@ -152,6 +176,12 @@
}
}
}
+
+ composable(S2R_STANDARD_SCREEN) { SwipeToRevealSingleButtonWithAnchoring() }
+
+ composable(S2R_DUAL_DIRECTION_SCREEN) {
+ SwipeToRevealBothDirectionsNonAnchoring()
+ }
}
}
}
@@ -163,3 +193,5 @@
private const val SCREEN3 = "screen3"
private const val EDGE_SWIPE_SCREEN = "edge_swipe_screen"
private const val PAGER_SCAFFOLD_SCREEN = "pager_scaffold_screen"
+private const val S2R_STANDARD_SCREEN = "s2r_standard_screen"
+private const val S2R_DUAL_DIRECTION_SCREEN = "s2r_dual_direction_screen"
diff --git a/wear/compose/integration-tests/navigation/src/main/java/androidx/wear/compose/integration/navigation/SwipeToRevealScreen.kt b/wear/compose/integration-tests/navigation/src/main/java/androidx/wear/compose/integration/navigation/SwipeToRevealScreen.kt
new file mode 100644
index 0000000..0b72ca6
--- /dev/null
+++ b/wear/compose/integration-tests/navigation/src/main/java/androidx/wear/compose/integration/navigation/SwipeToRevealScreen.kt
@@ -0,0 +1,101 @@
+/*
+ * 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.wear.compose.integration.navigation
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Delete
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.foundation.SwipeDirection
+import androidx.wear.compose.material3.Button
+import androidx.wear.compose.material3.Icon
+import androidx.wear.compose.material3.SwipeToReveal
+import androidx.wear.compose.material3.SwipeToRevealDefaults
+import androidx.wear.compose.material3.Text
+import androidx.wear.compose.material3.rememberRevealState
+
+@Composable
+fun SwipeToRevealSingleButtonWithAnchoring() {
+ Box(
+ modifier = Modifier.fillMaxSize().padding(horizontal = 10.dp),
+ contentAlignment = Alignment.Center
+ ) {
+ SwipeToReveal(
+ revealState =
+ rememberRevealState(
+ swipeDirection = SwipeDirection.RightToLeft,
+ anchorWidth = SwipeToRevealDefaults.SingleActionAnchorWidth,
+ ),
+ actions = {
+ primaryAction(
+ onClick = { /* This block is called when the primary action is executed. */ },
+ icon = { Icon(Icons.Outlined.Delete, contentDescription = "Delete") },
+ text = { Text("Delete") },
+ label = "Delete"
+ )
+ undoPrimaryAction(
+ onClick = { /* This block is called when the undo primary action is executed. */
+ },
+ text = { Text("Undo Delete") },
+ )
+ }
+ ) {
+ Button(modifier = Modifier.fillMaxWidth(), onClick = {}) {
+ Text("This Button has only one action", modifier = Modifier.fillMaxWidth())
+ }
+ }
+ }
+}
+
+@Composable
+fun SwipeToRevealBothDirectionsNonAnchoring() {
+ Box(
+ modifier = Modifier.fillMaxSize().padding(horizontal = 10.dp),
+ contentAlignment = Alignment.Center
+ ) {
+ SwipeToReveal(
+ revealState =
+ rememberRevealState(
+ swipeDirection = SwipeDirection.Both,
+ useAnchoredActions = false,
+ ),
+ actions = {
+ primaryAction(
+ onClick = { /* This block is called when the primary action is executed. */ },
+ icon = { Icon(Icons.Outlined.Delete, contentDescription = "Delete") },
+ text = { Text("Delete") },
+ label = "Delete"
+ )
+ undoPrimaryAction(
+ onClick = { /* This block is called when the undo primary action is executed. */
+ },
+ text = { Text("Undo Delete") },
+ )
+ }
+ ) {
+ Button(modifier = Modifier.fillMaxWidth(), onClick = {}) {
+ Text("This Button has only one action", modifier = Modifier.fillMaxWidth())
+ }
+ }
+ }
+}
diff --git a/wear/protolayout/protolayout-material3/api/current.txt b/wear/protolayout/protolayout-material3/api/current.txt
index eae2d58..fb54399 100644
--- a/wear/protolayout/protolayout-material3/api/current.txt
+++ b/wear/protolayout/protolayout-material3/api/current.txt
@@ -11,6 +11,15 @@
method public androidx.wear.protolayout.material3.AppCardStyle smallAppCardStyle();
}
+ public final class AvatarButtonStyle {
+ field public static final androidx.wear.protolayout.material3.AvatarButtonStyle.Companion Companion;
+ }
+
+ public static final class AvatarButtonStyle.Companion {
+ method public androidx.wear.protolayout.material3.AvatarButtonStyle defaultAvatarButtonStyle();
+ method public androidx.wear.protolayout.material3.AvatarButtonStyle largeAvatarButtonStyle();
+ }
+
public final class ButtonColors {
ctor public ButtonColors();
ctor public ButtonColors(optional androidx.wear.protolayout.types.LayoutColor containerColor, optional androidx.wear.protolayout.types.LayoutColor iconColor, optional androidx.wear.protolayout.types.LayoutColor labelColor, optional androidx.wear.protolayout.types.LayoutColor secondaryLabelColor);
@@ -48,6 +57,7 @@
}
public final class ButtonKt {
+ method public static androidx.wear.protolayout.LayoutElementBuilders.LayoutElement avatarButton(androidx.wear.protolayout.material3.MaterialScope, androidx.wear.protolayout.ModifiersBuilders.Clickable onClick, kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement> labelContent, kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement> avatarContent, optional androidx.wear.protolayout.modifiers.LayoutModifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement>? secondaryLabelContent, optional androidx.wear.protolayout.DimensionBuilders.ContainerDimension height, optional androidx.wear.protolayout.ModifiersBuilders.Corner shape, optional androidx.wear.protolayout.material3.ButtonColors colors, optional androidx.wear.protolayout.material3.AvatarButtonStyle style, optional int horizontalAlignment, optional androidx.wear.protolayout.ModifiersBuilders.Padding contentPadding);
method public static androidx.wear.protolayout.LayoutElementBuilders.LayoutElement button(androidx.wear.protolayout.material3.MaterialScope, androidx.wear.protolayout.ModifiersBuilders.Clickable onClick, kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement> labelContent, optional androidx.wear.protolayout.modifiers.LayoutModifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement>? secondaryLabelContent, optional kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement>? iconContent, optional androidx.wear.protolayout.DimensionBuilders.ContainerDimension width, optional androidx.wear.protolayout.DimensionBuilders.ContainerDimension height, optional androidx.wear.protolayout.ModifiersBuilders.Corner shape, optional androidx.wear.protolayout.material3.ButtonColors colors, optional kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement>? backgroundContent, optional androidx.wear.protolayout.material3.ButtonStyle style, optional int horizontalAlignment, optional androidx.wear.protolayout.ModifiersBuilders.Padding contentPadding);
method public static androidx.wear.protolayout.LayoutElementBuilders.LayoutElement compactButton(androidx.wear.protolayout.material3.MaterialScope, androidx.wear.protolayout.ModifiersBuilders.Clickable onClick, optional androidx.wear.protolayout.modifiers.LayoutModifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement>? labelContent, optional kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement>? iconContent, optional androidx.wear.protolayout.DimensionBuilders.ContainerDimension width, optional androidx.wear.protolayout.ModifiersBuilders.Corner shape, optional androidx.wear.protolayout.material3.ButtonColors colors, optional int horizontalAlignment, optional androidx.wear.protolayout.ModifiersBuilders.Padding contentPadding);
method public static androidx.wear.protolayout.LayoutElementBuilders.LayoutElement iconButton(androidx.wear.protolayout.material3.MaterialScope, androidx.wear.protolayout.ModifiersBuilders.Clickable onClick, kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement> iconContent, optional androidx.wear.protolayout.modifiers.LayoutModifier modifier, optional androidx.wear.protolayout.DimensionBuilders.ContainerDimension width, optional androidx.wear.protolayout.DimensionBuilders.ContainerDimension height, optional androidx.wear.protolayout.ModifiersBuilders.Corner shape, optional androidx.wear.protolayout.material3.ButtonColors colors, optional androidx.wear.protolayout.material3.IconButtonStyle style, optional androidx.wear.protolayout.ModifiersBuilders.Padding contentPadding);
@@ -248,7 +258,24 @@
}
public final class PrimaryLayoutKt {
- method public static androidx.wear.protolayout.LayoutElementBuilders.LayoutElement primaryLayout(androidx.wear.protolayout.material3.MaterialScope, kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement> mainSlot, optional kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement>? titleSlot, optional kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement>? bottomSlot, optional kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement>? labelForBottomSlot, optional androidx.wear.protolayout.ModifiersBuilders.Clickable? onClick);
+ method public static androidx.wear.protolayout.LayoutElementBuilders.LayoutElement primaryLayout(androidx.wear.protolayout.material3.MaterialScope, kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement> mainSlot, optional kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement>? titleSlot, optional kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement>? bottomSlot, optional kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement>? labelForBottomSlot, optional androidx.wear.protolayout.ModifiersBuilders.Clickable? onClick, optional androidx.wear.protolayout.material3.PrimaryLayoutMargins margins);
+ }
+
+ public abstract class PrimaryLayoutMargins {
+ field public static final androidx.wear.protolayout.material3.PrimaryLayoutMargins.Companion Companion;
+ field public static final androidx.wear.protolayout.material3.PrimaryLayoutMargins DEFAULT_PRIMARY_LAYOUT_MARGIN;
+ field public static final androidx.wear.protolayout.material3.PrimaryLayoutMargins MAX_PRIMARY_LAYOUT_MARGIN;
+ field public static final androidx.wear.protolayout.material3.PrimaryLayoutMargins MID_PRIMARY_LAYOUT_MARGIN;
+ field public static final androidx.wear.protolayout.material3.PrimaryLayoutMargins MIN_PRIMARY_LAYOUT_MARGIN;
+ }
+
+ public static final class PrimaryLayoutMargins.Companion {
+ method public androidx.wear.protolayout.material3.PrimaryLayoutMargins customizedPrimaryLayoutMargin(@FloatRange(from=0.0, to=1.0) float start, @FloatRange(from=0.0, to=1.0) float end);
+ method public androidx.wear.protolayout.material3.PrimaryLayoutMargins customizedPrimaryLayoutMargin(@FloatRange(from=0.0, to=1.0) float start, @FloatRange(from=0.0, to=1.0) float end, @FloatRange(from=0.0, to=1.0) float bottom);
+ property public final androidx.wear.protolayout.material3.PrimaryLayoutMargins DEFAULT_PRIMARY_LAYOUT_MARGIN;
+ property public final androidx.wear.protolayout.material3.PrimaryLayoutMargins MAX_PRIMARY_LAYOUT_MARGIN;
+ property public final androidx.wear.protolayout.material3.PrimaryLayoutMargins MID_PRIMARY_LAYOUT_MARGIN;
+ property public final androidx.wear.protolayout.material3.PrimaryLayoutMargins MIN_PRIMARY_LAYOUT_MARGIN;
}
public final class ProgressIndicatorColors {
diff --git a/wear/protolayout/protolayout-material3/api/restricted_current.txt b/wear/protolayout/protolayout-material3/api/restricted_current.txt
index eae2d58..fb54399 100644
--- a/wear/protolayout/protolayout-material3/api/restricted_current.txt
+++ b/wear/protolayout/protolayout-material3/api/restricted_current.txt
@@ -11,6 +11,15 @@
method public androidx.wear.protolayout.material3.AppCardStyle smallAppCardStyle();
}
+ public final class AvatarButtonStyle {
+ field public static final androidx.wear.protolayout.material3.AvatarButtonStyle.Companion Companion;
+ }
+
+ public static final class AvatarButtonStyle.Companion {
+ method public androidx.wear.protolayout.material3.AvatarButtonStyle defaultAvatarButtonStyle();
+ method public androidx.wear.protolayout.material3.AvatarButtonStyle largeAvatarButtonStyle();
+ }
+
public final class ButtonColors {
ctor public ButtonColors();
ctor public ButtonColors(optional androidx.wear.protolayout.types.LayoutColor containerColor, optional androidx.wear.protolayout.types.LayoutColor iconColor, optional androidx.wear.protolayout.types.LayoutColor labelColor, optional androidx.wear.protolayout.types.LayoutColor secondaryLabelColor);
@@ -48,6 +57,7 @@
}
public final class ButtonKt {
+ method public static androidx.wear.protolayout.LayoutElementBuilders.LayoutElement avatarButton(androidx.wear.protolayout.material3.MaterialScope, androidx.wear.protolayout.ModifiersBuilders.Clickable onClick, kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement> labelContent, kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement> avatarContent, optional androidx.wear.protolayout.modifiers.LayoutModifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement>? secondaryLabelContent, optional androidx.wear.protolayout.DimensionBuilders.ContainerDimension height, optional androidx.wear.protolayout.ModifiersBuilders.Corner shape, optional androidx.wear.protolayout.material3.ButtonColors colors, optional androidx.wear.protolayout.material3.AvatarButtonStyle style, optional int horizontalAlignment, optional androidx.wear.protolayout.ModifiersBuilders.Padding contentPadding);
method public static androidx.wear.protolayout.LayoutElementBuilders.LayoutElement button(androidx.wear.protolayout.material3.MaterialScope, androidx.wear.protolayout.ModifiersBuilders.Clickable onClick, kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement> labelContent, optional androidx.wear.protolayout.modifiers.LayoutModifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement>? secondaryLabelContent, optional kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement>? iconContent, optional androidx.wear.protolayout.DimensionBuilders.ContainerDimension width, optional androidx.wear.protolayout.DimensionBuilders.ContainerDimension height, optional androidx.wear.protolayout.ModifiersBuilders.Corner shape, optional androidx.wear.protolayout.material3.ButtonColors colors, optional kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement>? backgroundContent, optional androidx.wear.protolayout.material3.ButtonStyle style, optional int horizontalAlignment, optional androidx.wear.protolayout.ModifiersBuilders.Padding contentPadding);
method public static androidx.wear.protolayout.LayoutElementBuilders.LayoutElement compactButton(androidx.wear.protolayout.material3.MaterialScope, androidx.wear.protolayout.ModifiersBuilders.Clickable onClick, optional androidx.wear.protolayout.modifiers.LayoutModifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement>? labelContent, optional kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement>? iconContent, optional androidx.wear.protolayout.DimensionBuilders.ContainerDimension width, optional androidx.wear.protolayout.ModifiersBuilders.Corner shape, optional androidx.wear.protolayout.material3.ButtonColors colors, optional int horizontalAlignment, optional androidx.wear.protolayout.ModifiersBuilders.Padding contentPadding);
method public static androidx.wear.protolayout.LayoutElementBuilders.LayoutElement iconButton(androidx.wear.protolayout.material3.MaterialScope, androidx.wear.protolayout.ModifiersBuilders.Clickable onClick, kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement> iconContent, optional androidx.wear.protolayout.modifiers.LayoutModifier modifier, optional androidx.wear.protolayout.DimensionBuilders.ContainerDimension width, optional androidx.wear.protolayout.DimensionBuilders.ContainerDimension height, optional androidx.wear.protolayout.ModifiersBuilders.Corner shape, optional androidx.wear.protolayout.material3.ButtonColors colors, optional androidx.wear.protolayout.material3.IconButtonStyle style, optional androidx.wear.protolayout.ModifiersBuilders.Padding contentPadding);
@@ -248,7 +258,24 @@
}
public final class PrimaryLayoutKt {
- method public static androidx.wear.protolayout.LayoutElementBuilders.LayoutElement primaryLayout(androidx.wear.protolayout.material3.MaterialScope, kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement> mainSlot, optional kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement>? titleSlot, optional kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement>? bottomSlot, optional kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement>? labelForBottomSlot, optional androidx.wear.protolayout.ModifiersBuilders.Clickable? onClick);
+ method public static androidx.wear.protolayout.LayoutElementBuilders.LayoutElement primaryLayout(androidx.wear.protolayout.material3.MaterialScope, kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement> mainSlot, optional kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement>? titleSlot, optional kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement>? bottomSlot, optional kotlin.jvm.functions.Function1<? super androidx.wear.protolayout.material3.MaterialScope,? extends androidx.wear.protolayout.LayoutElementBuilders.LayoutElement>? labelForBottomSlot, optional androidx.wear.protolayout.ModifiersBuilders.Clickable? onClick, optional androidx.wear.protolayout.material3.PrimaryLayoutMargins margins);
+ }
+
+ public abstract class PrimaryLayoutMargins {
+ field public static final androidx.wear.protolayout.material3.PrimaryLayoutMargins.Companion Companion;
+ field public static final androidx.wear.protolayout.material3.PrimaryLayoutMargins DEFAULT_PRIMARY_LAYOUT_MARGIN;
+ field public static final androidx.wear.protolayout.material3.PrimaryLayoutMargins MAX_PRIMARY_LAYOUT_MARGIN;
+ field public static final androidx.wear.protolayout.material3.PrimaryLayoutMargins MID_PRIMARY_LAYOUT_MARGIN;
+ field public static final androidx.wear.protolayout.material3.PrimaryLayoutMargins MIN_PRIMARY_LAYOUT_MARGIN;
+ }
+
+ public static final class PrimaryLayoutMargins.Companion {
+ method public androidx.wear.protolayout.material3.PrimaryLayoutMargins customizedPrimaryLayoutMargin(@FloatRange(from=0.0, to=1.0) float start, @FloatRange(from=0.0, to=1.0) float end);
+ method public androidx.wear.protolayout.material3.PrimaryLayoutMargins customizedPrimaryLayoutMargin(@FloatRange(from=0.0, to=1.0) float start, @FloatRange(from=0.0, to=1.0) float end, @FloatRange(from=0.0, to=1.0) float bottom);
+ property public final androidx.wear.protolayout.material3.PrimaryLayoutMargins DEFAULT_PRIMARY_LAYOUT_MARGIN;
+ property public final androidx.wear.protolayout.material3.PrimaryLayoutMargins MAX_PRIMARY_LAYOUT_MARGIN;
+ property public final androidx.wear.protolayout.material3.PrimaryLayoutMargins MID_PRIMARY_LAYOUT_MARGIN;
+ property public final androidx.wear.protolayout.material3.PrimaryLayoutMargins MIN_PRIMARY_LAYOUT_MARGIN;
}
public final class ProgressIndicatorColors {
diff --git a/wear/protolayout/protolayout-material3/samples/src/main/java/androidx/wear/protolayout/material3/samples/Material3ComponentsSample.kt b/wear/protolayout/protolayout-material3/samples/src/main/java/androidx/wear/protolayout/material3/samples/Material3ComponentsSample.kt
index e5d1f6a..64bd836 100644
--- a/wear/protolayout/protolayout-material3/samples/src/main/java/androidx/wear/protolayout/material3/samples/Material3ComponentsSample.kt
+++ b/wear/protolayout/protolayout-material3/samples/src/main/java/androidx/wear/protolayout/material3/samples/Material3ComponentsSample.kt
@@ -38,9 +38,13 @@
import androidx.wear.protolayout.material3.DataCardStyle.Companion.extraLargeDataCardStyle
import androidx.wear.protolayout.material3.DataCardStyle.Companion.largeCompactDataCardStyle
import androidx.wear.protolayout.material3.GraphicDataCardStyle.Companion.largeGraphicDataCardStyle
+import androidx.wear.protolayout.material3.MaterialScope
+import androidx.wear.protolayout.material3.PrimaryLayoutMargins.Companion.MAX_PRIMARY_LAYOUT_MARGIN
import androidx.wear.protolayout.material3.TitleCardStyle.Companion.largeTitleCardStyle
import androidx.wear.protolayout.material3.Typography
import androidx.wear.protolayout.material3.appCard
+import androidx.wear.protolayout.material3.avatarButton
+import androidx.wear.protolayout.material3.avatarImage
import androidx.wear.protolayout.material3.backgroundImage
import androidx.wear.protolayout.material3.button
import androidx.wear.protolayout.material3.buttonGroup
@@ -145,7 +149,7 @@
ModifiersBuilders.Modifiers.Builder()
.setBackground(
ModifiersBuilders.Background.Builder()
- .setCorner(shapes.full)
+ .setCorner(shapes.small)
.build()
)
.build()
@@ -154,6 +158,8 @@
}
}
},
+ // Adjust margins as the corner of the inner content is on the square side.
+ margins = MAX_PRIMARY_LAYOUT_MARGIN,
bottomSlot = {
iconEdgeButton(
onClick = clickable,
@@ -464,6 +470,16 @@
}
@Sampled
+fun MaterialScope.avatarButtonSample() =
+ avatarButton(
+ onClick = clickable(),
+ modifier = LayoutModifier.contentDescription("Pill button"),
+ avatarContent = { avatarImage("id") },
+ labelContent = { text("Primary label".layoutString) },
+ secondaryLabelContent = { text("Secondary label".layoutString) },
+ )
+
+@Sampled
fun compactButtonsSample(
context: Context,
deviceConfiguration: DeviceParameters,
diff --git a/wear/protolayout/protolayout-material3/src/androidTest/java/androidx/wear/protolayout/material3/TestCasesGenerator.kt b/wear/protolayout/protolayout-material3/src/androidTest/java/androidx/wear/protolayout/material3/TestCasesGenerator.kt
index 4537676..b631b44 100644
--- a/wear/protolayout/protolayout-material3/src/androidTest/java/androidx/wear/protolayout/material3/TestCasesGenerator.kt
+++ b/wear/protolayout/protolayout-material3/src/androidTest/java/androidx/wear/protolayout/material3/TestCasesGenerator.kt
@@ -24,8 +24,10 @@
import androidx.wear.protolayout.DimensionBuilders.expand
import androidx.wear.protolayout.LayoutElementBuilders
import androidx.wear.protolayout.LayoutElementBuilders.Column
+import androidx.wear.protolayout.LayoutElementBuilders.HORIZONTAL_ALIGN_END
import androidx.wear.protolayout.expression.VersionBuilders.VersionInfo
import androidx.wear.protolayout.material3.AppCardStyle.Companion.largeAppCardStyle
+import androidx.wear.protolayout.material3.AvatarButtonStyle.Companion.largeAvatarButtonStyle
import androidx.wear.protolayout.material3.ButtonDefaults.filledButtonColors
import androidx.wear.protolayout.material3.ButtonDefaults.filledTonalButtonColors
import androidx.wear.protolayout.material3.ButtonDefaults.filledVariantButtonColors
@@ -36,10 +38,11 @@
import androidx.wear.protolayout.material3.DataCardStyle.Companion.smallCompactDataCardStyle
import androidx.wear.protolayout.material3.IconButtonStyle.Companion.largeIconButtonStyle
import androidx.wear.protolayout.material3.MaterialGoldenTest.Companion.pxToDp
+import androidx.wear.protolayout.material3.PrimaryLayoutMargins.Companion.MAX_PRIMARY_LAYOUT_MARGIN
+import androidx.wear.protolayout.material3.PrimaryLayoutMargins.Companion.MIN_PRIMARY_LAYOUT_MARGIN
import androidx.wear.protolayout.material3.TextButtonStyle.Companion.extraLargeTextButtonStyle
import androidx.wear.protolayout.material3.TextButtonStyle.Companion.largeTextButtonStyle
import androidx.wear.protolayout.material3.TextButtonStyle.Companion.smallTextButtonStyle
-import androidx.wear.protolayout.material3.TitleContentPlacementInDataCard.Companion.Bottom
import androidx.wear.protolayout.modifiers.LayoutModifier
import androidx.wear.protolayout.modifiers.clickable
import androidx.wear.protolayout.modifiers.clip
@@ -135,6 +138,7 @@
graphic = { circularProgressIndicator(staticProgress = 0.5F) }
)
},
+ margins = MIN_PRIMARY_LAYOUT_MARGIN
)
}
testCases["primarylayout_edgebuttonfilledvariant_iconoverride_golden$NORMAL_SCALE_SUFFIX"] =
@@ -153,17 +157,9 @@
title = { text("MM".layoutString) },
content = { text("Min".layoutString) },
secondaryIcon = { icon(ICON_ID) },
- shape = shapes.full
- )
- }
- buttonGroupItem {
- iconDataCard(
- onClick = clickable,
- modifier = LayoutModifier.contentDescription("Data Card"),
- title = { text("MM".layoutString) },
- content = { text("Min".layoutString) },
- secondaryIcon = { icon(ICON_ID) },
- titleContentPlacement = Bottom
+ shape = shapes.none,
+ width = expand(),
+ height = expand()
)
}
buttonGroupItem {
@@ -178,11 +174,13 @@
backgroundColor = colorScheme.onSecondary,
titleColor = colorScheme.secondary,
contentColor = colorScheme.secondaryDim
- )
+ ),
+ shape = shapes.full
)
}
}
},
+ margins = MAX_PRIMARY_LAYOUT_MARGIN,
bottomSlot = {
textEdgeButton(
onClick = clickable,
@@ -392,6 +390,47 @@
},
)
}
+ testCases["primarylayout_nobottomslotnotitle_avatarbuttons_golden$NORMAL_SCALE_SUFFIX"] =
+ materialScope(
+ ApplicationProvider.getApplicationContext(),
+ deviceParameters,
+ allowDynamicTheme = false
+ ) {
+ primaryLayout(
+ mainSlot = {
+ Column.Builder()
+ .setWidth(expand())
+ .setHeight(expand())
+ .addContent(
+ avatarButton(
+ onClick = clickable,
+ labelContent = { text("Primary label".layoutString) },
+ secondaryLabelContent = {
+ text("Secondary label".layoutString)
+ },
+ avatarContent = { avatarImage(IMAGE_ID) },
+ )
+ )
+ .addContent(DEFAULT_SPACER_BETWEEN_BUTTON_GROUPS)
+ .addContent(
+ avatarButton(
+ onClick = clickable,
+ labelContent = {
+ text("Primary label overflowing".layoutString)
+ },
+ secondaryLabelContent = {
+ text("Secondary label overflowing".layoutString)
+ },
+ avatarContent = { avatarImage(IMAGE_ID) },
+ height = expand(),
+ style = largeAvatarButtonStyle(),
+ horizontalAlignment = HORIZONTAL_ALIGN_END
+ )
+ )
+ .build()
+ },
+ )
+ }
testCases["primarylayout_oneslotbuttons_golden$NORMAL_SCALE_SUFFIX"] =
materialScope(
ApplicationProvider.getApplicationContext(),
@@ -473,7 +512,10 @@
deviceParameters,
allowDynamicTheme = false
) {
- primaryLayout(mainSlot = { progressIndicatorGroup() })
+ primaryLayout(
+ mainSlot = { progressIndicatorGroup() },
+ margins = MIN_PRIMARY_LAYOUT_MARGIN
+ )
}
testCases["primarylayout_circularprogressindicators_fallback__golden$NORMAL_SCALE_SUFFIX"] =
@@ -486,6 +528,7 @@
) {
primaryLayout(
mainSlot = { progressIndicatorGroup() },
+ margins = MIN_PRIMARY_LAYOUT_MARGIN,
bottomSlot = {
iconEdgeButton(
onClick = clickable,
diff --git a/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/Button.kt b/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/Button.kt
index c6d834e..8940585 100644
--- a/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/Button.kt
+++ b/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/Button.kt
@@ -16,10 +16,12 @@
package androidx.wear.protolayout.material3
+import androidx.wear.protolayout.DimensionBuilders
import androidx.wear.protolayout.DimensionBuilders.ContainerDimension
import androidx.wear.protolayout.DimensionBuilders.dp
import androidx.wear.protolayout.DimensionBuilders.expand
import androidx.wear.protolayout.DimensionBuilders.weight
+import androidx.wear.protolayout.LayoutElementBuilders
import androidx.wear.protolayout.LayoutElementBuilders.Box
import androidx.wear.protolayout.LayoutElementBuilders.HORIZONTAL_ALIGN_CENTER
import androidx.wear.protolayout.LayoutElementBuilders.HORIZONTAL_ALIGN_END
@@ -29,9 +31,11 @@
import androidx.wear.protolayout.ModifiersBuilders.Clickable
import androidx.wear.protolayout.ModifiersBuilders.Corner
import androidx.wear.protolayout.ModifiersBuilders.Padding
+import androidx.wear.protolayout.material3.AvatarButtonStyle.Companion.defaultAvatarButtonStyle
import androidx.wear.protolayout.material3.ButtonDefaults.DEFAULT_CONTENT_PADDING
import androidx.wear.protolayout.material3.ButtonDefaults.IMAGE_BUTTON_DEFAULT_SIZE_DP
import androidx.wear.protolayout.material3.ButtonDefaults.METADATA_TAG_BUTTON
+import androidx.wear.protolayout.material3.ButtonDefaults.buildContentForAvatarButton
import androidx.wear.protolayout.material3.ButtonDefaults.buildContentForCompactButton
import androidx.wear.protolayout.material3.ButtonDefaults.buildContentForPillShapeButton
import androidx.wear.protolayout.material3.ButtonDefaults.filledButtonColors
@@ -42,6 +46,8 @@
import androidx.wear.protolayout.material3.CompactButtonStyle.COMPACT_BUTTON_ICON_SIZE_SMALL_DP
import androidx.wear.protolayout.material3.CompactButtonStyle.COMPACT_BUTTON_LABEL_TYPOGRAPHY
import androidx.wear.protolayout.material3.IconButtonStyle.Companion.defaultIconButtonStyle
+import androidx.wear.protolayout.material3.PredefinedPrimaryLayoutMargins.maxPrimaryLayoutMargins
+import androidx.wear.protolayout.material3.PredefinedPrimaryLayoutMargins.minPrimaryLayoutMargins
import androidx.wear.protolayout.material3.TextButtonStyle.Companion.defaultTextButtonStyle
import androidx.wear.protolayout.modifiers.LayoutModifier
import androidx.wear.protolayout.modifiers.background
@@ -62,7 +68,11 @@
* the associated action.
* @param modifier Modifiers to set to this element. It's highly recommended to set a content
* description using [contentDescription].
- * @param shape Defines the button's shape, in other words the corner radius for this button.
+ * @param shape Defines the button's shape, in other words the corner radius for this button. If
+ * changing these to radius smaller than [Shapes.medium], it is important to adjusts the margins
+ * of [primaryLayout] used to accommodate for more space, for example by using
+ * [maxPrimaryLayoutMargins]. Or, if the [shape] is set to [Shapes.full], using
+ * [minPrimaryLayoutMargins] can be considered.
* @param width The width of this button. It's highly recommended to set this to [expand] or
* [weight]
* @param height The height of this button. It's highly recommended to set this to [expand] or
@@ -82,7 +92,6 @@
* @sample androidx.wear.protolayout.material3.samples.oneSlotButtonsSample
*/
// TODO: b/346958146 - Link Button visuals in DAC
-// TODO: b/373578620 - Add how corners affects margins in the layout.
public fun MaterialScope.iconButton(
onClick: Clickable,
iconContent: (MaterialScope.() -> LayoutElement),
@@ -121,7 +130,11 @@
* the associated action.
* @param modifier Modifiers to set to this element. It's highly recommended to set a content
* description using [contentDescription].
- * @param shape Defines the button's shape, in other words the corner radius for this button.
+ * @param shape Defines the button's shape, in other words the corner radius for this button. If
+ * changing these to radius smaller than [Shapes.medium], it is important to adjusts the margins
+ * of [primaryLayout] used to accommodate for more space, for example by using
+ * [maxPrimaryLayoutMargins]. Or, if the [shape] is set to [Shapes.full], using
+ * [minPrimaryLayoutMargins] can be considered.
* @param width The width of this button. It's highly recommended to set this to [expand] or
* [weight]
* @param height The height of this button. It's highly recommended to set this to [expand] or
@@ -142,7 +155,6 @@
* @sample androidx.wear.protolayout.material3.samples.oneSlotButtonsSample
*/
// TODO: b/346958146 - Link Button visuals in DAC
-// TODO: b/373578620 - Add how corners affects margins in the layout.
public fun MaterialScope.textButton(
onClick: Clickable,
labelContent: (MaterialScope.() -> LayoutElement),
@@ -186,7 +198,11 @@
* recommended to use default styling that is automatically provided by only calling [text].
* @param iconContent The icon slot for content displayed in this button. It is recommended to use
* default styling that is automatically provided by only calling [icon] with the resource ID.
- * @param shape Defines the button's shape, in other words the corner radius for this button.
+ * @param shape Defines the button's shape, in other words the corner radius for this button. If
+ * changing these to radius smaller than [Shapes.medium], it is important to adjusts the margins
+ * of [primaryLayout] used to accommodate for more space, for example by using
+ * [maxPrimaryLayoutMargins]. Or, if the [shape] is set to [Shapes.full], using
+ * [minPrimaryLayoutMargins] can be considered.
* @param width The width of this button. It's highly recommended to set this to [expand] or
* [weight]
* @param height The height of this button. It's highly recommended to set this to [expand] or
@@ -279,6 +295,119 @@
)
/**
+ * Opinionated ProtoLayout Material3 pill shape avatar button that offers up to three slots to take
+ * content representing vertically stacked label and secondary label, and an image (avatar) next to
+ * it.
+ *
+ * Difference from the [button] is that this one takes an image instead of an icon and spaces the
+ * content proportionally, so that edge of the button nicely hugs the avatar image.
+ *
+ * @param onClick Associated [Clickable] for click events. When the button is clicked it will fire
+ * the associated action.
+ * @param labelContent The text slot for content displayed in this button. It is recommended to use
+ * default styling that is automatically provided by only calling [text].
+ * @param modifier Modifiers to set to this element. It's highly recommended to set a content
+ * description using [contentDescription].
+ * @param secondaryLabelContent The text slot for content displayed in this button. It is
+ * recommended to use default styling that is automatically provided by only calling [text].
+ * @param avatarContent The avatar slot for content displayed in this button. It is recommended to
+ * use default styling that is automatically provided by only calling [avatarImage] with the
+ * resource ID. Width and height of this element should be set to [expand].
+ * @param shape Defines the button's shape, in other words the corner radius for this button. If
+ * changing these to radius smaller than [Shapes.medium], it is important to adjusts the margins
+ * of [primaryLayout] used to accommodate for more space, for example by using
+ * [maxPrimaryLayoutMargins]. Or, if the [shape] is set to [Shapes.full], using
+ * [minPrimaryLayoutMargins] can be considered.
+ * @param height The height of this button. It's highly recommended to set this to [expand] or
+ * [weight]
+ * @param colors The colors used for this button. If not set, [ButtonDefaults.filledButtonColors]
+ * will be used as high emphasis button. Other recommended colors are
+ * [ButtonDefaults.filledTonalButtonColors] and [ButtonDefaults.filledVariantButtonColors]. If
+ * using custom colors, it is important to choose a color pair from same role to ensure
+ * accessibility with sufficient color contrast.
+ * @param style The style which provides the attribute values required for constructing this pill
+ * shape button and its inner content. It also provides default style for the inner content, that
+ * can be overridden by each content slot.
+ * @param horizontalAlignment The horizontal placement of the [avatarContent]. This should be
+ * [HORIZONTAL_ALIGN_START] to place the [avatarContent] on the start side of the button, or
+ * [HORIZONTAL_ALIGN_END] to place in on the end side. [HORIZONTAL_ALIGN_CENTER] will be ignored
+ * and replaced with [HORIZONTAL_ALIGN_START].
+ * @param contentPadding The inner padding used to prevent inner content from being too close to the
+ * button's edge. It's highly recommended to keep the default. Only vertical values would be used,
+ * as horizontally elements are spaced out proportionally to the buttons width.
+ * @sample androidx.wear.protolayout.material3.samples.avatarButtonSample
+ */
+// TODO: b/346958146 - Link Button visuals in DAC
+public fun MaterialScope.avatarButton(
+ onClick: Clickable,
+ labelContent: (MaterialScope.() -> LayoutElement),
+ avatarContent: (MaterialScope.() -> LayoutElement),
+ modifier: LayoutModifier = LayoutModifier,
+ secondaryLabelContent: (MaterialScope.() -> LayoutElement)? = null,
+ height: ContainerDimension = wrapWithMinTapTargetDimension(),
+ shape: Corner = shapes.full,
+ colors: ButtonColors = filledButtonColors(),
+ style: AvatarButtonStyle = defaultAvatarButtonStyle(),
+ @HorizontalAlignment horizontalAlignment: Int = HORIZONTAL_ALIGN_START,
+ contentPadding: Padding = style.innerVerticalPadding
+): LayoutElement =
+ buttonContainer(
+ onClick = onClick,
+ modifier = modifier.background(color = colors.containerColor, corner = shape),
+ width = expand(),
+ height = height,
+ contentPadding = contentPadding,
+ content = {
+ buildContentForAvatarButton(
+ label =
+ withStyle(
+ defaultTextElementStyle =
+ TextElementStyle(
+ typography = style.labelTypography,
+ color = colors.labelColor,
+ multilineAlignment =
+ HORIZONTAL_ALIGN_START.horizontalAlignToTextAlign()
+ )
+ )
+ .labelContent(),
+ secondaryLabel =
+ secondaryLabelContent?.let {
+ withStyle(
+ defaultTextElementStyle =
+ TextElementStyle(
+ typography = style.secondaryLabelTypography,
+ color = colors.secondaryLabelColor,
+ multilineAlignment =
+ HORIZONTAL_ALIGN_START.horizontalAlignToTextAlign()
+ )
+ )
+ .secondaryLabelContent()
+ },
+ avatar =
+ withStyle(
+ defaultAvatarImageStyle =
+ AvatarImageStyle(
+ width = expand(),
+ // We want height to be same as the calculated width
+ height =
+ DimensionBuilders.ProportionalDimensionProp.Builder()
+ .setAspectRatioWidth(1)
+ .setAspectRatioHeight(1)
+ .build(),
+ contentScaleMode = LayoutElementBuilders.CONTENT_SCALE_MODE_FIT
+ )
+ )
+ .avatarContent(),
+ horizontalAlignment =
+ if (horizontalAlignment == HORIZONTAL_ALIGN_CENTER) HORIZONTAL_ALIGN_START
+ else horizontalAlignment,
+ style = style,
+ height = height
+ )
+ }
+ )
+
+/**
* ProtoLayout Material3 clickable image button that doesn't offer additional slots, only image (for
* example [backgroundImage] as a background.
*
@@ -331,7 +460,11 @@
* parameter.
* @param iconContent The icon slot for content displayed in this button. It is recommended to use
* default styling that is automatically provided by only calling [icon] with the resource ID.
- * @param shape Defines the button's shape, in other words the corner radius for this button.
+ * @param shape Defines the button's shape, in other words the corner radius for this button. If
+ * changing these to radius smaller than [Shapes.medium], it is important to adjusts the margins
+ * of [primaryLayout] used to accommodate for more space, for example by using
+ * [maxPrimaryLayoutMargins]. Or, if the [shape] is set to [Shapes.full], using
+ * [minPrimaryLayoutMargins] can be considered.
* @param width The width of this button. It's highly recommended to set this to [expand] or
* [weight]
* @param colors The colors used for this button. If not set, [ButtonDefaults.filledButtonColors]
diff --git a/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/ButtonDefaults.kt b/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/ButtonDefaults.kt
index 491d263..dbb8d68 100644
--- a/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/ButtonDefaults.kt
+++ b/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/ButtonDefaults.kt
@@ -19,10 +19,14 @@
import android.graphics.Color
import androidx.annotation.Dimension
import androidx.annotation.Dimension.Companion.DP
+import androidx.annotation.FloatRange
import androidx.wear.protolayout.DimensionBuilders
+import androidx.wear.protolayout.DimensionBuilders.ContainerDimension
import androidx.wear.protolayout.DimensionBuilders.expand
+import androidx.wear.protolayout.DimensionBuilders.weight
import androidx.wear.protolayout.LayoutElementBuilders.Box
import androidx.wear.protolayout.LayoutElementBuilders.Column
+import androidx.wear.protolayout.LayoutElementBuilders.HORIZONTAL_ALIGN_END
import androidx.wear.protolayout.LayoutElementBuilders.HORIZONTAL_ALIGN_START
import androidx.wear.protolayout.LayoutElementBuilders.HorizontalAlignment
import androidx.wear.protolayout.LayoutElementBuilders.LayoutElement
@@ -81,6 +85,95 @@
}
/**
+ * Returns [LayoutElement] describing the inner content for the avatar shape button.
+ *
+ * This is a [Row] containing the following:
+ * * avatar
+ * * spacing if icon is present
+ * * labels that are in [Column]
+ *
+ * Additionally, horizontal padding and spacing for avatar and labels is weight based.
+ *
+ * [horizontalAlignment] defines side that avatar is.
+ */
+ internal fun buildContentForAvatarButton(
+ avatar: LayoutElement,
+ label: LayoutElement,
+ secondaryLabel: LayoutElement?,
+ @HorizontalAlignment horizontalAlignment: Int,
+ style: AvatarButtonStyle,
+ height: ContainerDimension,
+ ): LayoutElement {
+ val verticalElementBuilder: Column.Builder =
+ Column.Builder().setWidth(expand()).setHorizontalAlignment(HORIZONTAL_ALIGN_START)
+ val horizontalElementBuilder: Row.Builder =
+ Row.Builder().setWidth(expand()).setHeight(height)
+
+ ContainerWithSpacersBuilder<LayoutElement>(
+ { it: LayoutElement? -> verticalElementBuilder.addContent(it!!) },
+ label
+ )
+ .addElement(secondaryLabel, horizontalSpacer(style.labelsSpaceDp))
+
+ // Side padding - start
+ horizontalElementBuilder.addContent(
+ verticalSpacer(
+ weight(
+ if (horizontalAlignment == HORIZONTAL_ALIGN_START) style.avatarPaddingWeight
+ else style.labelsPaddingWeight
+ )
+ )
+ )
+
+ // Wrap avatar in expandable box with weights
+ val wrapAvatar =
+ Box.Builder()
+ .setWidth(weight(style.avatarSizeWeight))
+ .setHeight(height)
+ .addContent(avatar)
+ .build()
+
+ if (horizontalAlignment == HORIZONTAL_ALIGN_START) {
+ horizontalElementBuilder.addContent(wrapAvatar)
+ horizontalElementBuilder.addContent(verticalSpacer(style.avatarToLabelsSpaceDp))
+ }
+
+ // Labels
+ horizontalElementBuilder.addContent(
+ Box.Builder()
+ .setHorizontalAlignment(HORIZONTAL_ALIGN_START)
+ // Remaining % from 100% is for labels
+ .setWidth(
+ weight(
+ 100 -
+ style.avatarPaddingWeight -
+ style.labelsPaddingWeight -
+ style.avatarSizeWeight
+ )
+ )
+ .addContent(verticalElementBuilder.build())
+ .build()
+ )
+
+ if (horizontalAlignment == HORIZONTAL_ALIGN_END) {
+ horizontalElementBuilder.addContent(verticalSpacer(style.avatarToLabelsSpaceDp))
+ horizontalElementBuilder.addContent(wrapAvatar)
+ }
+
+ // Side padding - end
+ horizontalElementBuilder.addContent(
+ verticalSpacer(
+ weight(
+ if (horizontalAlignment == HORIZONTAL_ALIGN_START) style.labelsPaddingWeight
+ else style.avatarPaddingWeight
+ )
+ )
+ )
+
+ return horizontalElementBuilder.build()
+ }
+
+ /**
* Returns [LayoutElement] describing the inner content for the compact button.
*
* This is a [Row] wrapped inside of the Box for alignment, containing the following:
@@ -283,3 +376,50 @@
)
}
}
+
+/** Provides style values for the avatar button component. */
+public class AvatarButtonStyle
+internal constructor(
+ @TypographyToken internal val labelTypography: Int,
+ @TypographyToken internal val secondaryLabelTypography: Int,
+ @FloatRange(from = 0.0, to = 100.0) internal val avatarSizeWeight: Float,
+ @FloatRange(from = 0.0, to = 100.0) internal val avatarPaddingWeight: Float,
+ @FloatRange(from = 0.0, to = 100.0) internal val labelsPaddingWeight: Float,
+ internal val innerVerticalPadding: Padding,
+ @Dimension(DP) internal val avatarToLabelsSpaceDp: Int,
+ @Dimension(DP) internal val labelsSpaceDp: Int,
+) {
+ public companion object {
+ /**
+ * Default style variation for the [avatarButton] where all opinionated inner content is
+ * displayed in a medium size.
+ */
+ public fun defaultAvatarButtonStyle(): AvatarButtonStyle =
+ AvatarButtonStyle(
+ labelTypography = Typography.LABEL_MEDIUM,
+ secondaryLabelTypography = Typography.BODY_SMALL,
+ avatarSizeWeight = 19.6f,
+ avatarPaddingWeight = 4.16f,
+ labelsPaddingWeight = 7.1f,
+ innerVerticalPadding = padding(vertical = 8f, horizontal = Float.NaN),
+ avatarToLabelsSpaceDp = 6,
+ labelsSpaceDp = 0
+ )
+
+ /**
+ * Default style variation for the [avatarButton] where all opinionated inner content is
+ * displayed in a large size.
+ */
+ public fun largeAvatarButtonStyle(): AvatarButtonStyle =
+ AvatarButtonStyle(
+ labelTypography = Typography.TITLE_MEDIUM,
+ secondaryLabelTypography = Typography.LABEL_SMALL,
+ avatarSizeWeight = 23.15f,
+ avatarPaddingWeight = 2.1f,
+ labelsPaddingWeight = 6f,
+ innerVerticalPadding = padding(vertical = 6f, horizontal = Float.NaN),
+ avatarToLabelsSpaceDp = 8,
+ labelsSpaceDp = 0
+ )
+ }
+}
diff --git a/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/ButtonGroup.kt b/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/ButtonGroup.kt
index eb1ec09..f38013d 100644
--- a/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/ButtonGroup.kt
+++ b/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/ButtonGroup.kt
@@ -64,6 +64,8 @@
* @param spacing The amount of spacing between buttons
* @param content The content for each child. The UX guidance is to use no more than 3 elements
* within a this button group.
+ * @sample androidx.wear.protolayout.material3.samples.dataCardSample
+ * @sample androidx.wear.protolayout.material3.samples.oneSlotButtonsSample
*/
// TODO: b/346958146 - Link visuals once they are available.
public fun MaterialScope.buttonGroup(
diff --git a/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/Card.kt b/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/Card.kt
index 951a192..ef9b1cc 100644
--- a/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/Card.kt
+++ b/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/Card.kt
@@ -38,6 +38,8 @@
import androidx.wear.protolayout.material3.DataCardStyle.Companion.defaultDataCardStyle
import androidx.wear.protolayout.material3.GraphicDataCardDefaults.buildContentForGraphicDataCard
import androidx.wear.protolayout.material3.GraphicDataCardStyle.Companion.defaultGraphicDataCardStyle
+import androidx.wear.protolayout.material3.PredefinedPrimaryLayoutMargins.maxPrimaryLayoutMargins
+import androidx.wear.protolayout.material3.PredefinedPrimaryLayoutMargins.minPrimaryLayoutMargins
import androidx.wear.protolayout.material3.TitleCardDefaults.buildContentForTitleCard
import androidx.wear.protolayout.material3.TitleCardStyle.Companion.defaultTitleCardStyle
import androidx.wear.protolayout.modifiers.LayoutModifier
@@ -63,7 +65,11 @@
* expected to be a short piece of text. Uses [CardColors.timeColor] color by default.
* @param height The height of this card. It's highly recommended to set this to [expand] or
* [weight].
- * @param shape Defines the card's shape, in other words the corner radius for this card.
+ * @param shape Defines the card's shape, in other words the corner radius for this card. If
+ * changing these to radius smaller than [Shapes.medium], it is important to adjusts the margins
+ * of [primaryLayout] used to accommodate for more space, for example by using
+ * [maxPrimaryLayoutMargins]. Or, if the [shape] is set to [Shapes.full], using
+ * [minPrimaryLayoutMargins] can be considered.
* @param colors The colors to be used for a background and inner content of this card. If the
* background image is also specified, the image will be laid out on top of the background color.
* In case of the fully opaque background image, then the background color will not be shown.
@@ -87,7 +93,6 @@
* @sample androidx.wear.protolayout.material3.samples.titleCardSample
*/
// TODO: b/346958146 - link Card visuals in DAC
-// TODO: b/373578620 - Add how corners affects margins in the layout.
public fun MaterialScope.titleCard(
onClick: Clickable,
title: (MaterialScope.() -> LayoutElement),
@@ -186,7 +191,11 @@
* @param height The height of this card. It's highly recommended to leave this with default value
* as `wrap` if there's only 1 card on the screen. If there are two cards, it is highly
* recommended to set this to [expand] and use the smaller styles.
- * @param shape Defines the card's shape, in other words the corner radius for this card.
+ * @param shape Defines the card's shape, in other words the corner radius for this card. If
+ * changing these to radius smaller than [Shapes.medium], it is important to adjusts the margins
+ * of [primaryLayout] used to accommodate for more space, for example by using
+ * [maxPrimaryLayoutMargins]. Or, if the [shape] is set to [Shapes.full], using
+ * [minPrimaryLayoutMargins] can be considered.
* @param colors The colors to be used for a background and inner content of this card. If the
* background image is also specified, the image will be laid out on top of the background color.
* In case of the fully opaque background image, then the background color will not be shown.
@@ -207,7 +216,6 @@
* @sample androidx.wear.protolayout.material3.samples.appCardSample
*/
// TODO: b/346958146 - link Card visuals in DAC
-// TODO: b/373578620 - Add how corners affects margins in the layout.
public fun MaterialScope.appCard(
onClick: Clickable,
title: (MaterialScope.() -> LayoutElement),
@@ -313,7 +321,11 @@
* for the most optimal experience across different screen sizes.
* @param height The height of this card. It's highly recommended to set this to [expand] for the
* most optimal experience across different screen sizes.
- * @param shape Defines the card's shape, in other words the corner radius for this card.
+ * @param shape Defines the card's shape, in other words the corner radius for this card. If
+ * changing these to radius smaller than [Shapes.medium], it is important to adjusts the margins
+ * of [primaryLayout] used to accommodate for more space, for example by using
+ * [maxPrimaryLayoutMargins]. Or, if the [shape] is set to [Shapes.full], using
+ * [minPrimaryLayoutMargins] can be considered.
* @param colors The colors to be used for a background and inner content of this card. If the
* background image is also specified, the image will be laid out on top of the background color.
* In case of the fully opaque background image, then the background color will not be shown.
@@ -339,7 +351,6 @@
* @sample androidx.wear.protolayout.material3.samples.dataCardSample
*/
// TODO: b/346958146 - link Card visuals in DAC
-// TODO: b/373578620 - Add how corners affects margins in the layout.
public fun MaterialScope.textDataCard(
onClick: Clickable,
title: (MaterialScope.() -> LayoutElement),
@@ -426,7 +437,11 @@
* for the most optimal experience across different screen sizes.
* @param height The height of this card. It's highly recommended to set this to [expand] for the
* most optimal experience across different screen sizes.
- * @param shape Defines the card's shape, in other words the corner radius for this card.
+ * @param shape Defines the card's shape, in other words the corner radius for this card. If
+ * changing these to radius smaller than [Shapes.medium], it is important to adjusts the margins
+ * of [primaryLayout] used to accommodate for more space, for example by using
+ * [maxPrimaryLayoutMargins]. Or, if the [shape] is set to [Shapes.full], using
+ * [minPrimaryLayoutMargins] can be considered.
* @param colors The colors to be used for a background and inner content of this card. If the
* background image is also specified, the image will be laid out on top of the background color.
* In case of the fully opaque background image, then the background color will not be shown.
@@ -454,7 +469,6 @@
* @sample androidx.wear.protolayout.material3.samples.dataCardSample
*/
// TODO: b/346958146 - link Card visuals in DAC
-// TODO: b/373578620 - Add how corners affects margins in the layout.
public fun MaterialScope.iconDataCard(
onClick: Clickable,
title: (MaterialScope.() -> LayoutElement),
@@ -532,7 +546,11 @@
* @param graphic A slot for displaying graphic data, such as progress indicator.
* @param height The width of this card. It's highly recommended to set this to [expand] for the
* most optimal experience across different screen sizes.
- * @param shape Defines the card's shape, in other words the corner radius for this card.
+ * @param shape Defines the card's shape, in other words the corner radius for this card. If
+ * changing these to radius smaller than [Shapes.medium], it is important to adjusts the margins
+ * of [primaryLayout] used to accommodate for more space, for example by using
+ * [maxPrimaryLayoutMargins]. Or, if the [shape] is set to [Shapes.full], using
+ * [minPrimaryLayoutMargins] can be considered.
* @param colors The colors to be used for a background and inner content of this card. Specified
* colors can be [CardDefaults.filledCardColors] for high emphasis card,
* [CardDefaults.filledVariantCardColors] for high/medium emphasis card,
@@ -552,7 +570,6 @@
* @sample androidx.wear.protolayout.material3.samples.graphicDataCardSample
*/
// TODO: b/346958146 - link Card visuals in DAC
-// TODO: b/373578620 - Add how corners affects margins in the layout.
public fun MaterialScope.graphicDataCard(
onClick: Clickable,
// TODO: b/368272767 - Potentially add helper for CPI and icon and link in KDocs.
@@ -627,7 +644,11 @@
* @param modifier Modifiers to set to this element. It's highly recommended to set a content
* description using [contentDescription]. If [LayoutModifier.background] modifier is used and the
* the background image is also specified, the image will be laid out on top of this color. In
- * case of the fully opaque background image, then the background color will not be shown.
+ * case of the fully opaque background image, then the background color will not be shown. If
+ * [LayoutModifier.clip] modifier is used to change the shape of the card to radius smaller than
+ * [Shapes.medium], it is important to adjusts the margins of [primaryLayout] used to accommodate
+ * for more space, for example by using [maxPrimaryLayoutMargins]. Or, if changing to
+ * [Shapes.full], using [minPrimaryLayoutMargins] can be considered.
* @param backgroundContent The background object to be used behind the content in the card. It is
* recommended to use the default styling that is automatically provided by only calling
* [backgroundImage] with the content. It can be combined with the specified
@@ -641,7 +662,6 @@
* @sample androidx.wear.protolayout.material3.samples.cardSample
*/
// TODO: b/346958146 - link Card visuals in DAC
-// TODO: b/373578620 - Add how corners affects margins in the layout.
public fun MaterialScope.card(
onClick: Clickable,
modifier: LayoutModifier = LayoutModifier,
diff --git a/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/Helpers.kt b/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/Helpers.kt
index 8f080b0..f29af19 100644
--- a/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/Helpers.kt
+++ b/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/Helpers.kt
@@ -26,7 +26,6 @@
import androidx.wear.protolayout.DimensionBuilders.ContainerDimension
import androidx.wear.protolayout.DimensionBuilders.DpProp
import androidx.wear.protolayout.DimensionBuilders.WrappedDimensionProp
-import androidx.wear.protolayout.DimensionBuilders.dp
import androidx.wear.protolayout.DimensionBuilders.expand
import androidx.wear.protolayout.DimensionBuilders.wrap
import androidx.wear.protolayout.LayoutElementBuilders
@@ -44,6 +43,8 @@
import androidx.wear.protolayout.ModifiersBuilders.ElementMetadata
import androidx.wear.protolayout.ModifiersBuilders.Padding
import androidx.wear.protolayout.ModifiersBuilders.SEMANTICS_ROLE_BUTTON
+import androidx.wear.protolayout.material3.PrimaryLayoutDefaults.percentageHeightToDp
+import androidx.wear.protolayout.material3.PrimaryLayoutDefaults.percentageWidthToDp
import androidx.wear.protolayout.materialcore.fontscaling.FontScaleConverterFactory
import androidx.wear.protolayout.modifiers.LayoutModifier
import androidx.wear.protolayout.modifiers.clickable
@@ -53,6 +54,7 @@
import androidx.wear.protolayout.modifiers.toProtoLayoutModifiers
import androidx.wear.protolayout.types.LayoutColor
import androidx.wear.protolayout.types.argb
+import androidx.wear.protolayout.types.dp
import java.nio.charset.StandardCharsets
/**
@@ -62,7 +64,7 @@
internal const val SCREEN_SIZE_BREAKPOINT_DP = 225
/** Minimum tap target for any clickable element. */
-internal val MINIMUM_TAP_TARGET_SIZE: DpProp = dp(48f)
+internal val MINIMUM_TAP_TARGET_SIZE: DpProp = 48f.dp
/** Returns byte array representation of tag from String. */
internal fun String.toTagBytes(): ByteArray = toByteArray(StandardCharsets.UTF_8)
@@ -89,7 +91,7 @@
@Dimension(unit = SP) private fun Float.dpToSpLinear(fontScale: Float): Float = this / fontScale
-internal fun Int.toDp() = dp(this.toFloat())
+internal fun Int.toDp() = this.toFloat().dp
/** Builds a horizontal Spacer, with width set to expand and height set to the given value. */
internal fun horizontalSpacer(@Dimension(unit = DP) heightDp: Int): Spacer =
@@ -206,3 +208,33 @@
)
.build()
}
+
+/**
+ * Returns [Padding] objects with values represented as percentages from the screen size.
+ *
+ * @param start The ratio percentage of the screen width that should be use as start padding
+ * @param end The ratio percentage of the screen width that should be use as end padding
+ * @param bottom The ratio percentage of the screen width that should be use as bottom padding
+ */
+internal fun MaterialScope.percentagePadding(
+ @FloatRange(from = 0.0, to = 1.0) start: Float,
+ @FloatRange(from = 0.0, to = 1.0) end: Float,
+ @FloatRange(from = 0.0, to = 1.0) bottom: Float
+): Padding =
+ padding(
+ start = percentageWidthToDp(start),
+ end = percentageWidthToDp(end),
+ bottom = percentageHeightToDp(bottom)
+ )
+
+/**
+ * Returns [Padding] objects with values represented as percentages from the screen size, using only
+ * horizontal padding.
+ *
+ * @param start The ratio percentage of the screen width that should be use as start padding
+ * @param end The ratio percentage of the screen width that should be use as end padding
+ */
+internal fun MaterialScope.percentagePadding(
+ @FloatRange(from = 0.0, to = 1.0) start: Float,
+ @FloatRange(from = 0.0, to = 1.0) end: Float
+): Padding = padding(start = percentageWidthToDp(start), end = percentageWidthToDp(end))
diff --git a/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/MaterialScope.kt b/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/MaterialScope.kt
index 6b9a14b..d547b3f 100644
--- a/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/MaterialScope.kt
+++ b/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/MaterialScope.kt
@@ -73,6 +73,7 @@
internal val defaultIconStyle: IconStyle,
internal val defaultBackgroundImageStyle: BackgroundImageStyle,
internal val defaultAvatarImageStyle: AvatarImageStyle,
+ internal val layoutSlotsPresence: LayoutSlotsPresence
) {
/** Color Scheme used within this scope and its components. */
public val colorScheme: ColorScheme = theme.colorScheme
@@ -84,7 +85,8 @@
defaultTextElementStyle: TextElementStyle = this.defaultTextElementStyle,
defaultIconStyle: IconStyle = this.defaultIconStyle,
defaultBackgroundImageStyle: BackgroundImageStyle = this.defaultBackgroundImageStyle,
- defaultAvatarImageStyle: AvatarImageStyle = this.defaultAvatarImageStyle
+ defaultAvatarImageStyle: AvatarImageStyle = this.defaultAvatarImageStyle,
+ layoutSlotsPresence: LayoutSlotsPresence = this.layoutSlotsPresence
): MaterialScope =
MaterialScope(
context = context,
@@ -94,7 +96,8 @@
defaultTextElementStyle = defaultTextElementStyle,
defaultIconStyle = defaultIconStyle,
defaultBackgroundImageStyle = defaultBackgroundImageStyle,
- defaultAvatarImageStyle = defaultAvatarImageStyle
+ defaultAvatarImageStyle = defaultAvatarImageStyle,
+ layoutSlotsPresence = layoutSlotsPresence
)
}
@@ -140,7 +143,8 @@
defaultTextElementStyle = TextElementStyle(),
defaultIconStyle = IconStyle(),
defaultBackgroundImageStyle = BackgroundImageStyle(),
- defaultAvatarImageStyle = AvatarImageStyle()
+ defaultAvatarImageStyle = AvatarImageStyle(),
+ layoutSlotsPresence = LayoutSlotsPresence()
)
.layout()
@@ -180,3 +184,9 @@
@ContentScaleMode
val contentScaleMode: Int = LayoutElementBuilders.CONTENT_SCALE_MODE_FILL_BOUNDS
)
+
+internal class LayoutSlotsPresence(
+ val isTitleSlotPresent: Boolean = false,
+ val isBottomSlotEdgeButton: Boolean = false,
+ val isBottomSlotPresent: Boolean = isBottomSlotEdgeButton
+)
diff --git a/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/PrimaryLayout.kt b/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/PrimaryLayout.kt
index 0bd6491..39019fa 100644
--- a/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/PrimaryLayout.kt
+++ b/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/PrimaryLayout.kt
@@ -18,11 +18,11 @@
import androidx.annotation.Dimension
import androidx.annotation.Dimension.Companion.DP
+import androidx.annotation.FloatRange
import androidx.annotation.RestrictTo
import androidx.annotation.VisibleForTesting
import androidx.wear.protolayout.DimensionBuilders
import androidx.wear.protolayout.DimensionBuilders.DpProp
-import androidx.wear.protolayout.DimensionBuilders.dp
import androidx.wear.protolayout.DimensionBuilders.expand
import androidx.wear.protolayout.DimensionBuilders.wrap
import androidx.wear.protolayout.LayoutElementBuilders.Box
@@ -33,25 +33,39 @@
import androidx.wear.protolayout.ModifiersBuilders.ElementMetadata
import androidx.wear.protolayout.ModifiersBuilders.Modifiers
import androidx.wear.protolayout.ModifiersBuilders.Padding
+import androidx.wear.protolayout.material3.PredefinedPrimaryLayoutMargins.defaultPrimaryLayoutMargins
+import androidx.wear.protolayout.material3.PredefinedPrimaryLayoutMargins.maxBottomMargin
+import androidx.wear.protolayout.material3.PredefinedPrimaryLayoutMargins.maxPrimaryLayoutMargins
+import androidx.wear.protolayout.material3.PredefinedPrimaryLayoutMargins.maxSideMargin
+import androidx.wear.protolayout.material3.PredefinedPrimaryLayoutMargins.midPrimaryLayoutMargins
+import androidx.wear.protolayout.material3.PredefinedPrimaryLayoutMargins.minPrimaryLayoutMargins
import androidx.wear.protolayout.material3.PrimaryLayoutDefaults.BOTTOM_EDGE_BUTTON_TOP_MARGIN_DP
-import androidx.wear.protolayout.material3.PrimaryLayoutDefaults.BOTTOM_SLOT_EMPTY_MARGIN_BOTTOM_PERCENTAGE
+import androidx.wear.protolayout.material3.PrimaryLayoutDefaults.BOTTOM_SLOT_OTHER_MARGIN_BOTTOM_PERCENTAGE
import androidx.wear.protolayout.material3.PrimaryLayoutDefaults.BOTTOM_SLOT_OTHER_MARGIN_SIDE_PERCENTAGE
-import androidx.wear.protolayout.material3.PrimaryLayoutDefaults.BOTTOM_SLOT_OTHER_NO_LABEL_MARGIN_BOTTOM_PERCENTAGE
-import androidx.wear.protolayout.material3.PrimaryLayoutDefaults.BOTTOM_SLOT_OTHER_NO_LABEL_MARGIN_TOP_PERCENTAGE
-import androidx.wear.protolayout.material3.PrimaryLayoutDefaults.BOTTOM_SLOT_OTHER_WITH_LABEL_MARGIN_BOTTOM_PERCENTAGE
-import androidx.wear.protolayout.material3.PrimaryLayoutDefaults.BOTTOM_SLOT_OTHER_WITH_LABEL_MARGIN_TOP_PERCENTAGE
+import androidx.wear.protolayout.material3.PrimaryLayoutDefaults.BOTTOM_SLOT_OTHER_MARGIN_TOP_DP
import androidx.wear.protolayout.material3.PrimaryLayoutDefaults.FOOTER_LABEL_SLOT_MARGIN_SIDE_PERCENTAGE
import androidx.wear.protolayout.material3.PrimaryLayoutDefaults.FOOTER_LABEL_TO_BOTTOM_SLOT_SPACER_HEIGHT_DP
import androidx.wear.protolayout.material3.PrimaryLayoutDefaults.HEADER_ICON_SIZE_DP
-import androidx.wear.protolayout.material3.PrimaryLayoutDefaults.HEADER_ICON_TITLE_SPACER_HEIGHT_DP
+import androidx.wear.protolayout.material3.PrimaryLayoutDefaults.HEADER_ICON_TITLE_SPACER_HEIGHT_LARGE_DP
+import androidx.wear.protolayout.material3.PrimaryLayoutDefaults.HEADER_ICON_TITLE_SPACER_HEIGHT_SMALL_DP
import androidx.wear.protolayout.material3.PrimaryLayoutDefaults.HEADER_MARGIN_BOTTOM_DP
import androidx.wear.protolayout.material3.PrimaryLayoutDefaults.HEADER_MARGIN_SIDE_PERCENTAGE
import androidx.wear.protolayout.material3.PrimaryLayoutDefaults.HEADER_MARGIN_TOP_DP
-import androidx.wear.protolayout.material3.PrimaryLayoutDefaults.MAIN_SLOT_WITHOUT_BOTTOM_SLOT_WITHOUT_TITLE_MARGIN_SIDE_PERCENTAGE
-import androidx.wear.protolayout.material3.PrimaryLayoutDefaults.MAIN_SLOT_WITHOUT_BOTTOM_SLOT_WITH_TITLE_MARGIN_SIDE_PERCENTAGE
-import androidx.wear.protolayout.material3.PrimaryLayoutDefaults.MAIN_SLOT_WITH_BOTTOM_SLOT_WITHOUT_TITLE_MARGIN_SIDE_PERCENTAGE
-import androidx.wear.protolayout.material3.PrimaryLayoutDefaults.MAIN_SLOT_WITH_BOTTOM_SLOT_WITH_TITLE_MARGIN_SIDE_PERCENTAGE
import androidx.wear.protolayout.material3.PrimaryLayoutDefaults.METADATA_TAG
+import androidx.wear.protolayout.material3.PrimaryLayoutDefaults.percentageHeightToDp
+import androidx.wear.protolayout.material3.PrimaryLayoutMargins.Companion.DEFAULT_PRIMARY_LAYOUT_MARGIN
+import androidx.wear.protolayout.material3.PrimaryLayoutMargins.Companion.MAX_PRIMARY_LAYOUT_MARGIN
+import androidx.wear.protolayout.material3.PrimaryLayoutMargins.Companion.MID_PRIMARY_LAYOUT_MARGIN
+import androidx.wear.protolayout.material3.PrimaryLayoutMargins.Companion.MIN_PRIMARY_LAYOUT_MARGIN
+import androidx.wear.protolayout.material3.PrimaryLayoutMargins.Companion.customizedPrimaryLayoutMargin
+import androidx.wear.protolayout.material3.PrimaryLayoutMarginsImpl.Companion.DEFAULT
+import androidx.wear.protolayout.material3.PrimaryLayoutMarginsImpl.Companion.MAX
+import androidx.wear.protolayout.material3.PrimaryLayoutMarginsImpl.Companion.MID
+import androidx.wear.protolayout.material3.PrimaryLayoutMarginsImpl.Companion.MIN
+import androidx.wear.protolayout.modifiers.LayoutModifier
+import androidx.wear.protolayout.modifiers.padding
+import androidx.wear.protolayout.modifiers.toProtoLayoutModifiers
+import androidx.wear.protolayout.types.dp
/**
* ProtoLayout Material3 full screen layout that represents a suggested Material3 layout style that
@@ -99,19 +113,31 @@
* it an edge button, the given label will be ignored.
* @param onClick The clickable action for whole layout. If any area (outside of other added
* tappable components) is clicked, it will fire the associated action.
+ * @param margins The customized outer margin that will be applied as following:
+ * * `start` and `end` would be applied as a side margins on [mainSlot]
+ * * `bottom` would be applied as a bottom margin when [bottomSlot] is not present.
+ *
+ * It is highly recommended to use provided constants for these
+ * margins - [DEFAULT_PRIMARY_LAYOUT_MARGIN], [MIN_PRIMARY_LAYOUT_MARGIN],
+ * [MID_PRIMARY_LAYOUT_MARGIN] or [MAX_PRIMARY_LAYOUT_MARGIN], depending on inner content and its
+ * corners shape. If providing custom numbers by [customizedPrimaryLayoutMargin], it is a
+ * requirement for those to be percentages of the screen width and height.
+ *
* @sample androidx.wear.protolayout.material3.samples.topLevelLayout
+ * @sample androidx.wear.protolayout.material3.samples.cardSample
+ * @sample androidx.wear.protolayout.material3.samples.oneSlotButtonsSample
+ * @sample androidx.wear.protolayout.material3.samples.graphicDataCardSample
*/
-// TODO: b/356568440 - Add sample above and put it in a proper samples file and link with @sample
// TODO: b/346958146 - Link visuals once they are available.
// TODO: b/353247528 - Handle the icon.
-// TODO: b/369162409 -Allow side and bottom margins in PrimaryLayout to be customizable.
// TODO: b/370976767 - Specify that this should be used with MaterialTileService.
public fun MaterialScope.primaryLayout(
mainSlot: (MaterialScope.() -> LayoutElement),
titleSlot: (MaterialScope.() -> LayoutElement)? = null,
bottomSlot: (MaterialScope.() -> LayoutElement)? = null,
labelForBottomSlot: (MaterialScope.() -> LayoutElement)? = null,
- onClick: Clickable? = null
+ onClick: Clickable? = null,
+ margins: PrimaryLayoutMargins = DEFAULT_PRIMARY_LAYOUT_MARGIN
): LayoutElement =
primaryLayoutWithOverrideIcon(
overrideIcon = false,
@@ -119,7 +145,8 @@
mainSlot = mainSlot,
bottomSlot = bottomSlot,
labelForBottomSlot = labelForBottomSlot,
- onClick = onClick
+ onClick = onClick,
+ margins = margins
)
/**
@@ -135,6 +162,7 @@
bottomSlot: (MaterialScope.() -> LayoutElement)? = null,
labelForBottomSlot: (MaterialScope.() -> LayoutElement)? = null,
onClick: Clickable? = null,
+ margins: PrimaryLayoutMargins = DEFAULT_PRIMARY_LAYOUT_MARGIN
): LayoutElement {
val screenWidth = deviceConfiguration.screenWidthDp
val screenHeight = deviceConfiguration.screenHeightDp
@@ -156,19 +184,6 @@
onClick?.apply { modifiers.setClickable(this) }
- val mainSlotSideMargin: DpProp =
- dp(
- screenWidth *
- if (bottomSlot != null)
- (if (titleSlot != null)
- MAIN_SLOT_WITH_BOTTOM_SLOT_WITH_TITLE_MARGIN_SIDE_PERCENTAGE
- else MAIN_SLOT_WITH_BOTTOM_SLOT_WITHOUT_TITLE_MARGIN_SIDE_PERCENTAGE)
- else
- (if (titleSlot != null)
- MAIN_SLOT_WITHOUT_BOTTOM_SLOT_WITH_TITLE_MARGIN_SIDE_PERCENTAGE
- else MAIN_SLOT_WITHOUT_BOTTOM_SLOT_WITHOUT_TITLE_MARGIN_SIDE_PERCENTAGE)
- )
-
val mainLayout =
Column.Builder()
.setModifiers(modifiers.build())
@@ -191,19 +206,60 @@
)
)
+ val bottomSlotValue = bottomSlot?.let { bottomSlot() }
+
+ val marginsValues: Padding =
+ withStyle(
+ layoutSlotsPresence =
+ LayoutSlotsPresence(
+ isTitleSlotPresent = titleSlot != null,
+ isBottomSlotPresent = bottomSlot != null,
+ isBottomSlotEdgeButton = bottomSlotValue?.isSlotEdgeButton() == true
+ )
+ )
+ .let { scope ->
+ if (margins is PrimaryLayoutMarginsImpl) {
+ when (margins.size) {
+ MIN -> scope.minPrimaryLayoutMargins()
+ MID -> scope.midPrimaryLayoutMargins()
+ MAX -> scope.maxPrimaryLayoutMargins()
+ DEFAULT -> scope.defaultPrimaryLayoutMargins()
+ else -> scope.defaultPrimaryLayoutMargins()
+ }
+ } else if (margins is CustomPrimaryLayoutMargins) {
+ margins.toPadding(scope)
+ } else {
+ // Fallback to default
+ scope.defaultPrimaryLayoutMargins()
+ }
+ }
+
// Contains main content. This Box is needed to set to expand, even if empty so it
// fills the empty space until bottom content.
- mainSlot?.let { mainLayout.addContent(mainSlot().getMainContentBox(mainSlotSideMargin)) }
+ mainSlot?.let {
+ mainLayout.addContent(
+ mainSlot()
+ .getMainContentBox(
+ sideMargins = marginsValues,
+ maxSideMarginFallbackDp = maxSideMargin()
+ )
+ )
+ }
// Contains bottom slot, optional label or needed padding if empty.
- mainLayout.addContent(getFooterContent(bottomSlot?.let { bottomSlot() }, labelSlot))
+ mainLayout.addContent(
+ getFooterContent(
+ bottomSlot = bottomSlotValue,
+ labelSlot = labelSlot,
+ bottomMarginForNoContentDp = marginsValues.bottom?.value ?: maxBottomMargin()
+ )
+ )
return mainLayout.build()
}
private fun MaterialScope.getIconPlaceholder(overrideIcon: Boolean): LayoutElement {
- val iconSlot =
- Box.Builder().setWidth(HEADER_ICON_SIZE_DP.toDp()).setHeight(HEADER_ICON_SIZE_DP.toDp())
+ val iconSlot = Box.Builder().setWidth(HEADER_ICON_SIZE_DP.dp).setHeight(HEADER_ICON_SIZE_DP.dp)
if (overrideIcon) {
iconSlot.setModifiers(
Modifiers.Builder()
@@ -233,7 +289,13 @@
titleSlot?.apply {
headerBuilder
- .addContent(horizontalSpacer(HEADER_ICON_TITLE_SPACER_HEIGHT_DP))
+ .addContent(
+ horizontalSpacer(
+ if (deviceConfiguration.screenHeightDp.isBreakpoint())
+ HEADER_ICON_TITLE_SPACER_HEIGHT_LARGE_DP
+ else HEADER_ICON_TITLE_SPACER_HEIGHT_SMALL_DP
+ )
+ )
.addContent(titleSlot)
}
@@ -241,22 +303,25 @@
}
/** Returns central slot with the optional main content. It expands to fill the available space. */
-private fun LayoutElement.getMainContentBox(sideMargin: DpProp): Box =
- Box.Builder()
+private fun LayoutElement.getMainContentBox(
+ sideMargins: Padding,
+ maxSideMarginFallbackDp: Float,
+): Box {
+ // Start and end Padding shouldn't be null if these are predefined margins, but if developers
+ // sets some other object, we will fallback to the max margin.
+ val sideMarginStart = sideMargins.start?.value ?: maxSideMarginFallbackDp
+ val sideMarginEnd = sideMargins.end?.value ?: maxSideMarginFallbackDp
+ return Box.Builder()
.setWidth(expand())
.setHeight(expand())
.setModifiers(
- Modifiers.Builder()
- .setPadding(
- Padding.Builder() // Top and bottom space has been added to other elements.
- .setStart(sideMargin)
- .setEnd(sideMargin)
- .build()
- )
- .build()
+ // Top and bottom space has been added to other elements.
+ LayoutModifier.padding(start = sideMarginStart, end = sideMarginEnd)
+ .toProtoLayoutModifiers()
)
.addContent(this)
.build()
+}
/**
* Returns the footer content, containing bottom slot and optional label with the corresponding
@@ -265,22 +330,19 @@
*/
private fun MaterialScope.getFooterContent(
bottomSlot: LayoutElement?,
- labelSlot: LayoutElement?
+ labelSlot: LayoutElement?,
+ bottomMarginForNoContentDp: Float
): LayoutElement {
val footer = Box.Builder().setWidth(wrap()).setHeight(wrap())
if (bottomSlot == null) {
footer.setWidth(expand())
- footer.setHeight(
- dp(BOTTOM_SLOT_EMPTY_MARGIN_BOTTOM_PERCENTAGE * deviceConfiguration.screenHeightDp)
- )
+ footer.setHeight(bottomMarginForNoContentDp.dp)
} else if (bottomSlot.isSlotEdgeButton()) {
// Label shouldn't be used with EdgeButton.
footer.setModifiers(
Modifiers.Builder()
- .setPadding(
- Padding.Builder().setTop(BOTTOM_EDGE_BUTTON_TOP_MARGIN_DP.toDp()).build()
- )
+ .setPadding(Padding.Builder().setTop(BOTTOM_EDGE_BUTTON_TOP_MARGIN_DP.dp).build())
.build()
)
@@ -292,21 +354,10 @@
Modifiers.Builder()
.setPadding(
Padding.Builder()
- .setTop(
- dp(
- (if (labelSlot == null)
- BOTTOM_SLOT_OTHER_NO_LABEL_MARGIN_TOP_PERCENTAGE
- else BOTTOM_SLOT_OTHER_WITH_LABEL_MARGIN_TOP_PERCENTAGE) *
- deviceConfiguration.screenHeightDp
- )
- )
+ .setTop(BOTTOM_SLOT_OTHER_MARGIN_TOP_DP.dp)
.setBottom(
- dp(
- (if (labelSlot == null)
- BOTTOM_SLOT_OTHER_NO_LABEL_MARGIN_BOTTOM_PERCENTAGE
- else BOTTOM_SLOT_OTHER_WITH_LABEL_MARGIN_BOTTOM_PERCENTAGE) *
- deviceConfiguration.screenHeightDp
- )
+ percentageHeightToDp(BOTTOM_SLOT_OTHER_MARGIN_BOTTOM_PERCENTAGE / 100)
+ .dp
)
.build()
)
@@ -317,10 +368,9 @@
otherBottomSlot
.addContent(
generateLabelContent(
- dp(
- FOOTER_LABEL_SLOT_MARGIN_SIDE_PERCENTAGE *
- deviceConfiguration.screenWidthDp
- )
+ (FOOTER_LABEL_SLOT_MARGIN_SIDE_PERCENTAGE *
+ deviceConfiguration.screenWidthDp)
+ .dp
)
)
.addContent(horizontalSpacer(FOOTER_LABEL_TO_BOTTOM_SLOT_SPACER_HEIGHT_DP))
@@ -330,10 +380,9 @@
otherBottomSlot
.addContent(
bottomSlot.generateBottomSlotContent(
- dp(
- BOTTOM_SLOT_OTHER_MARGIN_SIDE_PERCENTAGE *
- deviceConfiguration.screenWidthDp
- )
+ (BOTTOM_SLOT_OTHER_MARGIN_SIDE_PERCENTAGE *
+ deviceConfiguration.screenWidthDp)
+ .dp
)
)
.build()
@@ -363,47 +412,47 @@
.addContent(this)
.build()
-private fun MaterialScope.getMarginForHeader(): Padding {
- return Padding.Builder()
- .setTop(HEADER_MARGIN_TOP_DP.toDp())
- .setBottom(HEADER_MARGIN_BOTTOM_DP.toDp())
- .setStart(dp(HEADER_MARGIN_SIDE_PERCENTAGE * deviceConfiguration.screenWidthDp))
- .setEnd(dp(HEADER_MARGIN_SIDE_PERCENTAGE * deviceConfiguration.screenWidthDp))
- .build()
-}
+private fun MaterialScope.getMarginForHeader() =
+ padding(
+ top = HEADER_MARGIN_TOP_DP,
+ bottom = HEADER_MARGIN_BOTTOM_DP,
+ start = HEADER_MARGIN_SIDE_PERCENTAGE * deviceConfiguration.screenWidthDp,
+ end = HEADER_MARGIN_SIDE_PERCENTAGE * deviceConfiguration.screenWidthDp
+ )
/** Contains the default values used by Material layout. */
internal object PrimaryLayoutDefaults {
+ internal fun MaterialScope.percentageWidthToDp(
+ @FloatRange(from = 0.0, to = 1.0) percentage: Float
+ ): Float = percentage * deviceConfiguration.screenWidthDp
+
+ internal fun MaterialScope.percentageHeightToDp(
+ @FloatRange(from = 0.0, to = 1.0) percentage: Float
+ ): Float = percentage * deviceConfiguration.screenHeightDp
+
/** Tool tag for Metadata in Modifiers, so we know that Row is actually a PrimaryLayout. */
- @VisibleForTesting const val METADATA_TAG: String = "M3_PL"
+ @VisibleForTesting internal const val METADATA_TAG: String = "M3_PL"
- @Dimension(unit = DP) const val HEADER_MARGIN_TOP_DP: Int = 3
+ @Dimension(DP) internal const val HEADER_MARGIN_TOP_DP = 3f
- @Dimension(unit = DP) const val HEADER_MARGIN_BOTTOM_DP: Int = 6
+ @Dimension(DP) internal const val HEADER_MARGIN_BOTTOM_DP = 6f
- const val HEADER_MARGIN_SIDE_PERCENTAGE: Float = 14.5f / 100
+ internal const val HEADER_MARGIN_SIDE_PERCENTAGE = 14.5f / 100
- @Dimension(unit = DP) const val HEADER_ICON_SIZE_DP: Int = 24
+ @Dimension(DP) internal const val HEADER_ICON_SIZE_DP = 24f
- @Dimension(unit = DP) const val HEADER_ICON_TITLE_SPACER_HEIGHT_DP: Int = 2
+ @Dimension(DP) internal const val HEADER_ICON_TITLE_SPACER_HEIGHT_SMALL_DP = 2
+ @Dimension(DP) internal const val HEADER_ICON_TITLE_SPACER_HEIGHT_LARGE_DP = 4
// The remaining margins around EdgeButton are within the component itself.
- @Dimension(unit = DP) const val BOTTOM_EDGE_BUTTON_TOP_MARGIN_DP: Int = 4
+ @Dimension(DP) internal const val BOTTOM_EDGE_BUTTON_TOP_MARGIN_DP = 4f
- const val BOTTOM_SLOT_OTHER_NO_LABEL_MARGIN_TOP_PERCENTAGE: Float = 4f / 100
- const val BOTTOM_SLOT_OTHER_NO_LABEL_MARGIN_BOTTOM_PERCENTAGE: Float = 8.3f / 100
+ @Dimension(DP) internal const val BOTTOM_SLOT_OTHER_MARGIN_TOP_DP = 6f
+ internal const val BOTTOM_SLOT_OTHER_MARGIN_BOTTOM_PERCENTAGE = 5.2f
- const val BOTTOM_SLOT_OTHER_WITH_LABEL_MARGIN_TOP_PERCENTAGE: Float = 3f / 100
- const val BOTTOM_SLOT_OTHER_WITH_LABEL_MARGIN_BOTTOM_PERCENTAGE: Float = 5f / 100
- const val BOTTOM_SLOT_OTHER_MARGIN_SIDE_PERCENTAGE: Float = 26f / 100
+ internal const val BOTTOM_SLOT_OTHER_MARGIN_SIDE_PERCENTAGE = 26f / 100
- @Dimension(unit = DP) const val FOOTER_LABEL_TO_BOTTOM_SLOT_SPACER_HEIGHT_DP: Int = 2
+ @Dimension(DP) internal const val FOOTER_LABEL_TO_BOTTOM_SLOT_SPACER_HEIGHT_DP = 2
- const val FOOTER_LABEL_SLOT_MARGIN_SIDE_PERCENTAGE: Float = 16.64f / 100
-
- const val BOTTOM_SLOT_EMPTY_MARGIN_BOTTOM_PERCENTAGE: Float = 14f / 100
- const val MAIN_SLOT_WITH_BOTTOM_SLOT_WITH_TITLE_MARGIN_SIDE_PERCENTAGE: Float = 3f / 100
- const val MAIN_SLOT_WITH_BOTTOM_SLOT_WITHOUT_TITLE_MARGIN_SIDE_PERCENTAGE: Float = 6f / 100
- const val MAIN_SLOT_WITHOUT_BOTTOM_SLOT_WITH_TITLE_MARGIN_SIDE_PERCENTAGE: Float = 7.3f / 100
- const val MAIN_SLOT_WITHOUT_BOTTOM_SLOT_WITHOUT_TITLE_MARGIN_SIDE_PERCENTAGE: Float = 8.3f / 100
+ internal const val FOOTER_LABEL_SLOT_MARGIN_SIDE_PERCENTAGE = 16.64f / 100
}
diff --git a/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/PrimaryLayoutMargins.kt b/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/PrimaryLayoutMargins.kt
new file mode 100644
index 0000000..d38e0c5
--- /dev/null
+++ b/wear/protolayout/protolayout-material3/src/main/java/androidx/wear/protolayout/material3/PrimaryLayoutMargins.kt
@@ -0,0 +1,505 @@
+/*
+ * 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.wear.protolayout.material3
+
+import androidx.annotation.FloatRange
+import androidx.annotation.IntDef
+import androidx.annotation.RestrictTo
+import androidx.wear.protolayout.ModifiersBuilders.Padding
+import androidx.wear.protolayout.material3.PrimaryLayoutDefaults.percentageHeightToDp
+import androidx.wear.protolayout.material3.PrimaryLayoutDefaults.percentageWidthToDp
+import androidx.wear.protolayout.material3.PrimaryLayoutMargins.Companion.DEFAULT_PRIMARY_LAYOUT_MARGIN
+import androidx.wear.protolayout.material3.PrimaryLayoutMargins.Companion.MAX_PRIMARY_LAYOUT_MARGIN
+import androidx.wear.protolayout.material3.PrimaryLayoutMargins.Companion.MID_PRIMARY_LAYOUT_MARGIN
+import androidx.wear.protolayout.material3.PrimaryLayoutMargins.Companion.MIN_PRIMARY_LAYOUT_MARGIN
+import androidx.wear.protolayout.material3.PrimaryLayoutMarginsImpl.Companion.DEFAULT
+import androidx.wear.protolayout.material3.PrimaryLayoutMarginsImpl.Companion.MAX
+import androidx.wear.protolayout.material3.PrimaryLayoutMarginsImpl.Companion.MID
+import androidx.wear.protolayout.material3.PrimaryLayoutMarginsImpl.Companion.MIN
+import kotlin.Float.Companion.NaN
+
+/**
+ * The set of margins for the [primaryLayout]'s customization.
+ *
+ * It is highly recommended to use these predefined values that are optimized for different screen
+ * sizes, content's corners and slots presences. Those are:
+ * * [MIN_PRIMARY_LAYOUT_MARGIN]
+ * * [MID_PRIMARY_LAYOUT_MARGIN]
+ * * [DEFAULT_PRIMARY_LAYOUT_MARGIN]
+ * * [MAX_PRIMARY_LAYOUT_MARGIN].
+ */
+public abstract class PrimaryLayoutMargins internal constructor() {
+ public companion object {
+ /**
+ * Default side margins for the main slot of [primaryLayout] that works for the majority of
+ * [Shapes] inside the main content components, usually across round toward medium round
+ * corners.
+ *
+ * The actual returned values depend on presence of slots in [primaryLayout] which will be
+ * applied automatically.
+ */
+ @JvmField
+ public val DEFAULT_PRIMARY_LAYOUT_MARGIN: PrimaryLayoutMargins =
+ PrimaryLayoutMarginsImpl(DEFAULT)
+
+ /**
+ * Min side margins for the main slot of [primaryLayout] that should be used only when the
+ * main slot contains content components with fully rounded corners, such as [Shapes.full]
+ * to avoid clipping.
+ *
+ * The actual returned values depend on presence of slots in [primaryLayout] which will be
+ * applied automatically.
+ */
+ @JvmField
+ public val MIN_PRIMARY_LAYOUT_MARGIN: PrimaryLayoutMargins = PrimaryLayoutMarginsImpl(MIN)
+
+ /**
+ * Mid side margins for the main slot of [primaryLayout] that should be used when the main
+ * slot contains content components with fully rounded to medium round corners, for example,
+ * larger than [Shapes.medium] to avoid clipping.
+ *
+ * The actual returned values depend on presence of slots in [primaryLayout] which will be
+ * applied automatically.
+ */
+ @JvmField
+ public val MID_PRIMARY_LAYOUT_MARGIN: PrimaryLayoutMargins = PrimaryLayoutMarginsImpl(MID)
+
+ /**
+ * Max side margins for the main slot of [primaryLayout] that should be used when the main
+ * slot contains content components with square corners, for example, smaller than
+ * [Shapes.medium] to avoid clipping.
+ *
+ * The actual returned values depend on presence of slots in [primaryLayout] which will be
+ * applied automatically.
+ */
+ @JvmField
+ public val MAX_PRIMARY_LAYOUT_MARGIN: PrimaryLayoutMargins = PrimaryLayoutMarginsImpl(MAX)
+
+ /**
+ * Creates new set of margins to be used for [primaryLayout] customization. The passed in
+ * values represent percentages based on the screen width.
+ *
+ * It is highly recommended to use predefined values instead of creating this custom one,
+ * because they are optimized for different screen sizes, content's corners and slots
+ * presences. Those predefined ones are:
+ * * [MIN_PRIMARY_LAYOUT_MARGIN]
+ * * [MID_PRIMARY_LAYOUT_MARGIN]
+ * * [DEFAULT_PRIMARY_LAYOUT_MARGIN]
+ * * [MAX_PRIMARY_LAYOUT_MARGIN].
+ *
+ * @param start Percentage of the screen width that should be applied as margin on the start
+ * side
+ * @param end Percentage of the screen width that should be applied as margin on the end
+ * side
+ */
+ public fun customizedPrimaryLayoutMargin(
+ @FloatRange(from = 0.0, to = 1.0) start: Float,
+ @FloatRange(from = 0.0, to = 1.0) end: Float
+ ): PrimaryLayoutMargins = CustomPrimaryLayoutMargins(start = start, end = end)
+
+ /**
+ * Creates new set of margins to be used for [primaryLayout] customization. The passed in
+ * values represent percentages based on the screen width and screen height.
+ *
+ * It is highly recommended to use predefined values instead of creating this custom one,
+ * because they are optimized for different screen sizes, content's corners and slots
+ * presences. Those predefined ones are:
+ * * [MIN_PRIMARY_LAYOUT_MARGIN]
+ * * [MID_PRIMARY_LAYOUT_MARGIN]
+ * * [DEFAULT_PRIMARY_LAYOUT_MARGIN]
+ * * [MAX_PRIMARY_LAYOUT_MARGIN].
+ *
+ * @param start Percentage of the screen width that should be applied as margin on the start
+ * side
+ * @param end Percentage of the screen width that should be applied as margin on the end
+ * side
+ * @param bottom Percentage of the screen height that should be applied as margin on the
+ * bottom
+ */
+ public fun customizedPrimaryLayoutMargin(
+ @FloatRange(from = 0.0, to = 1.0) start: Float,
+ @FloatRange(from = 0.0, to = 1.0) end: Float,
+ @FloatRange(from = 0.0, to = 1.0) bottom: Float
+ ): PrimaryLayoutMargins =
+ CustomPrimaryLayoutMargins(start = start, end = end, bottom = bottom)
+ }
+}
+
+/**
+ * The predefined set of margin style sizes to be used by [primaryLayout] tod define default values.
+ */
+internal class PrimaryLayoutMarginsImpl internal constructor(internal val size: Int) :
+ PrimaryLayoutMargins() {
+ companion object {
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ @Retention(AnnotationRetention.SOURCE)
+ @IntDef(DEFAULT, MIN, MID, MAX)
+ annotation class PrimaryLayoutStyleSizes
+
+ internal const val DEFAULT = 0
+ internal const val MIN = 1
+ internal const val MID = 2
+ internal const val MAX = 3
+ }
+}
+
+/**
+ * The custom set of margins for the [primaryLayout]'s customization.
+ *
+ * It is highly recommended to use predefined values instead of creating this custom once, because
+ * they are optimized for different screen sizes, content's corners and slots presences. Those
+ * predefined once are:
+ * * [MIN_PRIMARY_LAYOUT_MARGIN]
+ * * [MID_PRIMARY_LAYOUT_MARGIN]
+ * * [DEFAULT_PRIMARY_LAYOUT_MARGIN]
+ * * [MAX_PRIMARY_LAYOUT_MARGIN].
+ */
+internal class CustomPrimaryLayoutMargins
+/**
+ * Creates new set of margins to be used for [primaryLayout] customization. The passed in values
+ * represent percentages based on the screen width.
+ *
+ * @param start Percentage of the screen width that should be applied as margin on the start side
+ * @param end Percentage of the screen width that should be applied as margin on the end side
+ */
+(
+ @FloatRange(from = 0.0, to = 1.0) internal val start: Float,
+ @FloatRange(from = 0.0, to = 1.0) internal val end: Float
+) : PrimaryLayoutMargins() {
+ internal var bottom: Float = NaN
+
+ /**
+ * Creates new set of margins to be used for [primaryLayout] customization. The passed in values
+ * represent percentages based on the screen width and screen height.
+ *
+ * @param start Percentage of the screen width that should be applied as margin on the start
+ * side
+ * @param end Percentage of the screen width that should be applied as margin on the end side
+ * @param bottom Percentage of the screen height that should be applied as margin on the bottom
+ */
+ constructor(
+ @FloatRange(from = 0.0, to = 1.0) start: Float,
+ @FloatRange(from = 0.0, to = 1.0) end: Float,
+ @FloatRange(from = 0.0, to = 1.0) bottom: Float
+ ) : this(start = start, end = end) {
+ this.bottom = bottom
+ }
+
+ /** Returns the given margins as [Padding] object. */
+ internal fun toPadding(scope: MaterialScope): Padding =
+ if (bottom.isNaN()) {
+ scope.percentagePadding(start = start, end = end)
+ } else {
+ scope.percentagePadding(start = start, end = end, bottom = bottom)
+ }
+}
+
+/**
+ * Default values for margins used in [primaryLayout] based on slots presence from [materialScope].
+ */
+internal object PredefinedPrimaryLayoutMargins {
+ /**
+ * Default side margins for the main slot of [primaryLayout] that works for the majority of
+ * [Shapes] inside the main content components, usually across round toward medium round
+ * corners.
+ *
+ * The actual returned values depend on presence of slots in [primaryLayout] which will be
+ * applied automatically.
+ */
+ fun MaterialScope.defaultPrimaryLayoutMargins(): Padding =
+ if (layoutSlotsPresence.isTitleSlotPresent) {
+ if (layoutSlotsPresence.isBottomSlotPresent) {
+ if (layoutSlotsPresence.isBottomSlotEdgeButton)
+ defaultPrimaryLayoutMarginsWithTitleWithEdgeButton()
+ else defaultPrimaryLayoutMarginsWithBottomSlotAsOther()
+ } else {
+ defaultPrimaryLayoutMarginsWithTitleWithoutBottomSlot()
+ }
+ } else {
+ if (layoutSlotsPresence.isBottomSlotPresent) {
+ if (layoutSlotsPresence.isBottomSlotEdgeButton)
+ defaultPrimaryLayoutMarginsWithoutTitleWithEdgeButton()
+ else defaultPrimaryLayoutMarginsWithBottomSlotAsOther()
+ } else {
+ defaultPrimaryLayoutMarginsWithoutTitleWithoutBottomSlot()
+ }
+ }
+
+ /**
+ * Min side margins for the main slot of [primaryLayout] that should be used only when the main
+ * slot contains content components with fully rounded corners, such as [Shapes.full] to avoid
+ * clipping.
+ *
+ * The actual returned values depend on presence of slots in [primaryLayout] which will be
+ * applied automatically.
+ */
+ internal fun MaterialScope.minPrimaryLayoutMargins(): Padding =
+ // Values are the same regardless of title slot presence.
+ if (layoutSlotsPresence.isBottomSlotPresent) {
+ if (layoutSlotsPresence.isBottomSlotEdgeButton) minPrimaryLayoutMarginsWithEdgeButton()
+ else minPrimaryLayoutMarginsWithBottomSlotAsOther()
+ } else {
+ minPrimaryLayoutMarginsWithoutBottomSlot()
+ }
+
+ /**
+ * Mid side margins for the main slot of [primaryLayout] that should be used when the main slot
+ * contains content components with fully rounded to medium round corners, for example, larger
+ * than [Shapes.medium] to avoid clipping.
+ *
+ * The actual returned values depend on presence of slots in [primaryLayout] which will be
+ * applied automatically.
+ */
+ internal fun MaterialScope.midPrimaryLayoutMargins(): Padding =
+ if (layoutSlotsPresence.isTitleSlotPresent) {
+ if (layoutSlotsPresence.isBottomSlotPresent) {
+ if (layoutSlotsPresence.isBottomSlotEdgeButton)
+ midPrimaryLayoutMarginsWithTitleWithEdgeButton()
+ else midPrimaryLayoutMarginsWithTitleWithBottomSlotAsOther()
+ } else {
+ midPrimaryLayoutMarginsWithTitleWithoutBottomSlot()
+ }
+ } else {
+ if (layoutSlotsPresence.isBottomSlotPresent) {
+ if (layoutSlotsPresence.isBottomSlotEdgeButton)
+ midPrimaryLayoutMarginsWithoutTitleWithEdgeButton()
+ else midPrimaryLayoutMarginsWithoutTitleWithBottomSlotAsOther()
+ } else {
+ midPrimaryLayoutMarginsWithoutTitleWithoutBottomSlot()
+ }
+ }
+
+ /**
+ * Max side margins for the main slot of [primaryLayout] that should be used when the main slot
+ * contains content components with square corners, for example, smaller than [Shapes.medium] to
+ * avoid clipping.
+ *
+ * The actual returned values depend on presence of slots in [primaryLayout] which will be
+ * applied automatically.
+ */
+ internal fun MaterialScope.maxPrimaryLayoutMargins(): Padding =
+ if (layoutSlotsPresence.isTitleSlotPresent) {
+ if (layoutSlotsPresence.isBottomSlotPresent) {
+ if (layoutSlotsPresence.isBottomSlotEdgeButton)
+ maxPrimaryLayoutMarginsWithTitleWithEdgeButton()
+ else maxPrimaryLayoutMarginsWithTitleWithBottomSlotAsOther()
+ } else {
+ maxPrimaryLayoutMarginsWithTitleWithoutBottomSlot()
+ }
+ } else {
+ if (layoutSlotsPresence.isBottomSlotPresent) {
+ if (layoutSlotsPresence.isBottomSlotEdgeButton)
+ maxPrimaryLayoutMarginsWithoutTitleWithEdgeButton()
+ else maxPrimaryLayoutMarginsWithoutTitleWithBottomSlotAsOther()
+ } else {
+ maxPrimaryLayoutMarginsWithoutTitleWithoutBottomSlot()
+ }
+ }
+
+ // Separate all cases internally so it's easier to track from the spec.
+
+ // Bottom slot as EdgeButton. Bottom margin are not allowed to be customized in these cases.
+
+ /**
+ * Default side margins for the main slot of [primaryLayout] that should be used when there is a
+ * title slot in the layout and bottom slot is set to be [edgeButton].
+ *
+ * These values work for the majority of [Shapes], usually across round toward medium round
+ * corners.
+ */
+ internal fun MaterialScope.defaultPrimaryLayoutMarginsWithTitleWithEdgeButton(): Padding =
+ percentagePadding(start = 7.1f / 100, end = 7.1f / 100)
+
+ /**
+ * Default side margins for the main slot of [primaryLayout] that should be used when the title
+ * slot is not present in the layout and bottom slot is set to be [edgeButton].
+ *
+ * These values work for the majority of [Shapes], usually across round toward medium round
+ * corners.
+ */
+ internal fun MaterialScope.defaultPrimaryLayoutMarginsWithoutTitleWithEdgeButton(): Padding =
+ percentagePadding(start = 12f / 100, end = 12f / 100)
+
+ /**
+ * Min side margins for the main slot of [primaryLayout] that should be used only when the main
+ * slot contains fully rounded corners, such as [Shapes.full] to avoid clipping, and when the
+ * bottom slot is set to be [edgeButton].
+ *
+ * This can be used regardless of the title slot presence.
+ */
+ internal fun MaterialScope.minPrimaryLayoutMarginsWithEdgeButton(): Padding =
+ percentagePadding(start = 3f / 100, end = 3f / 100)
+
+ /**
+ * Mid side margins for the main slot of [primaryLayout] that should be used when the main slot
+ * contains fully rounded to medium round corners, for example, larger than [Shapes.medium] to
+ * avoid clipping, and when there's title slot present and bottom slot is set to be
+ * [edgeButton].
+ */
+ internal fun MaterialScope.midPrimaryLayoutMarginsWithTitleWithEdgeButton(): Padding =
+ percentagePadding(start = 5.2f / 100, end = 5.2f / 100)
+
+ /**
+ * Mid side margins for the main slot of [primaryLayout] that should be used when the main slot
+ * contains fully rounded to medium round corners, for example, larger than [Shapes.medium] to
+ * avoid clipping, and when title slot is not present and bottom slot is set to be [edgeButton].
+ */
+ internal fun MaterialScope.midPrimaryLayoutMarginsWithoutTitleWithEdgeButton(): Padding =
+ percentagePadding(start = 7.1f / 100, end = 7.1f / 100)
+
+ /**
+ * Max side margins for the main slot of [primaryLayout] that should be used when the main slot
+ * contains square corners, for example, smaller than [Shapes.medium] to avoid clipping, and
+ * when there's title slot present and bottom slot is set to be [edgeButton].
+ */
+ internal fun MaterialScope.maxPrimaryLayoutMarginsWithTitleWithEdgeButton(): Padding =
+ percentagePadding(start = 10f / 100, end = 10f / 100)
+
+ /**
+ * Max side margins for the main slot of [primaryLayout] that should be used when the main slot
+ * contains square corners, for example, smaller than [Shapes.medium] to avoid clipping, and
+ * when title slot is not present and bottom slot is set to be [edgeButton].
+ */
+ internal fun MaterialScope.maxPrimaryLayoutMarginsWithoutTitleWithEdgeButton(): Padding =
+ percentagePadding(start = 15.5f / 100, end = 15.5f / 100)
+
+ // Bottom slot as other content. Bottom margin are not allowed to be customized in these cases.
+
+ /**
+ * Default side margins for the main slot of [primaryLayout] that should be used when the bottom
+ * slot is set to some other content besides [edgeButton].
+ *
+ * These values work for the majority of [Shapes], usually across round toward medium round
+ * corners. This can be used regardless of title slot presence.
+ */
+ internal fun MaterialScope.defaultPrimaryLayoutMarginsWithBottomSlotAsOther(): Padding =
+ percentagePadding(start = 12f / 100, end = 12f / 100)
+
+ /**
+ * Min side margins for the main slot of [primaryLayout] that should be used only when the main
+ * slot contains fully rounded corners, such as [Shapes.full] to avoid clipping, and when the
+ * bottom slot is set to some other content besides [edgeButton].
+ *
+ * This can be used regardless of the title slot presence.
+ */
+ internal fun MaterialScope.minPrimaryLayoutMarginsWithBottomSlotAsOther(): Padding =
+ percentagePadding(start = 3f / 100, end = 3f / 100)
+
+ /**
+ * Mid side margins for the main slot of [primaryLayout] that should be used when the main slot
+ * contains fully rounded to medium round corners, for example, larger than [Shapes.medium] to
+ * avoid clipping, and when there's title slot present and bottom slot is set to some other
+ * content besides [edgeButton].
+ */
+ internal fun MaterialScope.midPrimaryLayoutMarginsWithTitleWithBottomSlotAsOther(): Padding =
+ percentagePadding(start = 7.1f / 100, end = 7.1f / 100)
+
+ /**
+ * Mid side margins for the main slot of [primaryLayout] that should be used when the main slot
+ * contains fully rounded to medium round corners, for example, larger than [Shapes.medium] to
+ * avoid clipping, and when title slot is not present and bottom slot is set to some other
+ * content besides [edgeButton].
+ */
+ internal fun MaterialScope.midPrimaryLayoutMarginsWithoutTitleWithBottomSlotAsOther(): Padding =
+ percentagePadding(start = 8.3f / 100, end = 8.3f / 100)
+
+ /**
+ * Max side margins for the main slot of [primaryLayout] that should be used when the main slot
+ * contains square corners, for example, smaller than [Shapes.medium] to avoid clipping, and
+ * when there's title slot present and bottom slot is set to some other content besides
+ * [edgeButton].
+ */
+ internal fun MaterialScope.maxPrimaryLayoutMarginsWithTitleWithBottomSlotAsOther(): Padding =
+ percentagePadding(start = 14f / 100, end = 14f / 100)
+
+ /**
+ * Max side margins for the main slot of [primaryLayout] that should be used when the main slot
+ * contains square corners, for example, smaller than [Shapes.medium] to avoid clipping, and
+ * when title slot is not present and bottom slot is set to some other content besides
+ * [edgeButton].
+ */
+ internal fun MaterialScope.maxPrimaryLayoutMarginsWithoutTitleWithBottomSlotAsOther(): Padding =
+ percentagePadding(start = 15.5f / 100, end = 15.5f / 100)
+
+ // No bottom slot. Bottom margin are allowed to be customized in these cases.
+
+ /**
+ * Default side margins for the main slot of [primaryLayout] that should be used when title slot
+ * is present but the bottom slot is not.
+ *
+ * These values work for the majority of [Shapes], usually across round toward medium round
+ * corners.
+ */
+ internal fun MaterialScope.defaultPrimaryLayoutMarginsWithTitleWithoutBottomSlot(): Padding =
+ percentagePadding(start = 10f / 100, end = 10f / 100, bottom = 16.64f / 100)
+
+ /**
+ * Default side margins for the main slot of [primaryLayout] that should be used when neither
+ * title slot or bottom slot are present.
+ *
+ * These values work for the majority of [Shapes], usually across round toward medium round
+ * corners.
+ */
+ internal fun MaterialScope.defaultPrimaryLayoutMarginsWithoutTitleWithoutBottomSlot(): Padding =
+ percentagePadding(start = 12f / 100, end = 12f / 100, bottom = 14f / 100)
+
+ /**
+ * Min side margins for the main slot of [primaryLayout] that should be used only when the main
+ * slot contains fully rounded corners, such as [Shapes.full] to avoid clipping, and when the
+ * bottom slot is not present.
+ *
+ * This can be used regardless of the title slot presence.
+ */
+ internal fun MaterialScope.minPrimaryLayoutMarginsWithoutBottomSlot(): Padding =
+ percentagePadding(start = 3f / 100, end = 3f / 100, bottom = 10f / 100)
+
+ /**
+ * Mid side margins for the main slot of [primaryLayout] that should be used when the main slot
+ * contains fully rounded to medium round corners, for example, larger than [Shapes.medium] to
+ * avoid clipping, and when there's title slot present and bottom slot is not.
+ */
+ internal fun MaterialScope.midPrimaryLayoutMarginsWithTitleWithoutBottomSlot(): Padding =
+ percentagePadding(start = 5.2f / 100, end = 5.2f / 100, bottom = 19.6f / 100)
+
+ /**
+ * Mid side margins for the main slot of [primaryLayout] that should be used when the main slot
+ * contains fully rounded to medium round corners, for example, larger than [Shapes.medium] to
+ * avoid clipping, and when neither title slot or bottom slot are present.
+ */
+ internal fun MaterialScope.midPrimaryLayoutMarginsWithoutTitleWithoutBottomSlot(): Padding =
+ percentagePadding(start = 8.3f / 100, end = 8.3f / 100, bottom = 19.6f / 100)
+
+ /**
+ * Max side margins for the main slot of [primaryLayout] that should be used when the main slot
+ * contains square corners, for example, smaller than [Shapes.medium] to avoid clipping, and
+ * when there's title slot present and bottom slot is not.
+ */
+ internal fun MaterialScope.maxPrimaryLayoutMarginsWithTitleWithoutBottomSlot(): Padding =
+ percentagePadding(start = 14f / 100, end = 14f / 100, bottom = 16.64f / 100)
+
+ /**
+ * Max side margins for the main slot of [primaryLayout] that should be used when the main slot
+ * contains square corners, for example, smaller than [Shapes.medium] to avoid clipping, and
+ * when neither title slot or bottom slot are present.
+ */
+ internal fun MaterialScope.maxPrimaryLayoutMarginsWithoutTitleWithoutBottomSlot(): Padding =
+ percentagePadding(start = 16.64f / 100, end = 16.64f / 100, bottom = 14f / 100)
+
+ internal fun MaterialScope.maxSideMargin() = percentageWidthToDp(16.64f / 100)
+
+ internal fun MaterialScope.maxBottomMargin() = percentageHeightToDp(19.6f / 100)
+}
diff --git a/wear/protolayout/protolayout-material3/src/test/java/androidx/wear/protolayout/material3/ButtonTest.kt b/wear/protolayout/protolayout-material3/src/test/java/androidx/wear/protolayout/material3/ButtonTest.kt
index 7f18399..174d1af 100644
--- a/wear/protolayout/protolayout-material3/src/test/java/androidx/wear/protolayout/material3/ButtonTest.kt
+++ b/wear/protolayout/protolayout-material3/src/test/java/androidx/wear/protolayout/material3/ButtonTest.kt
@@ -83,6 +83,15 @@
}
@Test
+ fun avatarButton_size_default() {
+ LayoutElementAssertionsProvider(DEFAULT_AVATAR_BUTTON)
+ .onRoot()
+ .assert(hasWidth(expand()))
+ .assert(hasHeight(wrapWithMinTapTargetDimension()))
+ .assert(hasTag(ButtonDefaults.METADATA_TAG_BUTTON))
+ }
+
+ @Test
fun compactButton_size_default() {
LayoutElementAssertionsProvider(DEFAULT_COMPACT_BUTTON)
.onRoot()
@@ -164,6 +173,27 @@
}
@Test
+ fun avatarButton_hasLabel_asText() {
+ LayoutElementAssertionsProvider(DEFAULT_AVATAR_BUTTON)
+ .onElement(hasText(TEXT))
+ .assertExists()
+ }
+
+ @Test
+ fun avatarButton_hasSecondaryLabel_asText() {
+ LayoutElementAssertionsProvider(DEFAULT_AVATAR_BUTTON)
+ .onElement(hasText(TEXT2))
+ .assertExists()
+ }
+
+ @Test
+ fun avatarButton_hasAvatar_asImage() {
+ LayoutElementAssertionsProvider(DEFAULT_AVATAR_BUTTON)
+ .onElement(hasImage(IMAGE_ID))
+ .assertExists()
+ }
+
+ @Test
fun compactButton_hasLabel_asText() {
LayoutElementAssertionsProvider(DEFAULT_COMPACT_BUTTON)
.onElement(hasText(TEXT))
@@ -295,6 +325,17 @@
)
}
+ private val DEFAULT_AVATAR_BUTTON =
+ materialScope(CONTEXT, DEVICE_CONFIGURATION) {
+ avatarButton(
+ onClick = CLICKABLE,
+ modifier = LayoutModifier.contentDescription(CONTENT_DESCRIPTION),
+ labelContent = { text(TEXT.layoutString) },
+ secondaryLabelContent = { text(TEXT2.layoutString) },
+ avatarContent = { icon(IMAGE_ID) }
+ )
+ }
+
private val DEFAULT_COMPACT_BUTTON =
materialScope(CONTEXT, DEVICE_CONFIGURATION) {
compactButton(
diff --git a/wear/protolayout/protolayout-material3/src/test/java/androidx/wear/protolayout/material3/MaterialScopeTest.kt b/wear/protolayout/protolayout-material3/src/test/java/androidx/wear/protolayout/material3/MaterialScopeTest.kt
index cf470ad..1f3bd04 100644
--- a/wear/protolayout/protolayout-material3/src/test/java/androidx/wear/protolayout/material3/MaterialScopeTest.kt
+++ b/wear/protolayout/protolayout-material3/src/test/java/androidx/wear/protolayout/material3/MaterialScopeTest.kt
@@ -56,7 +56,8 @@
defaultTextElementStyle = TextElementStyle(),
defaultIconStyle = IconStyle(),
defaultBackgroundImageStyle = BackgroundImageStyle(),
- defaultAvatarImageStyle = AvatarImageStyle()
+ defaultAvatarImageStyle = AvatarImageStyle(),
+ layoutSlotsPresence = LayoutSlotsPresence()
)
assertThat(scopeWithDefaultTheme.deviceConfiguration).isEqualTo(DEVICE_PARAMETERS)
@@ -88,7 +89,8 @@
defaultTextElementStyle = TextElementStyle(),
defaultIconStyle = IconStyle(),
defaultBackgroundImageStyle = BackgroundImageStyle(),
- defaultAvatarImageStyle = AvatarImageStyle()
+ defaultAvatarImageStyle = AvatarImageStyle(),
+ layoutSlotsPresence = LayoutSlotsPresence()
)
assertThat(materialScope.deviceConfiguration).isEqualTo(DEVICE_PARAMETERS)
@@ -124,7 +126,8 @@
defaultTextElementStyle = TextElementStyle(),
defaultIconStyle = IconStyle(),
defaultBackgroundImageStyle = BackgroundImageStyle(),
- defaultAvatarImageStyle = AvatarImageStyle()
+ defaultAvatarImageStyle = AvatarImageStyle(),
+ layoutSlotsPresence = LayoutSlotsPresence()
)
assertThat(isDynamicColorSchemeEnabled(materialScope.context)).isFalse()
diff --git a/wear/protolayout/protolayout-material3/src/test/java/androidx/wear/protolayout/material3/PrimaryLayoutMarginsTest.kt b/wear/protolayout/protolayout-material3/src/test/java/androidx/wear/protolayout/material3/PrimaryLayoutMarginsTest.kt
new file mode 100644
index 0000000..024847d
--- /dev/null
+++ b/wear/protolayout/protolayout-material3/src/test/java/androidx/wear/protolayout/material3/PrimaryLayoutMarginsTest.kt
@@ -0,0 +1,78 @@
+/*
+ * 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.wear.protolayout.material3
+
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.wear.protolayout.material3.MaterialScopeTest.Companion.DEVICE_PARAMETERS
+import androidx.wear.protolayout.material3.PrimaryLayoutMargins.Companion.customizedPrimaryLayoutMargin
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.annotation.internal.DoNotInstrument
+
+@RunWith(AndroidJUnit4::class)
+@DoNotInstrument
+class PrimaryLayoutMarginsTest {
+ @Test
+ fun customizedMargins_horizontal_buildsPadding() {
+ val start = 0.2f
+ val end = 0.3f
+ val margins: CustomPrimaryLayoutMargins =
+ customizedPrimaryLayoutMargin(start = start, end = end) as CustomPrimaryLayoutMargins
+
+ assertThat(margins.toPadding(SCOPE).start!!.value)
+ .isEqualTo(start * DEVICE_PARAMETERS.screenWidthDp)
+ assertThat(margins.toPadding(SCOPE).end!!.value)
+ .isEqualTo(end * DEVICE_PARAMETERS.screenWidthDp)
+ }
+
+ @Test
+ fun customizedMargins_all_buildsPadding() {
+ val start = 0.2f
+ val end = 0.3f
+ val bottom = 0.4f
+ val margins: CustomPrimaryLayoutMargins =
+ customizedPrimaryLayoutMargin(start = start, end = end, bottom = bottom)
+ as CustomPrimaryLayoutMargins
+
+ assertThat(margins.toPadding(SCOPE).start!!.value)
+ .isEqualTo(start * DEVICE_PARAMETERS.screenWidthDp)
+ assertThat(margins.toPadding(SCOPE).end!!.value)
+ .isEqualTo(end * DEVICE_PARAMETERS.screenWidthDp)
+ assertThat(margins.toPadding(SCOPE).bottom!!.value)
+ .isEqualTo(bottom * DEVICE_PARAMETERS.screenWidthDp)
+ }
+
+ companion object {
+ val SCOPE =
+ MaterialScope(
+ context = getApplicationContext(),
+ deviceConfiguration = DEVICE_PARAMETERS,
+ allowDynamicTheme = true,
+ theme =
+ MaterialTheme(
+ colorScheme = dynamicColorScheme(context = getApplicationContext())
+ ),
+ defaultTextElementStyle = TextElementStyle(),
+ defaultIconStyle = IconStyle(),
+ defaultBackgroundImageStyle = BackgroundImageStyle(),
+ defaultAvatarImageStyle = AvatarImageStyle(),
+ layoutSlotsPresence = LayoutSlotsPresence()
+ )
+ }
+}
diff --git a/wear/protolayout/protolayout-renderer/build.gradle b/wear/protolayout/protolayout-renderer/build.gradle
index 5ba8d45..3fff1ce 100644
--- a/wear/protolayout/protolayout-renderer/build.gradle
+++ b/wear/protolayout/protolayout-renderer/build.gradle
@@ -26,10 +26,12 @@
plugins {
id("AndroidXPlugin")
id("com.android.library")
+ id("org.jetbrains.kotlin.android")
}
dependencies {
api(libs.jspecify)
+ api(libs.kotlinStdlib)
annotationProcessor(libs.nullaway)
api("androidx.annotation:annotation:1.8.1")
api("androidx.core:core:1.7.0")
diff --git a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/PropHelpers.kt b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/PropHelpers.kt
new file mode 100644
index 0000000..35ec5a4
--- /dev/null
+++ b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/PropHelpers.kt
@@ -0,0 +1,192 @@
+/*
+ * 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.wear.protolayout.renderer.inflater
+
+import android.util.Log
+import androidx.wear.protolayout.proto.ColorProto.ColorProp
+import androidx.wear.protolayout.proto.DimensionProto.DegreesProp
+import androidx.wear.protolayout.proto.DimensionProto.DpProp
+import androidx.wear.protolayout.proto.TypesProto.BoolProp
+import androidx.wear.protolayout.proto.TypesProto.FloatProp
+import androidx.wear.protolayout.proto.TypesProto.StringProp
+import androidx.wear.protolayout.renderer.dynamicdata.ProtoLayoutDynamicDataPipeline.PipelineMaker
+import java.util.Locale
+import java.util.Optional
+import java.util.function.Consumer
+
+/** Helpers for handling Prop classes' static and dynamic values. */
+internal object PropHelpers {
+ const val TAG = "ProtolayoutPropHelpers"
+
+ /** Handles a StringProp. */
+ @JvmStatic
+ fun handleProp(
+ stringProp: StringProp,
+ locale: Locale,
+ consumer: Consumer<String>,
+ posId: String,
+ pipelineMaker: Optional<PipelineMaker>,
+ ) {
+ if (stringProp.hasDynamicValue() && pipelineMaker.isPresent()) {
+ try {
+ pipelineMaker
+ .get()
+ .addPipelineFor(
+ stringProp.dynamicValue,
+ stringProp.value,
+ locale,
+ posId,
+ consumer
+ )
+ } catch (ex: RuntimeException) {
+ Log.e(TAG, "Error building pipeline", ex)
+ consumer.accept(stringProp.value)
+ }
+ } else {
+ consumer.accept(stringProp.value)
+ }
+ }
+
+ /** Handles a DegreesProp. */
+ @JvmStatic
+ fun handleProp(
+ degreesProp: DegreesProp,
+ consumer: Consumer<Float>,
+ posId: String,
+ pipelineMaker: Optional<PipelineMaker>,
+ ) {
+ if (degreesProp.hasDynamicValue() && pipelineMaker.isPresent()) {
+ try {
+ pipelineMaker.get().addPipelineFor(degreesProp, degreesProp.value, posId, consumer)
+ } catch (ex: RuntimeException) {
+ Log.e(TAG, "Error building pipeline", ex)
+ consumer.accept(degreesProp.value)
+ }
+ } else {
+ consumer.accept(degreesProp.value)
+ }
+ }
+
+ /** Handles a DpProp. */
+ @JvmStatic
+ fun handleProp(
+ dpProp: DpProp,
+ consumer: Consumer<Float>,
+ posId: String,
+ pipelineMaker: Optional<PipelineMaker>,
+ ) = handleProp(dpProp, consumer, consumer, posId, pipelineMaker)
+
+ /** Handles a DpProp. */
+ @JvmStatic
+ fun handleProp(
+ dpProp: DpProp,
+ staticValueConsumer: Consumer<Float>,
+ dynamicValueConsumer: Consumer<Float>,
+ posId: String,
+ pipelineMaker: Optional<PipelineMaker>,
+ ) {
+ if (dpProp.hasDynamicValue() && pipelineMaker.isPresent()) {
+ try {
+ pipelineMaker
+ .get()
+ .addPipelineFor(dpProp, dpProp.value, posId, dynamicValueConsumer)
+ } catch (ex: RuntimeException) {
+ Log.e(TAG, "Error building pipeline", ex)
+ staticValueConsumer.accept(dpProp.value)
+ }
+ } else {
+ staticValueConsumer.accept(dpProp.value)
+ }
+ }
+
+ /** Handles a ColorProp. */
+ @JvmStatic
+ fun handleProp(
+ colorProp: ColorProp,
+ consumer: Consumer<Int>,
+ posId: String,
+ pipelineMaker: Optional<PipelineMaker>,
+ ) {
+ if (colorProp.hasDynamicValue() && pipelineMaker.isPresent()) {
+ try {
+ pipelineMaker.get().addPipelineFor(colorProp, colorProp.argb, posId, consumer)
+ } catch (ex: RuntimeException) {
+ Log.e(TAG, "Error building pipeline", ex)
+ consumer.accept(colorProp.argb)
+ }
+ } else {
+ consumer.accept(colorProp.argb)
+ }
+ }
+
+ /** Handles a BoolProp. */
+ @JvmStatic
+ fun handleProp(
+ boolProp: BoolProp,
+ consumer: Consumer<Boolean>,
+ posId: String,
+ pipelineMaker: Optional<PipelineMaker>,
+ ) {
+ if (boolProp.hasDynamicValue() && pipelineMaker.isPresent()) {
+ try {
+ pipelineMaker.get().addPipelineFor(boolProp, boolProp.value, posId, consumer)
+ } catch (ex: RuntimeException) {
+ Log.e(TAG, "Error building pipeline", ex)
+ consumer.accept(boolProp.value)
+ }
+ } else {
+ consumer.accept(boolProp.value)
+ }
+ }
+
+ /** Handles a FloatProp. */
+ @JvmStatic
+ fun handleProp(
+ floatProp: FloatProp,
+ consumer: Consumer<Float>,
+ posId: String,
+ pipelineMaker: Optional<PipelineMaker>,
+ ) = handleProp(floatProp, consumer, consumer, posId, pipelineMaker)
+
+ /** Handles a FloatProp. */
+ @JvmStatic
+ fun handleProp(
+ floatProp: FloatProp,
+ staticValueConsumer: Consumer<Float>,
+ dynamicValueconsumer: Consumer<Float>,
+ posId: String,
+ pipelineMaker: Optional<PipelineMaker>,
+ ) {
+ if (floatProp.hasDynamicValue() && pipelineMaker.isPresent()) {
+ try {
+ pipelineMaker
+ .get()
+ .addPipelineFor(
+ floatProp.dynamicValue,
+ floatProp.value,
+ posId,
+ dynamicValueconsumer
+ )
+ } catch (ex: RuntimeException) {
+ Log.e(TAG, "Error building pipeline", ex)
+ staticValueConsumer.accept(floatProp.value)
+ }
+ } else {
+ staticValueConsumer.accept(floatProp.value)
+ }
+ }
+}
diff --git a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflater.java b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflater.java
index 012a095..41612f8 100644
--- a/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflater.java
+++ b/wear/protolayout/protolayout-renderer/src/main/java/androidx/wear/protolayout/renderer/inflater/ProtoLayoutInflater.java
@@ -26,6 +26,7 @@
import static androidx.wear.protolayout.renderer.common.ProtoLayoutDiffer.FIRST_CHILD_INDEX;
import static androidx.wear.protolayout.renderer.common.ProtoLayoutDiffer.ROOT_NODE_ID;
import static androidx.wear.protolayout.renderer.common.ProtoLayoutDiffer.getParentNodePosId;
+import static androidx.wear.protolayout.renderer.inflater.PropHelpers.handleProp;
import static com.google.common.util.concurrent.Futures.immediateFailedFuture;
import static com.google.common.util.concurrent.Futures.immediateFuture;
@@ -105,7 +106,6 @@
import androidx.wear.protolayout.proto.AlignmentProto.TextAlignment;
import androidx.wear.protolayout.proto.AlignmentProto.VerticalAlignment;
import androidx.wear.protolayout.proto.AlignmentProto.VerticalAlignmentProp;
-import androidx.wear.protolayout.proto.ColorProto.ColorProp;
import androidx.wear.protolayout.proto.DimensionProto.AngularDimension;
import androidx.wear.protolayout.proto.DimensionProto.ArcLineLength;
import androidx.wear.protolayout.proto.DimensionProto.ContainerDimension;
@@ -2829,6 +2829,7 @@
handleProp(
text.getText(),
+ mUiContext.getResources().getConfiguration().getLocales().get(0),
t -> {
// Underlines are applied using a Spannable here, rather than setting paint bits
// (or
@@ -4238,152 +4239,6 @@
}
/**
- * Either yield the constant value stored in stringProp, or register for updates if it is
- * dynamic property.
- *
- * <p>If both are set, this routine will yield the constant value if and only if this renderer
- * has a dynamic pipeline (i.e. {code mDataPipeline} is non-null), otherwise it will only
- * subscribe for dynamic updates. If the dynamic pipeline ever yields an invalid value (via
- * {@code onStateInvalid}), then stringProp's static valid will be used instead.
- */
- private void handleProp(
- StringProp stringProp,
- Consumer<String> consumer,
- String posId,
- Optional<PipelineMaker> pipelineMaker) {
- if (stringProp.hasDynamicValue() && pipelineMaker.isPresent()) {
- try {
- pipelineMaker
- .get()
- .addPipelineFor(
- stringProp.getDynamicValue(),
- stringProp.getValue(),
- mUiContext.getResources().getConfiguration().getLocales().get(0),
- posId,
- consumer);
- } catch (RuntimeException ex) {
- Log.e(TAG, "Error building pipeline", ex);
- consumer.accept(stringProp.getValue());
- }
- } else {
- consumer.accept(stringProp.getValue());
- }
- }
-
- private void handleProp(
- DegreesProp degreesProp,
- Consumer<Float> consumer,
- String posId,
- Optional<PipelineMaker> pipelineMaker) {
- if (degreesProp.hasDynamicValue() && pipelineMaker.isPresent()) {
- try {
- pipelineMaker
- .get()
- .addPipelineFor(degreesProp, degreesProp.getValue(), posId, consumer);
- } catch (RuntimeException ex) {
- Log.e(TAG, "Error building pipeline", ex);
- consumer.accept(degreesProp.getValue());
- }
- } else {
- consumer.accept(degreesProp.getValue());
- }
- }
-
- private void handleProp(
- DpProp dpProp,
- Consumer<Float> consumer,
- String posId,
- Optional<PipelineMaker> pipelineMaker) {
- handleProp(dpProp, consumer, consumer, posId, pipelineMaker);
- }
-
- private void handleProp(
- DpProp dpProp,
- Consumer<Float> staticValueConsumer,
- Consumer<Float> dynamicValueConsumer,
- String posId,
- Optional<PipelineMaker> pipelineMaker) {
- if (dpProp.hasDynamicValue() && pipelineMaker.isPresent()) {
- try {
- pipelineMaker
- .get()
- .addPipelineFor(dpProp, dpProp.getValue(), posId, dynamicValueConsumer);
- } catch (RuntimeException ex) {
- Log.e(TAG, "Error building pipeline", ex);
- staticValueConsumer.accept(dpProp.getValue());
- }
- } else {
- staticValueConsumer.accept(dpProp.getValue());
- }
- }
-
- private void handleProp(
- ColorProp colorProp,
- Consumer<Integer> consumer,
- String posId,
- Optional<PipelineMaker> pipelineMaker) {
- if (colorProp.hasDynamicValue() && pipelineMaker.isPresent()) {
- try {
- pipelineMaker.get().addPipelineFor(colorProp, colorProp.getArgb(), posId, consumer);
- } catch (RuntimeException ex) {
- Log.e(TAG, "Error building pipeline", ex);
- consumer.accept(colorProp.getArgb());
- }
- } else {
- consumer.accept(colorProp.getArgb());
- }
- }
-
- private void handleProp(
- BoolProp boolProp,
- Consumer<Boolean> consumer,
- String posId,
- Optional<PipelineMaker> pipelineMaker) {
- if (boolProp.hasDynamicValue() && pipelineMaker.isPresent()) {
- try {
- pipelineMaker.get().addPipelineFor(boolProp, boolProp.getValue(), posId, consumer);
- } catch (RuntimeException ex) {
- Log.e(TAG, "Error building pipeline", ex);
- consumer.accept(boolProp.getValue());
- }
- } else {
- consumer.accept(boolProp.getValue());
- }
- }
-
- private void handleProp(
- FloatProp floatProp,
- Consumer<Float> consumer,
- String posId,
- Optional<PipelineMaker> pipelineMaker) {
- handleProp(floatProp, consumer, consumer, posId, pipelineMaker);
- }
-
- private void handleProp(
- FloatProp floatProp,
- Consumer<Float> staticValueConsumer,
- Consumer<Float> dynamicValueconsumer,
- String posId,
- Optional<PipelineMaker> pipelineMaker) {
- if (floatProp.hasDynamicValue() && pipelineMaker.isPresent()) {
- try {
- pipelineMaker
- .get()
- .addPipelineFor(
- floatProp.getDynamicValue(),
- floatProp.getValue(),
- posId,
- dynamicValueconsumer);
- } catch (RuntimeException ex) {
- Log.e(TAG, "Error building pipeline", ex);
- staticValueConsumer.accept(floatProp.getValue());
- }
- } else {
- staticValueConsumer.accept(floatProp.getValue());
- }
- }
-
- /**
* Resolves the value for layout to be used in a Size Wrapper for elements containing dynamic
* values. Returns null if no size wrapper is needed.
*/
@@ -4959,6 +4814,7 @@
if (semantics.hasContentDescription()) {
handleProp(
semantics.getContentDescription(),
+ mUiContext.getResources().getConfiguration().getLocales().get(0),
view::setContentDescription,
posId,
pipelineMaker);
@@ -4970,6 +4826,7 @@
if (semantics.hasStateDescription()) {
handleProp(
semantics.getStateDescription(),
+ mUiContext.getResources().getConfiguration().getLocales().get(0),
(state) -> ViewCompat.setStateDescription(view, state),
posId,
pipelineMaker);
diff --git a/wear/protolayout/protolayout/api/current.txt b/wear/protolayout/protolayout/api/current.txt
index da4e766..4fb6ee7 100644
--- a/wear/protolayout/protolayout/api/current.txt
+++ b/wear/protolayout/protolayout/api/current.txt
@@ -1436,11 +1436,11 @@
public final class TriggerBuilders {
method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static androidx.wear.protolayout.TriggerBuilders.Trigger createOnConditionMetTrigger(androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool);
method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static androidx.wear.protolayout.TriggerBuilders.Trigger createOnLoadTrigger();
- method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static androidx.wear.protolayout.TriggerBuilders.Trigger createOnVisibleOnceTrigger();
+ method @SuppressCompatibility @androidx.wear.protolayout.expression.ProtoLayoutExperimental @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static androidx.wear.protolayout.TriggerBuilders.Trigger createOnVisibleOnceTrigger();
method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static androidx.wear.protolayout.TriggerBuilders.Trigger createOnVisibleTrigger();
}
- @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static final class TriggerBuilders.OnVisibleOnceTrigger implements androidx.wear.protolayout.TriggerBuilders.Trigger {
+ @SuppressCompatibility @androidx.wear.protolayout.expression.ProtoLayoutExperimental @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static final class TriggerBuilders.OnVisibleOnceTrigger implements androidx.wear.protolayout.TriggerBuilders.Trigger {
}
public static final class TriggerBuilders.OnVisibleOnceTrigger.Builder {
@@ -1564,17 +1564,17 @@
}
public interface LayoutModifier {
- method public <R> R foldIn(R initial, kotlin.jvm.functions.Function2<? super R,? super androidx.wear.protolayout.modifiers.LayoutModifier.Element,? extends R> operation);
+ method public <R> R foldRight(R initial, kotlin.jvm.functions.Function2<? super R,? super androidx.wear.protolayout.modifiers.LayoutModifier.Element,? extends R> operation);
method public default infix androidx.wear.protolayout.modifiers.LayoutModifier then(androidx.wear.protolayout.modifiers.LayoutModifier other);
field public static final androidx.wear.protolayout.modifiers.LayoutModifier.Companion Companion;
}
public static final class LayoutModifier.Companion implements androidx.wear.protolayout.modifiers.LayoutModifier {
- method public <R> R foldIn(R initial, kotlin.jvm.functions.Function2<? super R,? super androidx.wear.protolayout.modifiers.LayoutModifier.Element,? extends R> operation);
+ method public <R> R foldRight(R initial, kotlin.jvm.functions.Function2<? super R,? super androidx.wear.protolayout.modifiers.LayoutModifier.Element,? extends R> operation);
}
public static interface LayoutModifier.Element extends androidx.wear.protolayout.modifiers.LayoutModifier {
- method public default <R> R foldIn(R initial, kotlin.jvm.functions.Function2<? super R,? super androidx.wear.protolayout.modifiers.LayoutModifier.Element,? extends R> operation);
+ method public default <R> R foldRight(R initial, kotlin.jvm.functions.Function2<? super R,? super androidx.wear.protolayout.modifiers.LayoutModifier.Element,? extends R> operation);
}
public final class ModifierAppliersKt {
diff --git a/wear/protolayout/protolayout/api/restricted_current.txt b/wear/protolayout/protolayout/api/restricted_current.txt
index da4e766..4fb6ee7 100644
--- a/wear/protolayout/protolayout/api/restricted_current.txt
+++ b/wear/protolayout/protolayout/api/restricted_current.txt
@@ -1436,11 +1436,11 @@
public final class TriggerBuilders {
method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static androidx.wear.protolayout.TriggerBuilders.Trigger createOnConditionMetTrigger(androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool);
method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static androidx.wear.protolayout.TriggerBuilders.Trigger createOnLoadTrigger();
- method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static androidx.wear.protolayout.TriggerBuilders.Trigger createOnVisibleOnceTrigger();
+ method @SuppressCompatibility @androidx.wear.protolayout.expression.ProtoLayoutExperimental @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static androidx.wear.protolayout.TriggerBuilders.Trigger createOnVisibleOnceTrigger();
method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static androidx.wear.protolayout.TriggerBuilders.Trigger createOnVisibleTrigger();
}
- @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static final class TriggerBuilders.OnVisibleOnceTrigger implements androidx.wear.protolayout.TriggerBuilders.Trigger {
+ @SuppressCompatibility @androidx.wear.protolayout.expression.ProtoLayoutExperimental @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static final class TriggerBuilders.OnVisibleOnceTrigger implements androidx.wear.protolayout.TriggerBuilders.Trigger {
}
public static final class TriggerBuilders.OnVisibleOnceTrigger.Builder {
@@ -1564,17 +1564,17 @@
}
public interface LayoutModifier {
- method public <R> R foldIn(R initial, kotlin.jvm.functions.Function2<? super R,? super androidx.wear.protolayout.modifiers.LayoutModifier.Element,? extends R> operation);
+ method public <R> R foldRight(R initial, kotlin.jvm.functions.Function2<? super R,? super androidx.wear.protolayout.modifiers.LayoutModifier.Element,? extends R> operation);
method public default infix androidx.wear.protolayout.modifiers.LayoutModifier then(androidx.wear.protolayout.modifiers.LayoutModifier other);
field public static final androidx.wear.protolayout.modifiers.LayoutModifier.Companion Companion;
}
public static final class LayoutModifier.Companion implements androidx.wear.protolayout.modifiers.LayoutModifier {
- method public <R> R foldIn(R initial, kotlin.jvm.functions.Function2<? super R,? super androidx.wear.protolayout.modifiers.LayoutModifier.Element,? extends R> operation);
+ method public <R> R foldRight(R initial, kotlin.jvm.functions.Function2<? super R,? super androidx.wear.protolayout.modifiers.LayoutModifier.Element,? extends R> operation);
}
public static interface LayoutModifier.Element extends androidx.wear.protolayout.modifiers.LayoutModifier {
- method public default <R> R foldIn(R initial, kotlin.jvm.functions.Function2<? super R,? super androidx.wear.protolayout.modifiers.LayoutModifier.Element,? extends R> operation);
+ method public default <R> R foldRight(R initial, kotlin.jvm.functions.Function2<? super R,? super androidx.wear.protolayout.modifiers.LayoutModifier.Element,? extends R> operation);
}
public final class ModifierAppliersKt {
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ResourceBuilders.java b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ResourceBuilders.java
index a51c996..99e14b8 100644
--- a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ResourceBuilders.java
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ResourceBuilders.java
@@ -521,6 +521,7 @@
}
/** Gets the trigger to start the animation. */
+ @OptIn(markerClass = ProtoLayoutExperimental.class)
public @Nullable Trigger getStartTrigger() {
if (mImpl.hasStartTrigger()) {
return TriggerBuilders.triggerFromProto(mImpl.getStartTrigger());
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/TriggerBuilders.java b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/TriggerBuilders.java
index d05a53c..1c7a82a 100644
--- a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/TriggerBuilders.java
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/TriggerBuilders.java
@@ -23,6 +23,7 @@
import androidx.wear.protolayout.expression.DynamicBuilders;
import androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool;
import androidx.wear.protolayout.expression.Fingerprint;
+import androidx.wear.protolayout.expression.ProtoLayoutExperimental;
import androidx.wear.protolayout.expression.RequiresSchemaVersion;
import androidx.wear.protolayout.proto.TriggerProto;
@@ -66,6 +67,7 @@
* that the layout becomes visible.
*/
@RequiresSchemaVersion(major = 1, minor = 200)
+ @ProtoLayoutExperimental
public static @NonNull Trigger createOnVisibleOnceTrigger() {
return new OnVisibleOnceTrigger.Builder().build();
}
@@ -138,6 +140,7 @@
* the first time.
*/
@RequiresSchemaVersion(major = 1, minor = 200)
+ @ProtoLayoutExperimental
public static final class OnVisibleOnceTrigger implements Trigger {
private final TriggerProto.OnVisibleOnceTrigger mImpl;
private final @Nullable Fingerprint mFingerprint;
@@ -377,6 +380,7 @@
/** Creates a new wrapper instance from the proto. */
@RestrictTo(Scope.LIBRARY_GROUP)
+ @ProtoLayoutExperimental
public static @NonNull Trigger triggerFromProto(
TriggerProto.@NonNull Trigger proto, @Nullable Fingerprint fingerprint) {
if (proto.hasOnVisibleTrigger()) {
@@ -394,6 +398,7 @@
throw new IllegalStateException("Proto was not a recognised instance of Trigger");
}
+ @ProtoLayoutExperimental
static @NonNull Trigger triggerFromProto(TriggerProto.@NonNull Trigger proto) {
return triggerFromProto(proto, null);
}
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/Background.kt b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/Background.kt
index 322861d..89df3a3 100644
--- a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/Background.kt
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/Background.kt
@@ -115,7 +115,7 @@
): LayoutModifier = this then BaseCornerElement(bottomRightRadius = cornerRadius(x, y))
internal class BaseBackgroundElement(val color: LayoutColor) : LayoutModifier.Element {
- fun foldIn(initial: Background.Builder?): Background.Builder =
+ fun mergeTo(initial: Background.Builder?): Background.Builder =
(initial ?: Background.Builder()).setColor(color.prop)
}
@@ -127,7 +127,7 @@
@RequiresSchemaVersion(major = 1, minor = 400) val bottomRightRadius: CornerRadius? = null
) : LayoutModifier.Element {
@SuppressLint("ProtoLayoutMinSchema")
- fun foldIn(initial: Corner.Builder?): Corner.Builder =
+ fun mergeTo(initial: Corner.Builder?): Corner.Builder =
(initial ?: Corner.Builder()).apply {
cornerRadiusDp?.let { setRadius(cornerRadiusDp.dp) }
topLeftRadius?.let { setTopLeftRadius(cornerRadius(it.x.value, it.y.value)) }
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/Border.kt b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/Border.kt
index d3fca75..47e1ec1 100644
--- a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/Border.kt
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/Border.kt
@@ -33,6 +33,6 @@
internal class BaseBorderElement(@Dimension(DP) val width: Float, val color: LayoutColor) :
LayoutModifier.Element {
- fun foldIn(initial: Border.Builder?): Border.Builder =
+ fun mergeTo(initial: Border.Builder?): Border.Builder =
(initial ?: Border.Builder()).setWidth(width.dp).setColor(color.prop)
}
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/Clickable.kt b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/Clickable.kt
index da4d74f..a807b15 100644
--- a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/Clickable.kt
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/Clickable.kt
@@ -122,7 +122,7 @@
@Dimension(DP) val minClickableHeight: Float = Float.NaN,
) : LayoutModifier.Element {
@SuppressLint("ProtoLayoutMinSchema")
- fun foldIn(initial: Clickable.Builder?): Clickable.Builder =
+ fun mergeTo(initial: Clickable.Builder?): Clickable.Builder =
(initial ?: Clickable.Builder()).apply {
if (!id.isNullOrEmpty()) setId(id)
action?.let { setOnClick(it) }
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/LayoutModifier.kt b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/LayoutModifier.kt
index 74657a0..468ef56 100644
--- a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/LayoutModifier.kt
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/LayoutModifier.kt
@@ -16,6 +16,7 @@
package androidx.wear.protolayout.modifiers
+import androidx.wear.protolayout.modifiers.LayoutModifier.Element
import java.util.Objects
/**
@@ -28,14 +29,12 @@
interface LayoutModifier {
/**
* Accumulates a value starting with [initial] and applying [operation] to the current value and
- * each element from outside in.
+ * each element from left to right.
*
- * Elements wrap one another in a chain from left to right; an [Element] that appears to the
- * left of another in a `+` expression or in [operation]'s parameter order affects all of the
- * elements that appear after it. [foldIn] may be used to accumulate a value starting from the
- * parent or head of the modifier chain to the final wrapped child.
+ * [foldRight] may be used to accumulate a value starting from the head of the modifier chain to
+ * the final modifier element.
*/
- fun <R> foldIn(initial: R, operation: (R, Element) -> R): R
+ fun <R> foldRight(initial: R, operation: (R, Element) -> R): R
/**
* Concatenates this modifier with another.
@@ -47,7 +46,7 @@
/** A single element contained within a [LayoutModifier] chain. */
interface Element : LayoutModifier {
- override fun <R> foldIn(initial: R, operation: (R, Element) -> R): R =
+ override fun <R> foldRight(initial: R, operation: (R, Element) -> R): R =
operation(initial, this)
}
@@ -58,7 +57,7 @@
*/
companion object : LayoutModifier {
@Suppress("MissingJvmstatic")
- override fun <R> foldIn(initial: R, operation: (R, Element) -> R): R = initial
+ override fun <R> foldRight(initial: R, operation: (R, Element) -> R): R = initial
@Suppress("MissingJvmstatic")
override infix fun then(other: LayoutModifier): LayoutModifier = other
@@ -68,24 +67,24 @@
}
/**
- * A node in a [LayoutModifier] chain. A CombinedModifier always contains at least two elements; a
- * * Modifier [outer] that wraps around the Modifier [inner].
+ * A node in a [LayoutModifier] chain. A [CombinedLayoutModifier] always contains at least two
+ * elements.
*/
internal class CombinedLayoutModifier(
- private val outer: LayoutModifier,
- private val inner: LayoutModifier
+ private val left: LayoutModifier,
+ private val right: LayoutModifier
) : LayoutModifier {
- override fun <R> foldIn(initial: R, operation: (R, LayoutModifier.Element) -> R): R =
- inner.foldIn(outer.foldIn(initial, operation), operation)
+ override fun <R> foldRight(initial: R, operation: (R, Element) -> R): R =
+ right.foldRight(left.foldRight(initial, operation), operation)
override fun equals(other: Any?): Boolean =
- other is CombinedLayoutModifier && outer == other.outer && inner == other.inner
+ other is CombinedLayoutModifier && left == other.left && right == other.right
- override fun hashCode(): Int = Objects.hash(outer, inner)
+ override fun hashCode(): Int = Objects.hash(left, right)
override fun toString(): String =
"[" +
- foldIn("") { acc, element ->
+ foldRight("") { acc, element ->
if (acc.isEmpty()) element.toString() else "$acc, $element"
} +
"]"
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/Metadata.kt b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/Metadata.kt
index 9c0f8ca..7fb3fb2 100644
--- a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/Metadata.kt
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/Metadata.kt
@@ -35,6 +35,6 @@
fun LayoutModifier.tag(tag: String): LayoutModifier = tag(tag.toByteArray())
internal class BaseMetadataElement(val tagData: ByteArray) : LayoutModifier.Element {
- fun foldIn(initial: ElementMetadata.Builder?): ElementMetadata.Builder =
+ fun mergeTo(initial: ElementMetadata.Builder?): ElementMetadata.Builder =
(initial ?: ElementMetadata.Builder()).setTagData(tagData)
}
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/ModifierAppliers.kt b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/ModifierAppliers.kt
index aee3599..af956d0 100644
--- a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/ModifierAppliers.kt
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/ModifierAppliers.kt
@@ -44,17 +44,17 @@
var visible: BoolProp.Builder? = null
var opacity: FloatProp.Builder? = null
- this.foldIn(Unit) { _, e ->
+ this.foldRight(Unit) { _, e ->
when (e) {
- is BaseSemanticElement -> semantics = e.foldIn(semantics)
- is BaseBackgroundElement -> background = e.foldIn(background)
- is BaseCornerElement -> corners = e.foldIn(corners)
- is BaseClickableElement -> clickable = e.foldIn(clickable)
- is BasePaddingElement -> padding = e.foldIn(padding)
- is BaseMetadataElement -> metadata = e.foldIn(metadata)
- is BaseBorderElement -> border = e.foldIn(border)
- is BaseVisibilityElement -> visible = e.foldIn(visible)
- is BaseOpacityElement -> opacity = e.foldIn(opacity)
+ is BaseSemanticElement -> semantics = e.mergeTo(semantics)
+ is BaseBackgroundElement -> background = e.mergeTo(background)
+ is BaseCornerElement -> corners = e.mergeTo(corners)
+ is BaseClickableElement -> clickable = e.mergeTo(clickable)
+ is BasePaddingElement -> padding = e.mergeTo(padding)
+ is BaseMetadataElement -> metadata = e.mergeTo(metadata)
+ is BaseBorderElement -> border = e.mergeTo(border)
+ is BaseVisibilityElement -> visible = e.mergeTo(visible)
+ is BaseOpacityElement -> opacity = e.mergeTo(opacity)
}
}
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/Opacity.kt b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/Opacity.kt
index eeafb8f9..e8d6f3d 100644
--- a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/Opacity.kt
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/Opacity.kt
@@ -42,7 +42,7 @@
internal class BaseOpacityElement(val staticValue: Float, val dynamicValue: DynamicFloat? = null) :
LayoutModifier.Element {
@SuppressLint("ProtoLayoutMinSchema")
- fun foldIn(initial: FloatProp.Builder?): FloatProp.Builder =
+ fun mergeTo(initial: FloatProp.Builder?): FloatProp.Builder =
(initial ?: FloatProp.Builder(staticValue)).apply {
dynamicValue?.let { setDynamicValue(it) }
}
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/Padding.kt b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/Padding.kt
index 050b8ce..af7df74 100644
--- a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/Padding.kt
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/Padding.kt
@@ -139,7 +139,7 @@
val rtlAware: Boolean = true
) : LayoutModifier.Element {
- fun foldIn(initial: Padding.Builder?): Padding.Builder =
+ fun mergeTo(initial: Padding.Builder?): Padding.Builder =
(initial ?: Padding.Builder()).apply {
if (!start.isNaN()) {
setStart(start.dp)
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/Semantics.kt b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/Semantics.kt
index d77f5e3..7d1b332 100644
--- a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/Semantics.kt
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/Semantics.kt
@@ -58,7 +58,7 @@
@SemanticsRole val semanticsRole: Int = SEMANTICS_ROLE_NONE
) : LayoutModifier.Element {
@SuppressLint("ProtoLayoutMinSchema")
- fun foldIn(initial: Semantics.Builder?): Semantics.Builder =
+ fun mergeTo(initial: Semantics.Builder?): Semantics.Builder =
(initial ?: Semantics.Builder()).apply {
contentDescription?.let { setContentDescription(it) }
if (semanticsRole != SEMANTICS_ROLE_NONE) {
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/Visibility.kt b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/Visibility.kt
index b7eabd7..cb1596ab3 100644
--- a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/Visibility.kt
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/modifiers/Visibility.kt
@@ -46,7 +46,7 @@
val dynamicVisibility: DynamicBool? = null
) : LayoutModifier.Element {
@SuppressLint("ProtoLayoutMinSchema")
- fun foldIn(initial: BoolProp.Builder?): BoolProp.Builder =
+ fun mergeTo(initial: BoolProp.Builder?): BoolProp.Builder =
(initial ?: BoolProp.Builder(visibility)).apply {
dynamicVisibility?.let { setDynamicValue(it) }
}
diff --git a/wear/tiles/tiles-samples/src/main/java/androidx/wear/tiles/samples/tile/PlaygroundTileService.kt b/wear/tiles/tiles-samples/src/main/java/androidx/wear/tiles/samples/tile/PlaygroundTileService.kt
index 183279b..d370263 100644
--- a/wear/tiles/tiles-samples/src/main/java/androidx/wear/tiles/samples/tile/PlaygroundTileService.kt
+++ b/wear/tiles/tiles-samples/src/main/java/androidx/wear/tiles/samples/tile/PlaygroundTileService.kt
@@ -28,6 +28,7 @@
import androidx.wear.protolayout.TimelineBuilders
import androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat
import androidx.wear.protolayout.expression.VersionBuilders.VersionInfo
+import androidx.wear.protolayout.material3.AvatarButtonStyle.Companion.largeAvatarButtonStyle
import androidx.wear.protolayout.material3.ButtonDefaults.filledVariantButtonColors
import androidx.wear.protolayout.material3.CardColors
import androidx.wear.protolayout.material3.CardDefaults.filledTonalCardColors
@@ -36,8 +37,10 @@
import androidx.wear.protolayout.material3.DataCardStyle.Companion.extraLargeDataCardStyle
import androidx.wear.protolayout.material3.DataCardStyle.Companion.smallCompactDataCardStyle
import androidx.wear.protolayout.material3.MaterialScope
+import androidx.wear.protolayout.material3.PrimaryLayoutMargins.Companion.MAX_PRIMARY_LAYOUT_MARGIN
import androidx.wear.protolayout.material3.TextButtonStyle.Companion.smallTextButtonStyle
import androidx.wear.protolayout.material3.appCard
+import androidx.wear.protolayout.material3.avatarButton
import androidx.wear.protolayout.material3.avatarImage
import androidx.wear.protolayout.material3.button
import androidx.wear.protolayout.material3.buttonGroup
@@ -126,7 +129,8 @@
): LayoutElementBuilders.LayoutElement =
materialScope(context = context, deviceConfiguration = requestParams.deviceConfiguration) {
primaryLayout(
- mainSlot = { graphicDataCardSample() },
+ mainSlot = { oneSlotButtons() },
+ margins = MAX_PRIMARY_LAYOUT_MARGIN,
bottomSlot = {
textEdgeButton(
onClick = clickable(),
@@ -138,6 +142,17 @@
)
}
+private fun MaterialScope.avatarButtonSample() =
+ avatarButton(
+ onClick = clickable(),
+ modifier = LayoutModifier.contentDescription("Avatar button"),
+ avatarContent = { avatarImage(AVATAR_ID) },
+ style = largeAvatarButtonStyle(),
+ horizontalAlignment = LayoutElementBuilders.HORIZONTAL_ALIGN_END,
+ labelContent = { text("Primary label overflowing".layoutString) },
+ secondaryLabelContent = { text("Secondary label overflowing".layoutString) },
+ )
+
private fun MaterialScope.pillShapeButton() =
button(
onClick = clickable(),
diff --git a/webkit/webkit/api/1.13.0-beta01.txt b/webkit/webkit/api/1.13.0-beta01.txt
new file mode 100644
index 0000000..4a0366f
--- /dev/null
+++ b/webkit/webkit/api/1.13.0-beta01.txt
@@ -0,0 +1,570 @@
+// Signature format: 4.0
+package androidx.webkit {
+
+ @SuppressCompatibility @androidx.webkit.WebViewCompat.ExperimentalAsyncStartUp public interface BlockingStartUpLocation {
+ method public String getStackInformation();
+ }
+
+ @AnyThread public class CookieManagerCompat {
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_COOKIE_INFO, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static java.util.List<java.lang.String!> getCookieInfo(android.webkit.CookieManager, String);
+ }
+
+ public final class DropDataContentProvider extends android.content.ContentProvider {
+ ctor public DropDataContentProvider();
+ method public int delete(android.net.Uri, String?, String![]?);
+ method public String? getType(android.net.Uri);
+ method public android.net.Uri? insert(android.net.Uri, android.content.ContentValues?);
+ method public boolean onCreate();
+ method public android.database.Cursor? query(android.net.Uri, String![]?, String?, String![]?, String?);
+ method public int update(android.net.Uri, android.content.ContentValues?, String?, String![]?);
+ }
+
+ @UiThread public abstract class JavaScriptReplyProxy {
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void postMessage(byte[]);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_LISTENER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void postMessage(String);
+ }
+
+ @SuppressCompatibility @androidx.webkit.Profile.ExperimentalUrlPrefetch public class NoVarySearchHeader {
+ method @SuppressCompatibility @androidx.webkit.Profile.ExperimentalUrlPrefetch public static androidx.webkit.NoVarySearchHeader alwaysVaryData();
+ method @SuppressCompatibility @androidx.webkit.Profile.ExperimentalUrlPrefetch public static androidx.webkit.NoVarySearchHeader neverVaryData();
+ method @SuppressCompatibility @androidx.webkit.Profile.ExperimentalUrlPrefetch public static androidx.webkit.NoVarySearchHeader neverVaryExcept(boolean, java.util.List<java.lang.String!>);
+ method @SuppressCompatibility @androidx.webkit.Profile.ExperimentalUrlPrefetch public static androidx.webkit.NoVarySearchHeader varyExcept(boolean, java.util.List<java.lang.String!>);
+ field public final java.util.List<java.lang.String!> consideredQueryParameters;
+ field public final boolean ignoreDifferencesInParameters;
+ field public final java.util.List<java.lang.String!> ignoredQueryParameters;
+ field public final boolean varyOnKeyOrder;
+ }
+
+ @SuppressCompatibility @androidx.webkit.Profile.ExperimentalUrlPrefetch public interface OutcomeReceiverCompat<T, E extends java.lang.Throwable> {
+ method public default void onError(E);
+ method public void onResult(T!);
+ }
+
+ @SuppressCompatibility @androidx.webkit.Profile.ExperimentalUrlPrefetch public class PrefetchException extends java.lang.Exception {
+ ctor public PrefetchException();
+ ctor public PrefetchException(String);
+ }
+
+ @SuppressCompatibility @androidx.webkit.Profile.ExperimentalUrlPrefetch public class PrefetchNetworkException extends androidx.webkit.PrefetchException {
+ ctor public PrefetchNetworkException();
+ ctor public PrefetchNetworkException(int);
+ ctor public PrefetchNetworkException(String);
+ ctor public PrefetchNetworkException(String, int);
+ field public static final int NO_HTTP_RESPONSE_STATUS_CODE = 0; // 0x0
+ field public final int httpResponseStatusCode;
+ }
+
+ @SuppressCompatibility @androidx.webkit.WebViewCompat.ExperimentalUrlPrerender public class PrerenderException extends java.lang.Exception {
+ ctor public PrerenderException(String, Throwable?);
+ }
+
+ @SuppressCompatibility @androidx.webkit.WebViewCompat.ExperimentalUrlPrerender public interface PrerenderOperationCallback {
+ method public void onError(androidx.webkit.PrerenderException);
+ method public void onPrerenderActivated();
+ }
+
+ public class ProcessGlobalConfig {
+ ctor public ProcessGlobalConfig();
+ method public static void apply(androidx.webkit.ProcessGlobalConfig);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.STARTUP_FEATURE_SET_DATA_DIRECTORY_SUFFIX, enforcement="androidx.webkit.WebViewFeature#isConfigFeatureSupported(String, Context)") public androidx.webkit.ProcessGlobalConfig setDataDirectorySuffix(android.content.Context, String);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.STARTUP_FEATURE_SET_DIRECTORY_BASE_PATHS, enforcement="androidx.webkit.WebViewFeature#isConfigFeatureSupported(String, Context)") public androidx.webkit.ProcessGlobalConfig setDirectoryBasePaths(android.content.Context, java.io.File, java.io.File);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.STARTUP_FEATURE_CONFIGURE_PARTITIONED_COOKIES, enforcement="androidx.webkit.WebViewFeature#isConfigFeatureSupported(String, Context)") public androidx.webkit.ProcessGlobalConfig setPartitionedCookiesEnabled(android.content.Context, boolean);
+ }
+
+ public interface Profile {
+ method @SuppressCompatibility @RequiresFeature(name=androidx.webkit.WebViewFeature.PROFILE_URL_PREFETCH, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread @androidx.webkit.Profile.ExperimentalUrlPrefetch public void clearPrefetchAsync(String, java.util.concurrent.Executor, androidx.webkit.OutcomeReceiverCompat<java.lang.Void!,androidx.webkit.PrefetchException!>);
+ method @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public android.webkit.CookieManager getCookieManager();
+ method @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public android.webkit.GeolocationPermissions getGeolocationPermissions();
+ method @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public String getName();
+ method @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public android.webkit.ServiceWorkerController getServiceWorkerController();
+ method @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public android.webkit.WebStorage getWebStorage();
+ method @SuppressCompatibility @RequiresFeature(name=androidx.webkit.WebViewFeature.PROFILE_URL_PREFETCH, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread @androidx.webkit.Profile.ExperimentalUrlPrefetch public void prefetchUrlAsync(String, android.os.CancellationSignal?, java.util.concurrent.Executor, androidx.webkit.OutcomeReceiverCompat<java.lang.Void!,androidx.webkit.PrefetchException!>);
+ method @SuppressCompatibility @RequiresFeature(name=androidx.webkit.WebViewFeature.PROFILE_URL_PREFETCH, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread @androidx.webkit.Profile.ExperimentalUrlPrefetch public void prefetchUrlAsync(String, android.os.CancellationSignal?, java.util.concurrent.Executor, androidx.webkit.SpeculativeLoadingParameters, androidx.webkit.OutcomeReceiverCompat<java.lang.Void!,androidx.webkit.PrefetchException!>);
+ method @SuppressCompatibility @RequiresFeature(name=androidx.webkit.WebViewFeature.SPECULATIVE_LOADING_CONFIG, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread @androidx.webkit.Profile.ExperimentalUrlPrefetch public void setSpeculativeLoadingConfig(androidx.webkit.SpeculativeLoadingConfig);
+ field public static final String DEFAULT_PROFILE_NAME = "Default";
+ }
+
+ @SuppressCompatibility @RequiresOptIn(level=androidx.annotation.RequiresOptIn.Level.ERROR) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.FIELD}) public static @interface Profile.ExperimentalUrlPrefetch {
+ }
+
+ @UiThread public interface ProfileStore {
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public boolean deleteProfile(String);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public java.util.List<java.lang.String!> getAllProfileNames();
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.ProfileStore getInstance();
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public androidx.webkit.Profile getOrCreateProfile(String);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public androidx.webkit.Profile? getProfile(String);
+ }
+
+ public final class ProxyConfig {
+ method public java.util.List<java.lang.String!> getBypassRules();
+ method public java.util.List<androidx.webkit.ProxyConfig.ProxyRule!> getProxyRules();
+ method public boolean isReverseBypassEnabled();
+ field public static final String MATCH_ALL_SCHEMES = "*";
+ field public static final String MATCH_HTTP = "http";
+ field public static final String MATCH_HTTPS = "https";
+ }
+
+ public static final class ProxyConfig.Builder {
+ ctor public ProxyConfig.Builder();
+ ctor public ProxyConfig.Builder(androidx.webkit.ProxyConfig);
+ method public androidx.webkit.ProxyConfig.Builder addBypassRule(String);
+ method public androidx.webkit.ProxyConfig.Builder addDirect();
+ method public androidx.webkit.ProxyConfig.Builder addDirect(String);
+ method public androidx.webkit.ProxyConfig.Builder addProxyRule(String);
+ method public androidx.webkit.ProxyConfig.Builder addProxyRule(String, String);
+ method public androidx.webkit.ProxyConfig build();
+ method public androidx.webkit.ProxyConfig.Builder bypassSimpleHostnames();
+ method public androidx.webkit.ProxyConfig.Builder removeImplicitRules();
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.PROXY_OVERRIDE_REVERSE_BYPASS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public androidx.webkit.ProxyConfig.Builder setReverseBypassEnabled(boolean);
+ }
+
+ public static final class ProxyConfig.ProxyRule {
+ method public String getSchemeFilter();
+ method public String getUrl();
+ }
+
+ @AnyThread public abstract class ProxyController {
+ method public abstract void clearProxyOverride(java.util.concurrent.Executor, Runnable);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.PROXY_OVERRIDE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.ProxyController getInstance();
+ method public abstract void setProxyOverride(androidx.webkit.ProxyConfig, java.util.concurrent.Executor, Runnable);
+ }
+
+ public abstract class SafeBrowsingResponseCompat {
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void backToSafety(boolean);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_RESPONSE_PROCEED, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void proceed(boolean);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_RESPONSE_SHOW_INTERSTITIAL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void showInterstitial(boolean);
+ }
+
+ public interface ScriptHandler {
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.DOCUMENT_START_SCRIPT, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public void remove();
+ }
+
+ public abstract class ServiceWorkerClientCompat {
+ ctor public ServiceWorkerClientCompat();
+ method @WorkerThread public abstract android.webkit.WebResourceResponse? shouldInterceptRequest(android.webkit.WebResourceRequest);
+ }
+
+ @AnyThread public abstract class ServiceWorkerControllerCompat {
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.ServiceWorkerControllerCompat getInstance();
+ method public abstract androidx.webkit.ServiceWorkerWebSettingsCompat getServiceWorkerWebSettings();
+ method public abstract void setServiceWorkerClient(androidx.webkit.ServiceWorkerClientCompat?);
+ }
+
+ @AnyThread public abstract class ServiceWorkerWebSettingsCompat {
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_CONTENT_ACCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract boolean getAllowContentAccess();
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_FILE_ACCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract boolean getAllowFileAccess();
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_BLOCK_NETWORK_LOADS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract boolean getBlockNetworkLoads();
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_CACHE_MODE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract int getCacheMode();
+ method @RequiresFeature(name="REQUESTED_WITH_HEADER_ALLOW_LIST", enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract java.util.Set<java.lang.String!> getRequestedWithHeaderOriginAllowList();
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_CONTENT_ACCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setAllowContentAccess(boolean);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_FILE_ACCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setAllowFileAccess(boolean);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_BLOCK_NETWORK_LOADS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setBlockNetworkLoads(boolean);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_CACHE_MODE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setCacheMode(int);
+ method @RequiresFeature(name="REQUESTED_WITH_HEADER_ALLOW_LIST", enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setRequestedWithHeaderOriginAllowList(java.util.Set<java.lang.String!>);
+ }
+
+ @SuppressCompatibility @androidx.webkit.Profile.ExperimentalUrlPrefetch public class SpeculativeLoadingConfig {
+ method @IntRange(from=1, to=androidx.webkit.SpeculativeLoadingConfig.ABSOLUTE_MAX_PREFETCHES) public int getMaxPrefetches();
+ method @IntRange(from=1, to=java.lang.Integer.MAX_VALUE) public int getPrefetchTtlSeconds();
+ field public static final int ABSOLUTE_MAX_PREFETCHES = 20; // 0x14
+ field public static final int DEFAULT_MAX_PREFETCHES = 10; // 0xa
+ field public static final int DEFAULT_TTL_SECS = 60; // 0x3c
+ }
+
+ @SuppressCompatibility @androidx.webkit.Profile.ExperimentalUrlPrefetch public static final class SpeculativeLoadingConfig.Builder {
+ ctor public SpeculativeLoadingConfig.Builder();
+ method @SuppressCompatibility @androidx.webkit.Profile.ExperimentalUrlPrefetch public androidx.webkit.SpeculativeLoadingConfig build();
+ method public androidx.webkit.SpeculativeLoadingConfig.Builder setMaxPrefetches(@IntRange(from=1, to=androidx.webkit.SpeculativeLoadingConfig.ABSOLUTE_MAX_PREFETCHES) int);
+ method public androidx.webkit.SpeculativeLoadingConfig.Builder setPrefetchTtlSeconds(@IntRange(from=1, to=java.lang.Integer.MAX_VALUE) int);
+ }
+
+ @SuppressCompatibility @RequiresFeature(name=androidx.webkit.WebViewFeature.PROFILE_URL_PREFETCH, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @androidx.webkit.Profile.ExperimentalUrlPrefetch public final class SpeculativeLoadingParameters {
+ method public java.util.Map<java.lang.String!,java.lang.String!> getAdditionalHeaders();
+ method public androidx.webkit.NoVarySearchHeader? getExpectedNoVarySearchData();
+ method public boolean isJavaScriptEnabled();
+ }
+
+ public static final class SpeculativeLoadingParameters.Builder {
+ ctor public SpeculativeLoadingParameters.Builder();
+ method @SuppressCompatibility @androidx.webkit.Profile.ExperimentalUrlPrefetch public androidx.webkit.SpeculativeLoadingParameters.Builder addAdditionalHeader(String, String);
+ method @SuppressCompatibility @androidx.webkit.Profile.ExperimentalUrlPrefetch public androidx.webkit.SpeculativeLoadingParameters.Builder addAdditionalHeaders(java.util.Map<java.lang.String!,java.lang.String!>);
+ method @SuppressCompatibility @RequiresFeature(name=androidx.webkit.WebViewFeature.PROFILE_URL_PREFETCH, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @androidx.webkit.Profile.ExperimentalUrlPrefetch public androidx.webkit.SpeculativeLoadingParameters build();
+ method @SuppressCompatibility @androidx.webkit.Profile.ExperimentalUrlPrefetch public androidx.webkit.SpeculativeLoadingParameters.Builder setExpectedNoVarySearchData(androidx.webkit.NoVarySearchHeader);
+ method @SuppressCompatibility @androidx.webkit.Profile.ExperimentalUrlPrefetch public androidx.webkit.SpeculativeLoadingParameters.Builder setJavaScriptEnabled(boolean);
+ }
+
+ public class TracingConfig {
+ method public java.util.List<java.lang.String!> getCustomIncludedCategories();
+ method public int getPredefinedCategories();
+ method public int getTracingMode();
+ field public static final int CATEGORIES_ALL = 1; // 0x1
+ field public static final int CATEGORIES_ANDROID_WEBVIEW = 2; // 0x2
+ field public static final int CATEGORIES_FRAME_VIEWER = 64; // 0x40
+ field public static final int CATEGORIES_INPUT_LATENCY = 8; // 0x8
+ field public static final int CATEGORIES_JAVASCRIPT_AND_RENDERING = 32; // 0x20
+ field public static final int CATEGORIES_NONE = 0; // 0x0
+ field public static final int CATEGORIES_RENDERING = 16; // 0x10
+ field public static final int CATEGORIES_WEB_DEVELOPER = 4; // 0x4
+ field public static final int RECORD_CONTINUOUSLY = 1; // 0x1
+ field public static final int RECORD_UNTIL_FULL = 0; // 0x0
+ }
+
+ public static class TracingConfig.Builder {
+ ctor public TracingConfig.Builder();
+ method public androidx.webkit.TracingConfig.Builder addCategories(int...);
+ method public androidx.webkit.TracingConfig.Builder addCategories(java.lang.String!...);
+ method public androidx.webkit.TracingConfig.Builder addCategories(java.util.Collection<java.lang.String!>);
+ method public androidx.webkit.TracingConfig build();
+ method public androidx.webkit.TracingConfig.Builder setTracingMode(int);
+ }
+
+ @AnyThread public abstract class TracingController {
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.TRACING_CONTROLLER_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.TracingController getInstance();
+ method public abstract boolean isTracing();
+ method public abstract void start(androidx.webkit.TracingConfig);
+ method public abstract boolean stop(java.io.OutputStream?, java.util.concurrent.Executor);
+ }
+
+ public final class URLUtilCompat {
+ method public static String? getFilenameFromContentDisposition(String);
+ method public static String guessFileName(String, String?, String?);
+ }
+
+ public final class UserAgentMetadata {
+ method public String? getArchitecture();
+ method public int getBitness();
+ method public java.util.List<androidx.webkit.UserAgentMetadata.BrandVersion!> getBrandVersionList();
+ method public String? getFullVersion();
+ method public String? getModel();
+ method public String? getPlatform();
+ method public String? getPlatformVersion();
+ method public boolean isMobile();
+ method public boolean isWow64();
+ field public static final int BITNESS_DEFAULT = 0; // 0x0
+ }
+
+ public static final class UserAgentMetadata.BrandVersion {
+ method public String getBrand();
+ method public String getFullVersion();
+ method public String getMajorVersion();
+ }
+
+ public static final class UserAgentMetadata.BrandVersion.Builder {
+ ctor public UserAgentMetadata.BrandVersion.Builder();
+ ctor public UserAgentMetadata.BrandVersion.Builder(androidx.webkit.UserAgentMetadata.BrandVersion);
+ method public androidx.webkit.UserAgentMetadata.BrandVersion build();
+ method public androidx.webkit.UserAgentMetadata.BrandVersion.Builder setBrand(String);
+ method public androidx.webkit.UserAgentMetadata.BrandVersion.Builder setFullVersion(String);
+ method public androidx.webkit.UserAgentMetadata.BrandVersion.Builder setMajorVersion(String);
+ }
+
+ public static final class UserAgentMetadata.Builder {
+ ctor public UserAgentMetadata.Builder();
+ ctor public UserAgentMetadata.Builder(androidx.webkit.UserAgentMetadata);
+ method public androidx.webkit.UserAgentMetadata build();
+ method public androidx.webkit.UserAgentMetadata.Builder setArchitecture(String?);
+ method public androidx.webkit.UserAgentMetadata.Builder setBitness(int);
+ method public androidx.webkit.UserAgentMetadata.Builder setBrandVersionList(java.util.List<androidx.webkit.UserAgentMetadata.BrandVersion!>);
+ method public androidx.webkit.UserAgentMetadata.Builder setFullVersion(String?);
+ method public androidx.webkit.UserAgentMetadata.Builder setMobile(boolean);
+ method public androidx.webkit.UserAgentMetadata.Builder setModel(String?);
+ method public androidx.webkit.UserAgentMetadata.Builder setPlatform(String?);
+ method public androidx.webkit.UserAgentMetadata.Builder setPlatformVersion(String?);
+ method public androidx.webkit.UserAgentMetadata.Builder setWow64(boolean);
+ }
+
+ public class WebMessageCompat {
+ ctor @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public WebMessageCompat(byte[]);
+ ctor @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public WebMessageCompat(byte[], androidx.webkit.WebMessagePortCompat![]?);
+ ctor public WebMessageCompat(String?);
+ ctor public WebMessageCompat(String?, androidx.webkit.WebMessagePortCompat![]?);
+ method public byte[] getArrayBuffer();
+ method public String? getData();
+ method public androidx.webkit.WebMessagePortCompat![]? getPorts();
+ method public int getType();
+ field public static final int TYPE_ARRAY_BUFFER = 1; // 0x1
+ field public static final int TYPE_STRING = 0; // 0x0
+ }
+
+ @AnyThread public abstract class WebMessagePortCompat {
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_PORT_CLOSE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void close();
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_PORT_POST_MESSAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void postMessage(androidx.webkit.WebMessageCompat);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setWebMessageCallback(android.os.Handler?, androidx.webkit.WebMessagePortCompat.WebMessageCallbackCompat);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setWebMessageCallback(androidx.webkit.WebMessagePortCompat.WebMessageCallbackCompat);
+ }
+
+ public abstract static class WebMessagePortCompat.WebMessageCallbackCompat {
+ ctor public WebMessagePortCompat.WebMessageCallbackCompat();
+ method public void onMessage(androidx.webkit.WebMessagePortCompat, androidx.webkit.WebMessageCompat?);
+ }
+
+ public abstract class WebResourceErrorCompat {
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_RESOURCE_ERROR_GET_DESCRIPTION, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract CharSequence getDescription();
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_RESOURCE_ERROR_GET_CODE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract int getErrorCode();
+ }
+
+ public class WebResourceRequestCompat {
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_RESOURCE_REQUEST_IS_REDIRECT, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean isRedirect(android.webkit.WebResourceRequest);
+ }
+
+ public class WebSettingsCompat {
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.ATTRIBUTION_REGISTRATION_BEHAVIOR, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static int getAttributionRegistrationBehavior(android.webkit.WebSettings);
+ method @SuppressCompatibility @RequiresFeature(name=androidx.webkit.WebViewFeature.BACK_FORWARD_CACHE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @androidx.webkit.WebSettingsCompat.ExperimentalBackForwardCache public static boolean getBackForwardCacheEnabled(android.webkit.WebSettings);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.DISABLED_ACTION_MODE_MENU_ITEMS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static int getDisabledActionModeMenuItems(android.webkit.WebSettings);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean getEnterpriseAuthenticationAppLinkPolicyEnabled(android.webkit.WebSettings);
+ method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.FORCE_DARK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static int getForceDark(android.webkit.WebSettings);
+ method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.FORCE_DARK_STRATEGY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static int getForceDarkStrategy(android.webkit.WebSettings);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.OFF_SCREEN_PRERASTER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean getOffscreenPreRaster(android.webkit.WebSettings);
+ method @RequiresFeature(name="REQUESTED_WITH_HEADER_ALLOW_LIST", enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static java.util.Set<java.lang.String!> getRequestedWithHeaderOriginAllowList(android.webkit.WebSettings);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_ENABLE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean getSafeBrowsingEnabled(android.webkit.WebSettings);
+ method @SuppressCompatibility @RequiresFeature(name=androidx.webkit.WebViewFeature.SPECULATIVE_LOADING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @androidx.webkit.WebSettingsCompat.ExperimentalSpeculativeLoading public static int getSpeculativeLoadingStatus(android.webkit.WebSettings);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.USER_AGENT_METADATA, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.UserAgentMetadata getUserAgentMetadata(android.webkit.WebSettings);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_AUTHENTICATION, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static int getWebAuthenticationSupport(android.webkit.WebSettings);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEBVIEW_MEDIA_INTEGRITY_API_STATUS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.WebViewMediaIntegrityApiStatusConfig getWebViewMediaIntegrityApiStatus(android.webkit.WebSettings);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.ALGORITHMIC_DARKENING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean isAlgorithmicDarkeningAllowed(android.webkit.WebSettings);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.ALGORITHMIC_DARKENING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setAlgorithmicDarkeningAllowed(android.webkit.WebSettings, boolean);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.ATTRIBUTION_REGISTRATION_BEHAVIOR, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setAttributionRegistrationBehavior(android.webkit.WebSettings, int);
+ method @SuppressCompatibility @RequiresFeature(name=androidx.webkit.WebViewFeature.BACK_FORWARD_CACHE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @androidx.webkit.WebSettingsCompat.ExperimentalBackForwardCache public static void setBackForwardCacheEnabled(android.webkit.WebSettings, boolean);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.DISABLED_ACTION_MODE_MENU_ITEMS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setDisabledActionModeMenuItems(android.webkit.WebSettings, int);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setEnterpriseAuthenticationAppLinkPolicyEnabled(android.webkit.WebSettings, boolean);
+ method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.FORCE_DARK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setForceDark(android.webkit.WebSettings, int);
+ method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.FORCE_DARK_STRATEGY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setForceDarkStrategy(android.webkit.WebSettings, int);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.OFF_SCREEN_PRERASTER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setOffscreenPreRaster(android.webkit.WebSettings, boolean);
+ method @RequiresFeature(name="REQUESTED_WITH_HEADER_ALLOW_LIST", enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setRequestedWithHeaderOriginAllowList(android.webkit.WebSettings, java.util.Set<java.lang.String!>);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_ENABLE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setSafeBrowsingEnabled(android.webkit.WebSettings, boolean);
+ method @SuppressCompatibility @RequiresFeature(name=androidx.webkit.WebViewFeature.SPECULATIVE_LOADING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @androidx.webkit.WebSettingsCompat.ExperimentalSpeculativeLoading public static void setSpeculativeLoadingStatus(android.webkit.WebSettings, @SuppressCompatibility int);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.USER_AGENT_METADATA, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setUserAgentMetadata(android.webkit.WebSettings, androidx.webkit.UserAgentMetadata);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_AUTHENTICATION, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static void setWebAuthenticationSupport(android.webkit.WebSettings, int);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEBVIEW_MEDIA_INTEGRITY_API_STATUS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setWebViewMediaIntegrityApiStatus(android.webkit.WebSettings, androidx.webkit.WebViewMediaIntegrityApiStatusConfig);
+ field public static final int ATTRIBUTION_BEHAVIOR_APP_SOURCE_AND_APP_TRIGGER = 3; // 0x3
+ field public static final int ATTRIBUTION_BEHAVIOR_APP_SOURCE_AND_WEB_TRIGGER = 1; // 0x1
+ field public static final int ATTRIBUTION_BEHAVIOR_DISABLED = 0; // 0x0
+ field public static final int ATTRIBUTION_BEHAVIOR_WEB_SOURCE_AND_WEB_TRIGGER = 2; // 0x2
+ field @Deprecated public static final int DARK_STRATEGY_PREFER_WEB_THEME_OVER_USER_AGENT_DARKENING = 2; // 0x2
+ field @Deprecated public static final int DARK_STRATEGY_USER_AGENT_DARKENING_ONLY = 0; // 0x0
+ field @Deprecated public static final int DARK_STRATEGY_WEB_THEME_DARKENING_ONLY = 1; // 0x1
+ field @Deprecated public static final int FORCE_DARK_AUTO = 1; // 0x1
+ field @Deprecated public static final int FORCE_DARK_OFF = 0; // 0x0
+ field @Deprecated public static final int FORCE_DARK_ON = 2; // 0x2
+ field @SuppressCompatibility @androidx.webkit.WebSettingsCompat.ExperimentalSpeculativeLoading public static final int SPECULATIVE_LOADING_DISABLED = 0; // 0x0
+ field @SuppressCompatibility @androidx.webkit.WebSettingsCompat.ExperimentalSpeculativeLoading public static final int SPECULATIVE_LOADING_PRERENDER_ENABLED = 1; // 0x1
+ field public static final int WEB_AUTHENTICATION_SUPPORT_FOR_APP = 1; // 0x1
+ field public static final int WEB_AUTHENTICATION_SUPPORT_FOR_BROWSER = 2; // 0x2
+ field public static final int WEB_AUTHENTICATION_SUPPORT_NONE = 0; // 0x0
+ }
+
+ @SuppressCompatibility @RequiresOptIn(level=androidx.annotation.RequiresOptIn.Level.ERROR) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.TYPE}) public static @interface WebSettingsCompat.ExperimentalBackForwardCache {
+ }
+
+ @SuppressCompatibility @RequiresOptIn(level=androidx.annotation.RequiresOptIn.Level.ERROR) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.TYPE}) public static @interface WebSettingsCompat.ExperimentalSpeculativeLoading {
+ }
+
+ public final class WebStorageCompat {
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.DELETE_BROWSING_DATA, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static void deleteBrowsingData(android.webkit.WebStorage, Runnable);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.DELETE_BROWSING_DATA, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static void deleteBrowsingData(android.webkit.WebStorage, java.util.concurrent.Executor, Runnable);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.DELETE_BROWSING_DATA, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static String deleteBrowsingDataForSite(android.webkit.WebStorage, String, Runnable);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.DELETE_BROWSING_DATA, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static String deleteBrowsingDataForSite(android.webkit.WebStorage, String, java.util.concurrent.Executor, Runnable);
+ }
+
+ public final class WebViewAssetLoader {
+ method @WorkerThread public android.webkit.WebResourceResponse? shouldInterceptRequest(android.net.Uri);
+ field public static final String DEFAULT_DOMAIN = "appassets.androidplatform.net";
+ }
+
+ public static final class WebViewAssetLoader.AssetsPathHandler implements androidx.webkit.WebViewAssetLoader.PathHandler {
+ ctor public WebViewAssetLoader.AssetsPathHandler(android.content.Context);
+ method @WorkerThread public android.webkit.WebResourceResponse? handle(String);
+ }
+
+ public static final class WebViewAssetLoader.Builder {
+ ctor public WebViewAssetLoader.Builder();
+ method public androidx.webkit.WebViewAssetLoader.Builder addPathHandler(String, androidx.webkit.WebViewAssetLoader.PathHandler);
+ method public androidx.webkit.WebViewAssetLoader build();
+ method public androidx.webkit.WebViewAssetLoader.Builder setDomain(String);
+ method public androidx.webkit.WebViewAssetLoader.Builder setHttpAllowed(boolean);
+ }
+
+ public static final class WebViewAssetLoader.InternalStoragePathHandler implements androidx.webkit.WebViewAssetLoader.PathHandler {
+ ctor public WebViewAssetLoader.InternalStoragePathHandler(android.content.Context, java.io.File);
+ method @WorkerThread public android.webkit.WebResourceResponse handle(String);
+ }
+
+ public static interface WebViewAssetLoader.PathHandler {
+ method @WorkerThread public android.webkit.WebResourceResponse? handle(String);
+ }
+
+ public static final class WebViewAssetLoader.ResourcesPathHandler implements androidx.webkit.WebViewAssetLoader.PathHandler {
+ ctor public WebViewAssetLoader.ResourcesPathHandler(android.content.Context);
+ method @WorkerThread public android.webkit.WebResourceResponse? handle(String);
+ }
+
+ public class WebViewClientCompat extends android.webkit.WebViewClient {
+ ctor public WebViewClientCompat();
+ method @RequiresApi(android.os.Build.VERSION_CODES.M) public final void onReceivedError(android.webkit.WebView, android.webkit.WebResourceRequest, android.webkit.WebResourceError);
+ method @UiThread public void onReceivedError(android.webkit.WebView, android.webkit.WebResourceRequest, androidx.webkit.WebResourceErrorCompat);
+ method @RequiresApi(android.os.Build.VERSION_CODES.O_MR1) public final void onSafeBrowsingHit(android.webkit.WebView, android.webkit.WebResourceRequest, int, android.webkit.SafeBrowsingResponse);
+ method @UiThread public void onSafeBrowsingHit(android.webkit.WebView, android.webkit.WebResourceRequest, int, androidx.webkit.SafeBrowsingResponseCompat);
+ }
+
+ public class WebViewCompat {
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.DOCUMENT_START_SCRIPT, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static androidx.webkit.ScriptHandler addDocumentStartJavaScript(android.webkit.WebView, String, java.util.Set<java.lang.String!>);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_LISTENER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static void addWebMessageListener(android.webkit.WebView, String, java.util.Set<java.lang.String!>, androidx.webkit.WebViewCompat.WebMessageListener);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.CREATE_WEB_MESSAGE_CHANNEL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static androidx.webkit.WebMessagePortCompat![] createWebMessageChannel(android.webkit.WebView);
+ method @AnyThread public static android.content.pm.PackageInfo? getCurrentWebViewPackage(android.content.Context);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static androidx.webkit.Profile getProfile(android.webkit.WebView);
+ method @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_PRIVACY_POLICY_URL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static android.net.Uri getSafeBrowsingPrivacyPolicyUrl();
+ method @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_VARIATIONS_HEADER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static String getVariationsHeader();
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_WEB_CHROME_CLIENT, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static android.webkit.WebChromeClient? getWebChromeClient(android.webkit.WebView);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_WEB_VIEW_CLIENT, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static android.webkit.WebViewClient getWebViewClient(android.webkit.WebView);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_WEB_VIEW_RENDERER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static androidx.webkit.WebViewRenderProcess? getWebViewRenderProcess(android.webkit.WebView);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static androidx.webkit.WebViewRenderProcessClient? getWebViewRenderProcessClient(android.webkit.WebView);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.MUTE_AUDIO, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static boolean isAudioMuted(android.webkit.WebView);
+ method @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean isMultiProcessEnabled();
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.VISUAL_STATE_CALLBACK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static void postVisualStateCallback(android.webkit.WebView, long, androidx.webkit.WebViewCompat.VisualStateCallback);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.POST_WEB_MESSAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static void postWebMessage(android.webkit.WebView, androidx.webkit.WebMessageCompat, android.net.Uri);
+ method @SuppressCompatibility @RequiresFeature(name=androidx.webkit.WebViewFeature.PRERENDER_WITH_URL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread @androidx.webkit.WebViewCompat.ExperimentalUrlPrerender public static void prerenderUrl(android.webkit.WebView, String, android.os.CancellationSignal?, java.util.concurrent.Executor, androidx.webkit.PrerenderOperationCallback);
+ method @SuppressCompatibility @RequiresFeature(name=androidx.webkit.WebViewFeature.PRERENDER_WITH_URL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread @androidx.webkit.WebViewCompat.ExperimentalUrlPrerender public static void prerenderUrl(android.webkit.WebView, String, android.os.CancellationSignal?, java.util.concurrent.Executor, androidx.webkit.SpeculativeLoadingParameters, androidx.webkit.PrerenderOperationCallback);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_LISTENER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static void removeWebMessageListener(android.webkit.WebView, String);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.MUTE_AUDIO, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static void setAudioMuted(android.webkit.WebView, boolean);
+ method @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.DEFAULT_TRAFFICSTATS_TAGGING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setDefaultTrafficStatsTag(int);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static void setProfile(android.webkit.WebView, String);
+ method @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_ALLOWLIST, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setSafeBrowsingAllowlist(java.util.Set<java.lang.String!>, android.webkit.ValueCallback<java.lang.Boolean!>?);
+ method @Deprecated @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_WHITELIST, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setSafeBrowsingWhitelist(java.util.List<java.lang.String!>, android.webkit.ValueCallback<java.lang.Boolean!>?);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static void setWebViewRenderProcessClient(android.webkit.WebView, androidx.webkit.WebViewRenderProcessClient?);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static void setWebViewRenderProcessClient(android.webkit.WebView, java.util.concurrent.Executor, androidx.webkit.WebViewRenderProcessClient);
+ method @Deprecated @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.START_SAFE_BROWSING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void startSafeBrowsing(android.content.Context, android.webkit.ValueCallback<java.lang.Boolean!>?);
+ method @SuppressCompatibility @AnyThread @androidx.webkit.WebViewCompat.ExperimentalAsyncStartUp public static void startUpWebView(androidx.webkit.WebViewStartUpConfig, androidx.webkit.WebViewCompat.WebViewStartUpCallback);
+ }
+
+ @SuppressCompatibility @RequiresOptIn(level=androidx.annotation.RequiresOptIn.Level.ERROR) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.FIELD}) public static @interface WebViewCompat.ExperimentalAsyncStartUp {
+ }
+
+ @SuppressCompatibility @RequiresOptIn(level=androidx.annotation.RequiresOptIn.Level.ERROR) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.FIELD}) public static @interface WebViewCompat.ExperimentalUrlPrerender {
+ }
+
+ public static interface WebViewCompat.VisualStateCallback {
+ method @UiThread public void onComplete(long);
+ }
+
+ public static interface WebViewCompat.WebMessageListener {
+ method @UiThread public void onPostMessage(android.webkit.WebView, androidx.webkit.WebMessageCompat, android.net.Uri, boolean, androidx.webkit.JavaScriptReplyProxy);
+ }
+
+ @SuppressCompatibility @androidx.webkit.WebViewCompat.ExperimentalAsyncStartUp public static interface WebViewCompat.WebViewStartUpCallback {
+ method public void onSuccess(androidx.webkit.WebViewStartUpResult);
+ }
+
+ public class WebViewFeature {
+ method public static boolean isFeatureSupported(String);
+ method public static boolean isStartupFeatureSupported(android.content.Context, String);
+ field public static final String ALGORITHMIC_DARKENING = "ALGORITHMIC_DARKENING";
+ field public static final String ATTRIBUTION_REGISTRATION_BEHAVIOR = "ATTRIBUTION_REGISTRATION_BEHAVIOR";
+ field public static final String BACK_FORWARD_CACHE = "BACK_FORWARD_CACHE";
+ field public static final String CREATE_WEB_MESSAGE_CHANNEL = "CREATE_WEB_MESSAGE_CHANNEL";
+ field public static final String DEFAULT_TRAFFICSTATS_TAGGING = "DEFAULT_TRAFFICSTATS_TAGGING";
+ field public static final String DELETE_BROWSING_DATA = "DELETE_BROWSING_DATA";
+ field public static final String DISABLED_ACTION_MODE_MENU_ITEMS = "DISABLED_ACTION_MODE_MENU_ITEMS";
+ field public static final String DOCUMENT_START_SCRIPT = "DOCUMENT_START_SCRIPT";
+ field public static final String ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY = "ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY";
+ field public static final String FORCE_DARK = "FORCE_DARK";
+ field public static final String FORCE_DARK_STRATEGY = "FORCE_DARK_STRATEGY";
+ field public static final String GET_COOKIE_INFO = "GET_COOKIE_INFO";
+ field public static final String GET_VARIATIONS_HEADER = "GET_VARIATIONS_HEADER";
+ field public static final String GET_WEB_CHROME_CLIENT = "GET_WEB_CHROME_CLIENT";
+ field public static final String GET_WEB_VIEW_CLIENT = "GET_WEB_VIEW_CLIENT";
+ field public static final String GET_WEB_VIEW_RENDERER = "GET_WEB_VIEW_RENDERER";
+ field public static final String MULTI_PROCESS = "MULTI_PROCESS";
+ field public static final String MULTI_PROFILE = "MULTI_PROFILE";
+ field public static final String MUTE_AUDIO = "MUTE_AUDIO";
+ field public static final String OFF_SCREEN_PRERASTER = "OFF_SCREEN_PRERASTER";
+ field public static final String POST_WEB_MESSAGE = "POST_WEB_MESSAGE";
+ field @SuppressCompatibility @androidx.webkit.WebViewCompat.ExperimentalUrlPrerender public static final String PRERENDER_WITH_URL = "PRERENDER_URL_V2";
+ field @SuppressCompatibility @androidx.webkit.Profile.ExperimentalUrlPrefetch public static final String PROFILE_URL_PREFETCH = "PREFETCH_URL_V3";
+ field public static final String PROXY_OVERRIDE = "PROXY_OVERRIDE";
+ field public static final String PROXY_OVERRIDE_REVERSE_BYPASS = "PROXY_OVERRIDE_REVERSE_BYPASS";
+ field public static final String RECEIVE_HTTP_ERROR = "RECEIVE_HTTP_ERROR";
+ field public static final String RECEIVE_WEB_RESOURCE_ERROR = "RECEIVE_WEB_RESOURCE_ERROR";
+ field public static final String SAFE_BROWSING_ALLOWLIST = "SAFE_BROWSING_ALLOWLIST";
+ field public static final String SAFE_BROWSING_ENABLE = "SAFE_BROWSING_ENABLE";
+ field public static final String SAFE_BROWSING_HIT = "SAFE_BROWSING_HIT";
+ field public static final String SAFE_BROWSING_PRIVACY_POLICY_URL = "SAFE_BROWSING_PRIVACY_POLICY_URL";
+ field public static final String SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY = "SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY";
+ field public static final String SAFE_BROWSING_RESPONSE_PROCEED = "SAFE_BROWSING_RESPONSE_PROCEED";
+ field public static final String SAFE_BROWSING_RESPONSE_SHOW_INTERSTITIAL = "SAFE_BROWSING_RESPONSE_SHOW_INTERSTITIAL";
+ field @Deprecated public static final String SAFE_BROWSING_WHITELIST = "SAFE_BROWSING_WHITELIST";
+ field public static final String SERVICE_WORKER_BASIC_USAGE = "SERVICE_WORKER_BASIC_USAGE";
+ field public static final String SERVICE_WORKER_BLOCK_NETWORK_LOADS = "SERVICE_WORKER_BLOCK_NETWORK_LOADS";
+ field public static final String SERVICE_WORKER_CACHE_MODE = "SERVICE_WORKER_CACHE_MODE";
+ field public static final String SERVICE_WORKER_CONTENT_ACCESS = "SERVICE_WORKER_CONTENT_ACCESS";
+ field public static final String SERVICE_WORKER_FILE_ACCESS = "SERVICE_WORKER_FILE_ACCESS";
+ field public static final String SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST = "SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST";
+ field public static final String SHOULD_OVERRIDE_WITH_REDIRECTS = "SHOULD_OVERRIDE_WITH_REDIRECTS";
+ field public static final String SPECULATIVE_LOADING = "SPECULATIVE_LOADING_STATUS";
+ field @SuppressCompatibility @androidx.webkit.Profile.ExperimentalUrlPrefetch public static final String SPECULATIVE_LOADING_CONFIG = "SPECULATIVE_LOADING_CONFIG";
+ field public static final String STARTUP_FEATURE_CONFIGURE_PARTITIONED_COOKIES = "STARTUP_FEATURE_CONFIGURE_PARTITIONED_COOKIES";
+ field public static final String STARTUP_FEATURE_SET_DATA_DIRECTORY_SUFFIX = "STARTUP_FEATURE_SET_DATA_DIRECTORY_SUFFIX";
+ field public static final String STARTUP_FEATURE_SET_DIRECTORY_BASE_PATHS = "STARTUP_FEATURE_SET_DIRECTORY_BASE_PATHS";
+ field public static final String START_SAFE_BROWSING = "START_SAFE_BROWSING";
+ field public static final String TRACING_CONTROLLER_BASIC_USAGE = "TRACING_CONTROLLER_BASIC_USAGE";
+ field public static final String USER_AGENT_METADATA = "USER_AGENT_METADATA";
+ field public static final String VISUAL_STATE_CALLBACK = "VISUAL_STATE_CALLBACK";
+ field public static final String WEBVIEW_MEDIA_INTEGRITY_API_STATUS = "WEBVIEW_MEDIA_INTEGRITY_API_STATUS";
+ field public static final String WEB_AUTHENTICATION = "WEB_AUTHENTICATION";
+ field public static final String WEB_MESSAGE_ARRAY_BUFFER = "WEB_MESSAGE_ARRAY_BUFFER";
+ field public static final String WEB_MESSAGE_CALLBACK_ON_MESSAGE = "WEB_MESSAGE_CALLBACK_ON_MESSAGE";
+ field public static final String WEB_MESSAGE_LISTENER = "WEB_MESSAGE_LISTENER";
+ field public static final String WEB_MESSAGE_PORT_CLOSE = "WEB_MESSAGE_PORT_CLOSE";
+ field public static final String WEB_MESSAGE_PORT_POST_MESSAGE = "WEB_MESSAGE_PORT_POST_MESSAGE";
+ field public static final String WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK = "WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK";
+ field public static final String WEB_RESOURCE_ERROR_GET_CODE = "WEB_RESOURCE_ERROR_GET_CODE";
+ field public static final String WEB_RESOURCE_ERROR_GET_DESCRIPTION = "WEB_RESOURCE_ERROR_GET_DESCRIPTION";
+ field public static final String WEB_RESOURCE_REQUEST_IS_REDIRECT = "WEB_RESOURCE_REQUEST_IS_REDIRECT";
+ field public static final String WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE = "WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE";
+ field public static final String WEB_VIEW_RENDERER_TERMINATE = "WEB_VIEW_RENDERER_TERMINATE";
+ }
+
+ @RequiresFeature(name=androidx.webkit.WebViewFeature.WEBVIEW_MEDIA_INTEGRITY_API_STATUS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public class WebViewMediaIntegrityApiStatusConfig {
+ ctor public WebViewMediaIntegrityApiStatusConfig(androidx.webkit.WebViewMediaIntegrityApiStatusConfig.Builder);
+ method public int getDefaultStatus();
+ method public java.util.Map<java.lang.String!,java.lang.Integer!> getOverrideRules();
+ field public static final int WEBVIEW_MEDIA_INTEGRITY_API_DISABLED = 0; // 0x0
+ field public static final int WEBVIEW_MEDIA_INTEGRITY_API_ENABLED = 2; // 0x2
+ field public static final int WEBVIEW_MEDIA_INTEGRITY_API_ENABLED_WITHOUT_APP_IDENTITY = 1; // 0x1
+ }
+
+ public static final class WebViewMediaIntegrityApiStatusConfig.Builder {
+ ctor public WebViewMediaIntegrityApiStatusConfig.Builder(int);
+ method public androidx.webkit.WebViewMediaIntegrityApiStatusConfig.Builder addOverrideRule(String, int);
+ method public androidx.webkit.WebViewMediaIntegrityApiStatusConfig build();
+ }
+
+ public abstract class WebViewRenderProcess {
+ ctor public WebViewRenderProcess();
+ method public abstract boolean terminate();
+ }
+
+ public abstract class WebViewRenderProcessClient {
+ ctor public WebViewRenderProcessClient();
+ method public abstract void onRenderProcessResponsive(android.webkit.WebView, androidx.webkit.WebViewRenderProcess?);
+ method public abstract void onRenderProcessUnresponsive(android.webkit.WebView, androidx.webkit.WebViewRenderProcess?);
+ }
+
+ @SuppressCompatibility @androidx.webkit.WebViewCompat.ExperimentalAsyncStartUp public final class WebViewStartUpConfig {
+ method public java.util.concurrent.Executor getBackgroundExecutor();
+ method public boolean shouldRunUiThreadStartUpTasks();
+ }
+
+ @SuppressCompatibility @androidx.webkit.WebViewCompat.ExperimentalAsyncStartUp public static final class WebViewStartUpConfig.Builder {
+ ctor public WebViewStartUpConfig.Builder(java.util.concurrent.Executor);
+ method public androidx.webkit.WebViewStartUpConfig build();
+ method public androidx.webkit.WebViewStartUpConfig.Builder setShouldRunUiThreadStartUpTasks(boolean);
+ }
+
+ @SuppressCompatibility @androidx.webkit.WebViewCompat.ExperimentalAsyncStartUp public interface WebViewStartUpResult {
+ method public java.util.List<androidx.webkit.BlockingStartUpLocation!>? getBlockingStartUpLocations();
+ method public Long? getMaxTimePerTaskInUiThreadMillis();
+ method public Long? getTotalTimeInUiThreadMillis();
+ }
+
+}
+
diff --git a/webkit/webkit/api/res-1.13.0-beta01.txt b/webkit/webkit/api/res-1.13.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/webkit/webkit/api/res-1.13.0-beta01.txt
diff --git a/webkit/webkit/api/restricted_1.13.0-beta01.txt b/webkit/webkit/api/restricted_1.13.0-beta01.txt
new file mode 100644
index 0000000..4a0366f
--- /dev/null
+++ b/webkit/webkit/api/restricted_1.13.0-beta01.txt
@@ -0,0 +1,570 @@
+// Signature format: 4.0
+package androidx.webkit {
+
+ @SuppressCompatibility @androidx.webkit.WebViewCompat.ExperimentalAsyncStartUp public interface BlockingStartUpLocation {
+ method public String getStackInformation();
+ }
+
+ @AnyThread public class CookieManagerCompat {
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_COOKIE_INFO, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static java.util.List<java.lang.String!> getCookieInfo(android.webkit.CookieManager, String);
+ }
+
+ public final class DropDataContentProvider extends android.content.ContentProvider {
+ ctor public DropDataContentProvider();
+ method public int delete(android.net.Uri, String?, String![]?);
+ method public String? getType(android.net.Uri);
+ method public android.net.Uri? insert(android.net.Uri, android.content.ContentValues?);
+ method public boolean onCreate();
+ method public android.database.Cursor? query(android.net.Uri, String![]?, String?, String![]?, String?);
+ method public int update(android.net.Uri, android.content.ContentValues?, String?, String![]?);
+ }
+
+ @UiThread public abstract class JavaScriptReplyProxy {
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void postMessage(byte[]);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_LISTENER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void postMessage(String);
+ }
+
+ @SuppressCompatibility @androidx.webkit.Profile.ExperimentalUrlPrefetch public class NoVarySearchHeader {
+ method @SuppressCompatibility @androidx.webkit.Profile.ExperimentalUrlPrefetch public static androidx.webkit.NoVarySearchHeader alwaysVaryData();
+ method @SuppressCompatibility @androidx.webkit.Profile.ExperimentalUrlPrefetch public static androidx.webkit.NoVarySearchHeader neverVaryData();
+ method @SuppressCompatibility @androidx.webkit.Profile.ExperimentalUrlPrefetch public static androidx.webkit.NoVarySearchHeader neverVaryExcept(boolean, java.util.List<java.lang.String!>);
+ method @SuppressCompatibility @androidx.webkit.Profile.ExperimentalUrlPrefetch public static androidx.webkit.NoVarySearchHeader varyExcept(boolean, java.util.List<java.lang.String!>);
+ field public final java.util.List<java.lang.String!> consideredQueryParameters;
+ field public final boolean ignoreDifferencesInParameters;
+ field public final java.util.List<java.lang.String!> ignoredQueryParameters;
+ field public final boolean varyOnKeyOrder;
+ }
+
+ @SuppressCompatibility @androidx.webkit.Profile.ExperimentalUrlPrefetch public interface OutcomeReceiverCompat<T, E extends java.lang.Throwable> {
+ method public default void onError(E);
+ method public void onResult(T!);
+ }
+
+ @SuppressCompatibility @androidx.webkit.Profile.ExperimentalUrlPrefetch public class PrefetchException extends java.lang.Exception {
+ ctor public PrefetchException();
+ ctor public PrefetchException(String);
+ }
+
+ @SuppressCompatibility @androidx.webkit.Profile.ExperimentalUrlPrefetch public class PrefetchNetworkException extends androidx.webkit.PrefetchException {
+ ctor public PrefetchNetworkException();
+ ctor public PrefetchNetworkException(int);
+ ctor public PrefetchNetworkException(String);
+ ctor public PrefetchNetworkException(String, int);
+ field public static final int NO_HTTP_RESPONSE_STATUS_CODE = 0; // 0x0
+ field public final int httpResponseStatusCode;
+ }
+
+ @SuppressCompatibility @androidx.webkit.WebViewCompat.ExperimentalUrlPrerender public class PrerenderException extends java.lang.Exception {
+ ctor public PrerenderException(String, Throwable?);
+ }
+
+ @SuppressCompatibility @androidx.webkit.WebViewCompat.ExperimentalUrlPrerender public interface PrerenderOperationCallback {
+ method public void onError(androidx.webkit.PrerenderException);
+ method public void onPrerenderActivated();
+ }
+
+ public class ProcessGlobalConfig {
+ ctor public ProcessGlobalConfig();
+ method public static void apply(androidx.webkit.ProcessGlobalConfig);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.STARTUP_FEATURE_SET_DATA_DIRECTORY_SUFFIX, enforcement="androidx.webkit.WebViewFeature#isConfigFeatureSupported(String, Context)") public androidx.webkit.ProcessGlobalConfig setDataDirectorySuffix(android.content.Context, String);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.STARTUP_FEATURE_SET_DIRECTORY_BASE_PATHS, enforcement="androidx.webkit.WebViewFeature#isConfigFeatureSupported(String, Context)") public androidx.webkit.ProcessGlobalConfig setDirectoryBasePaths(android.content.Context, java.io.File, java.io.File);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.STARTUP_FEATURE_CONFIGURE_PARTITIONED_COOKIES, enforcement="androidx.webkit.WebViewFeature#isConfigFeatureSupported(String, Context)") public androidx.webkit.ProcessGlobalConfig setPartitionedCookiesEnabled(android.content.Context, boolean);
+ }
+
+ public interface Profile {
+ method @SuppressCompatibility @RequiresFeature(name=androidx.webkit.WebViewFeature.PROFILE_URL_PREFETCH, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread @androidx.webkit.Profile.ExperimentalUrlPrefetch public void clearPrefetchAsync(String, java.util.concurrent.Executor, androidx.webkit.OutcomeReceiverCompat<java.lang.Void!,androidx.webkit.PrefetchException!>);
+ method @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public android.webkit.CookieManager getCookieManager();
+ method @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public android.webkit.GeolocationPermissions getGeolocationPermissions();
+ method @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public String getName();
+ method @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public android.webkit.ServiceWorkerController getServiceWorkerController();
+ method @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public android.webkit.WebStorage getWebStorage();
+ method @SuppressCompatibility @RequiresFeature(name=androidx.webkit.WebViewFeature.PROFILE_URL_PREFETCH, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread @androidx.webkit.Profile.ExperimentalUrlPrefetch public void prefetchUrlAsync(String, android.os.CancellationSignal?, java.util.concurrent.Executor, androidx.webkit.OutcomeReceiverCompat<java.lang.Void!,androidx.webkit.PrefetchException!>);
+ method @SuppressCompatibility @RequiresFeature(name=androidx.webkit.WebViewFeature.PROFILE_URL_PREFETCH, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread @androidx.webkit.Profile.ExperimentalUrlPrefetch public void prefetchUrlAsync(String, android.os.CancellationSignal?, java.util.concurrent.Executor, androidx.webkit.SpeculativeLoadingParameters, androidx.webkit.OutcomeReceiverCompat<java.lang.Void!,androidx.webkit.PrefetchException!>);
+ method @SuppressCompatibility @RequiresFeature(name=androidx.webkit.WebViewFeature.SPECULATIVE_LOADING_CONFIG, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread @androidx.webkit.Profile.ExperimentalUrlPrefetch public void setSpeculativeLoadingConfig(androidx.webkit.SpeculativeLoadingConfig);
+ field public static final String DEFAULT_PROFILE_NAME = "Default";
+ }
+
+ @SuppressCompatibility @RequiresOptIn(level=androidx.annotation.RequiresOptIn.Level.ERROR) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.FIELD}) public static @interface Profile.ExperimentalUrlPrefetch {
+ }
+
+ @UiThread public interface ProfileStore {
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public boolean deleteProfile(String);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public java.util.List<java.lang.String!> getAllProfileNames();
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.ProfileStore getInstance();
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public androidx.webkit.Profile getOrCreateProfile(String);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public androidx.webkit.Profile? getProfile(String);
+ }
+
+ public final class ProxyConfig {
+ method public java.util.List<java.lang.String!> getBypassRules();
+ method public java.util.List<androidx.webkit.ProxyConfig.ProxyRule!> getProxyRules();
+ method public boolean isReverseBypassEnabled();
+ field public static final String MATCH_ALL_SCHEMES = "*";
+ field public static final String MATCH_HTTP = "http";
+ field public static final String MATCH_HTTPS = "https";
+ }
+
+ public static final class ProxyConfig.Builder {
+ ctor public ProxyConfig.Builder();
+ ctor public ProxyConfig.Builder(androidx.webkit.ProxyConfig);
+ method public androidx.webkit.ProxyConfig.Builder addBypassRule(String);
+ method public androidx.webkit.ProxyConfig.Builder addDirect();
+ method public androidx.webkit.ProxyConfig.Builder addDirect(String);
+ method public androidx.webkit.ProxyConfig.Builder addProxyRule(String);
+ method public androidx.webkit.ProxyConfig.Builder addProxyRule(String, String);
+ method public androidx.webkit.ProxyConfig build();
+ method public androidx.webkit.ProxyConfig.Builder bypassSimpleHostnames();
+ method public androidx.webkit.ProxyConfig.Builder removeImplicitRules();
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.PROXY_OVERRIDE_REVERSE_BYPASS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public androidx.webkit.ProxyConfig.Builder setReverseBypassEnabled(boolean);
+ }
+
+ public static final class ProxyConfig.ProxyRule {
+ method public String getSchemeFilter();
+ method public String getUrl();
+ }
+
+ @AnyThread public abstract class ProxyController {
+ method public abstract void clearProxyOverride(java.util.concurrent.Executor, Runnable);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.PROXY_OVERRIDE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.ProxyController getInstance();
+ method public abstract void setProxyOverride(androidx.webkit.ProxyConfig, java.util.concurrent.Executor, Runnable);
+ }
+
+ public abstract class SafeBrowsingResponseCompat {
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void backToSafety(boolean);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_RESPONSE_PROCEED, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void proceed(boolean);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_RESPONSE_SHOW_INTERSTITIAL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void showInterstitial(boolean);
+ }
+
+ public interface ScriptHandler {
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.DOCUMENT_START_SCRIPT, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public void remove();
+ }
+
+ public abstract class ServiceWorkerClientCompat {
+ ctor public ServiceWorkerClientCompat();
+ method @WorkerThread public abstract android.webkit.WebResourceResponse? shouldInterceptRequest(android.webkit.WebResourceRequest);
+ }
+
+ @AnyThread public abstract class ServiceWorkerControllerCompat {
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.ServiceWorkerControllerCompat getInstance();
+ method public abstract androidx.webkit.ServiceWorkerWebSettingsCompat getServiceWorkerWebSettings();
+ method public abstract void setServiceWorkerClient(androidx.webkit.ServiceWorkerClientCompat?);
+ }
+
+ @AnyThread public abstract class ServiceWorkerWebSettingsCompat {
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_CONTENT_ACCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract boolean getAllowContentAccess();
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_FILE_ACCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract boolean getAllowFileAccess();
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_BLOCK_NETWORK_LOADS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract boolean getBlockNetworkLoads();
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_CACHE_MODE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract int getCacheMode();
+ method @RequiresFeature(name="REQUESTED_WITH_HEADER_ALLOW_LIST", enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract java.util.Set<java.lang.String!> getRequestedWithHeaderOriginAllowList();
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_CONTENT_ACCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setAllowContentAccess(boolean);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_FILE_ACCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setAllowFileAccess(boolean);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_BLOCK_NETWORK_LOADS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setBlockNetworkLoads(boolean);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_CACHE_MODE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setCacheMode(int);
+ method @RequiresFeature(name="REQUESTED_WITH_HEADER_ALLOW_LIST", enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setRequestedWithHeaderOriginAllowList(java.util.Set<java.lang.String!>);
+ }
+
+ @SuppressCompatibility @androidx.webkit.Profile.ExperimentalUrlPrefetch public class SpeculativeLoadingConfig {
+ method @IntRange(from=1, to=androidx.webkit.SpeculativeLoadingConfig.ABSOLUTE_MAX_PREFETCHES) public int getMaxPrefetches();
+ method @IntRange(from=1, to=java.lang.Integer.MAX_VALUE) public int getPrefetchTtlSeconds();
+ field public static final int ABSOLUTE_MAX_PREFETCHES = 20; // 0x14
+ field public static final int DEFAULT_MAX_PREFETCHES = 10; // 0xa
+ field public static final int DEFAULT_TTL_SECS = 60; // 0x3c
+ }
+
+ @SuppressCompatibility @androidx.webkit.Profile.ExperimentalUrlPrefetch public static final class SpeculativeLoadingConfig.Builder {
+ ctor public SpeculativeLoadingConfig.Builder();
+ method @SuppressCompatibility @androidx.webkit.Profile.ExperimentalUrlPrefetch public androidx.webkit.SpeculativeLoadingConfig build();
+ method public androidx.webkit.SpeculativeLoadingConfig.Builder setMaxPrefetches(@IntRange(from=1, to=androidx.webkit.SpeculativeLoadingConfig.ABSOLUTE_MAX_PREFETCHES) int);
+ method public androidx.webkit.SpeculativeLoadingConfig.Builder setPrefetchTtlSeconds(@IntRange(from=1, to=java.lang.Integer.MAX_VALUE) int);
+ }
+
+ @SuppressCompatibility @RequiresFeature(name=androidx.webkit.WebViewFeature.PROFILE_URL_PREFETCH, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @androidx.webkit.Profile.ExperimentalUrlPrefetch public final class SpeculativeLoadingParameters {
+ method public java.util.Map<java.lang.String!,java.lang.String!> getAdditionalHeaders();
+ method public androidx.webkit.NoVarySearchHeader? getExpectedNoVarySearchData();
+ method public boolean isJavaScriptEnabled();
+ }
+
+ public static final class SpeculativeLoadingParameters.Builder {
+ ctor public SpeculativeLoadingParameters.Builder();
+ method @SuppressCompatibility @androidx.webkit.Profile.ExperimentalUrlPrefetch public androidx.webkit.SpeculativeLoadingParameters.Builder addAdditionalHeader(String, String);
+ method @SuppressCompatibility @androidx.webkit.Profile.ExperimentalUrlPrefetch public androidx.webkit.SpeculativeLoadingParameters.Builder addAdditionalHeaders(java.util.Map<java.lang.String!,java.lang.String!>);
+ method @SuppressCompatibility @RequiresFeature(name=androidx.webkit.WebViewFeature.PROFILE_URL_PREFETCH, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @androidx.webkit.Profile.ExperimentalUrlPrefetch public androidx.webkit.SpeculativeLoadingParameters build();
+ method @SuppressCompatibility @androidx.webkit.Profile.ExperimentalUrlPrefetch public androidx.webkit.SpeculativeLoadingParameters.Builder setExpectedNoVarySearchData(androidx.webkit.NoVarySearchHeader);
+ method @SuppressCompatibility @androidx.webkit.Profile.ExperimentalUrlPrefetch public androidx.webkit.SpeculativeLoadingParameters.Builder setJavaScriptEnabled(boolean);
+ }
+
+ public class TracingConfig {
+ method public java.util.List<java.lang.String!> getCustomIncludedCategories();
+ method public int getPredefinedCategories();
+ method public int getTracingMode();
+ field public static final int CATEGORIES_ALL = 1; // 0x1
+ field public static final int CATEGORIES_ANDROID_WEBVIEW = 2; // 0x2
+ field public static final int CATEGORIES_FRAME_VIEWER = 64; // 0x40
+ field public static final int CATEGORIES_INPUT_LATENCY = 8; // 0x8
+ field public static final int CATEGORIES_JAVASCRIPT_AND_RENDERING = 32; // 0x20
+ field public static final int CATEGORIES_NONE = 0; // 0x0
+ field public static final int CATEGORIES_RENDERING = 16; // 0x10
+ field public static final int CATEGORIES_WEB_DEVELOPER = 4; // 0x4
+ field public static final int RECORD_CONTINUOUSLY = 1; // 0x1
+ field public static final int RECORD_UNTIL_FULL = 0; // 0x0
+ }
+
+ public static class TracingConfig.Builder {
+ ctor public TracingConfig.Builder();
+ method public androidx.webkit.TracingConfig.Builder addCategories(int...);
+ method public androidx.webkit.TracingConfig.Builder addCategories(java.lang.String!...);
+ method public androidx.webkit.TracingConfig.Builder addCategories(java.util.Collection<java.lang.String!>);
+ method public androidx.webkit.TracingConfig build();
+ method public androidx.webkit.TracingConfig.Builder setTracingMode(int);
+ }
+
+ @AnyThread public abstract class TracingController {
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.TRACING_CONTROLLER_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.TracingController getInstance();
+ method public abstract boolean isTracing();
+ method public abstract void start(androidx.webkit.TracingConfig);
+ method public abstract boolean stop(java.io.OutputStream?, java.util.concurrent.Executor);
+ }
+
+ public final class URLUtilCompat {
+ method public static String? getFilenameFromContentDisposition(String);
+ method public static String guessFileName(String, String?, String?);
+ }
+
+ public final class UserAgentMetadata {
+ method public String? getArchitecture();
+ method public int getBitness();
+ method public java.util.List<androidx.webkit.UserAgentMetadata.BrandVersion!> getBrandVersionList();
+ method public String? getFullVersion();
+ method public String? getModel();
+ method public String? getPlatform();
+ method public String? getPlatformVersion();
+ method public boolean isMobile();
+ method public boolean isWow64();
+ field public static final int BITNESS_DEFAULT = 0; // 0x0
+ }
+
+ public static final class UserAgentMetadata.BrandVersion {
+ method public String getBrand();
+ method public String getFullVersion();
+ method public String getMajorVersion();
+ }
+
+ public static final class UserAgentMetadata.BrandVersion.Builder {
+ ctor public UserAgentMetadata.BrandVersion.Builder();
+ ctor public UserAgentMetadata.BrandVersion.Builder(androidx.webkit.UserAgentMetadata.BrandVersion);
+ method public androidx.webkit.UserAgentMetadata.BrandVersion build();
+ method public androidx.webkit.UserAgentMetadata.BrandVersion.Builder setBrand(String);
+ method public androidx.webkit.UserAgentMetadata.BrandVersion.Builder setFullVersion(String);
+ method public androidx.webkit.UserAgentMetadata.BrandVersion.Builder setMajorVersion(String);
+ }
+
+ public static final class UserAgentMetadata.Builder {
+ ctor public UserAgentMetadata.Builder();
+ ctor public UserAgentMetadata.Builder(androidx.webkit.UserAgentMetadata);
+ method public androidx.webkit.UserAgentMetadata build();
+ method public androidx.webkit.UserAgentMetadata.Builder setArchitecture(String?);
+ method public androidx.webkit.UserAgentMetadata.Builder setBitness(int);
+ method public androidx.webkit.UserAgentMetadata.Builder setBrandVersionList(java.util.List<androidx.webkit.UserAgentMetadata.BrandVersion!>);
+ method public androidx.webkit.UserAgentMetadata.Builder setFullVersion(String?);
+ method public androidx.webkit.UserAgentMetadata.Builder setMobile(boolean);
+ method public androidx.webkit.UserAgentMetadata.Builder setModel(String?);
+ method public androidx.webkit.UserAgentMetadata.Builder setPlatform(String?);
+ method public androidx.webkit.UserAgentMetadata.Builder setPlatformVersion(String?);
+ method public androidx.webkit.UserAgentMetadata.Builder setWow64(boolean);
+ }
+
+ public class WebMessageCompat {
+ ctor @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public WebMessageCompat(byte[]);
+ ctor @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public WebMessageCompat(byte[], androidx.webkit.WebMessagePortCompat![]?);
+ ctor public WebMessageCompat(String?);
+ ctor public WebMessageCompat(String?, androidx.webkit.WebMessagePortCompat![]?);
+ method public byte[] getArrayBuffer();
+ method public String? getData();
+ method public androidx.webkit.WebMessagePortCompat![]? getPorts();
+ method public int getType();
+ field public static final int TYPE_ARRAY_BUFFER = 1; // 0x1
+ field public static final int TYPE_STRING = 0; // 0x0
+ }
+
+ @AnyThread public abstract class WebMessagePortCompat {
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_PORT_CLOSE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void close();
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_PORT_POST_MESSAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void postMessage(androidx.webkit.WebMessageCompat);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setWebMessageCallback(android.os.Handler?, androidx.webkit.WebMessagePortCompat.WebMessageCallbackCompat);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setWebMessageCallback(androidx.webkit.WebMessagePortCompat.WebMessageCallbackCompat);
+ }
+
+ public abstract static class WebMessagePortCompat.WebMessageCallbackCompat {
+ ctor public WebMessagePortCompat.WebMessageCallbackCompat();
+ method public void onMessage(androidx.webkit.WebMessagePortCompat, androidx.webkit.WebMessageCompat?);
+ }
+
+ public abstract class WebResourceErrorCompat {
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_RESOURCE_ERROR_GET_DESCRIPTION, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract CharSequence getDescription();
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_RESOURCE_ERROR_GET_CODE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract int getErrorCode();
+ }
+
+ public class WebResourceRequestCompat {
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_RESOURCE_REQUEST_IS_REDIRECT, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean isRedirect(android.webkit.WebResourceRequest);
+ }
+
+ public class WebSettingsCompat {
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.ATTRIBUTION_REGISTRATION_BEHAVIOR, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static int getAttributionRegistrationBehavior(android.webkit.WebSettings);
+ method @SuppressCompatibility @RequiresFeature(name=androidx.webkit.WebViewFeature.BACK_FORWARD_CACHE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @androidx.webkit.WebSettingsCompat.ExperimentalBackForwardCache public static boolean getBackForwardCacheEnabled(android.webkit.WebSettings);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.DISABLED_ACTION_MODE_MENU_ITEMS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static int getDisabledActionModeMenuItems(android.webkit.WebSettings);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean getEnterpriseAuthenticationAppLinkPolicyEnabled(android.webkit.WebSettings);
+ method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.FORCE_DARK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static int getForceDark(android.webkit.WebSettings);
+ method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.FORCE_DARK_STRATEGY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static int getForceDarkStrategy(android.webkit.WebSettings);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.OFF_SCREEN_PRERASTER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean getOffscreenPreRaster(android.webkit.WebSettings);
+ method @RequiresFeature(name="REQUESTED_WITH_HEADER_ALLOW_LIST", enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static java.util.Set<java.lang.String!> getRequestedWithHeaderOriginAllowList(android.webkit.WebSettings);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_ENABLE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean getSafeBrowsingEnabled(android.webkit.WebSettings);
+ method @SuppressCompatibility @RequiresFeature(name=androidx.webkit.WebViewFeature.SPECULATIVE_LOADING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @androidx.webkit.WebSettingsCompat.ExperimentalSpeculativeLoading public static int getSpeculativeLoadingStatus(android.webkit.WebSettings);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.USER_AGENT_METADATA, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.UserAgentMetadata getUserAgentMetadata(android.webkit.WebSettings);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_AUTHENTICATION, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static int getWebAuthenticationSupport(android.webkit.WebSettings);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEBVIEW_MEDIA_INTEGRITY_API_STATUS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.WebViewMediaIntegrityApiStatusConfig getWebViewMediaIntegrityApiStatus(android.webkit.WebSettings);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.ALGORITHMIC_DARKENING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean isAlgorithmicDarkeningAllowed(android.webkit.WebSettings);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.ALGORITHMIC_DARKENING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setAlgorithmicDarkeningAllowed(android.webkit.WebSettings, boolean);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.ATTRIBUTION_REGISTRATION_BEHAVIOR, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setAttributionRegistrationBehavior(android.webkit.WebSettings, int);
+ method @SuppressCompatibility @RequiresFeature(name=androidx.webkit.WebViewFeature.BACK_FORWARD_CACHE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @androidx.webkit.WebSettingsCompat.ExperimentalBackForwardCache public static void setBackForwardCacheEnabled(android.webkit.WebSettings, boolean);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.DISABLED_ACTION_MODE_MENU_ITEMS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setDisabledActionModeMenuItems(android.webkit.WebSettings, int);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setEnterpriseAuthenticationAppLinkPolicyEnabled(android.webkit.WebSettings, boolean);
+ method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.FORCE_DARK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setForceDark(android.webkit.WebSettings, int);
+ method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.FORCE_DARK_STRATEGY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setForceDarkStrategy(android.webkit.WebSettings, int);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.OFF_SCREEN_PRERASTER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setOffscreenPreRaster(android.webkit.WebSettings, boolean);
+ method @RequiresFeature(name="REQUESTED_WITH_HEADER_ALLOW_LIST", enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setRequestedWithHeaderOriginAllowList(android.webkit.WebSettings, java.util.Set<java.lang.String!>);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_ENABLE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setSafeBrowsingEnabled(android.webkit.WebSettings, boolean);
+ method @SuppressCompatibility @RequiresFeature(name=androidx.webkit.WebViewFeature.SPECULATIVE_LOADING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @androidx.webkit.WebSettingsCompat.ExperimentalSpeculativeLoading public static void setSpeculativeLoadingStatus(android.webkit.WebSettings, @SuppressCompatibility int);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.USER_AGENT_METADATA, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setUserAgentMetadata(android.webkit.WebSettings, androidx.webkit.UserAgentMetadata);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_AUTHENTICATION, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static void setWebAuthenticationSupport(android.webkit.WebSettings, int);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEBVIEW_MEDIA_INTEGRITY_API_STATUS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setWebViewMediaIntegrityApiStatus(android.webkit.WebSettings, androidx.webkit.WebViewMediaIntegrityApiStatusConfig);
+ field public static final int ATTRIBUTION_BEHAVIOR_APP_SOURCE_AND_APP_TRIGGER = 3; // 0x3
+ field public static final int ATTRIBUTION_BEHAVIOR_APP_SOURCE_AND_WEB_TRIGGER = 1; // 0x1
+ field public static final int ATTRIBUTION_BEHAVIOR_DISABLED = 0; // 0x0
+ field public static final int ATTRIBUTION_BEHAVIOR_WEB_SOURCE_AND_WEB_TRIGGER = 2; // 0x2
+ field @Deprecated public static final int DARK_STRATEGY_PREFER_WEB_THEME_OVER_USER_AGENT_DARKENING = 2; // 0x2
+ field @Deprecated public static final int DARK_STRATEGY_USER_AGENT_DARKENING_ONLY = 0; // 0x0
+ field @Deprecated public static final int DARK_STRATEGY_WEB_THEME_DARKENING_ONLY = 1; // 0x1
+ field @Deprecated public static final int FORCE_DARK_AUTO = 1; // 0x1
+ field @Deprecated public static final int FORCE_DARK_OFF = 0; // 0x0
+ field @Deprecated public static final int FORCE_DARK_ON = 2; // 0x2
+ field @SuppressCompatibility @androidx.webkit.WebSettingsCompat.ExperimentalSpeculativeLoading public static final int SPECULATIVE_LOADING_DISABLED = 0; // 0x0
+ field @SuppressCompatibility @androidx.webkit.WebSettingsCompat.ExperimentalSpeculativeLoading public static final int SPECULATIVE_LOADING_PRERENDER_ENABLED = 1; // 0x1
+ field public static final int WEB_AUTHENTICATION_SUPPORT_FOR_APP = 1; // 0x1
+ field public static final int WEB_AUTHENTICATION_SUPPORT_FOR_BROWSER = 2; // 0x2
+ field public static final int WEB_AUTHENTICATION_SUPPORT_NONE = 0; // 0x0
+ }
+
+ @SuppressCompatibility @RequiresOptIn(level=androidx.annotation.RequiresOptIn.Level.ERROR) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.TYPE}) public static @interface WebSettingsCompat.ExperimentalBackForwardCache {
+ }
+
+ @SuppressCompatibility @RequiresOptIn(level=androidx.annotation.RequiresOptIn.Level.ERROR) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.TYPE}) public static @interface WebSettingsCompat.ExperimentalSpeculativeLoading {
+ }
+
+ public final class WebStorageCompat {
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.DELETE_BROWSING_DATA, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static void deleteBrowsingData(android.webkit.WebStorage, Runnable);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.DELETE_BROWSING_DATA, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static void deleteBrowsingData(android.webkit.WebStorage, java.util.concurrent.Executor, Runnable);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.DELETE_BROWSING_DATA, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static String deleteBrowsingDataForSite(android.webkit.WebStorage, String, Runnable);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.DELETE_BROWSING_DATA, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static String deleteBrowsingDataForSite(android.webkit.WebStorage, String, java.util.concurrent.Executor, Runnable);
+ }
+
+ public final class WebViewAssetLoader {
+ method @WorkerThread public android.webkit.WebResourceResponse? shouldInterceptRequest(android.net.Uri);
+ field public static final String DEFAULT_DOMAIN = "appassets.androidplatform.net";
+ }
+
+ public static final class WebViewAssetLoader.AssetsPathHandler implements androidx.webkit.WebViewAssetLoader.PathHandler {
+ ctor public WebViewAssetLoader.AssetsPathHandler(android.content.Context);
+ method @WorkerThread public android.webkit.WebResourceResponse? handle(String);
+ }
+
+ public static final class WebViewAssetLoader.Builder {
+ ctor public WebViewAssetLoader.Builder();
+ method public androidx.webkit.WebViewAssetLoader.Builder addPathHandler(String, androidx.webkit.WebViewAssetLoader.PathHandler);
+ method public androidx.webkit.WebViewAssetLoader build();
+ method public androidx.webkit.WebViewAssetLoader.Builder setDomain(String);
+ method public androidx.webkit.WebViewAssetLoader.Builder setHttpAllowed(boolean);
+ }
+
+ public static final class WebViewAssetLoader.InternalStoragePathHandler implements androidx.webkit.WebViewAssetLoader.PathHandler {
+ ctor public WebViewAssetLoader.InternalStoragePathHandler(android.content.Context, java.io.File);
+ method @WorkerThread public android.webkit.WebResourceResponse handle(String);
+ }
+
+ public static interface WebViewAssetLoader.PathHandler {
+ method @WorkerThread public android.webkit.WebResourceResponse? handle(String);
+ }
+
+ public static final class WebViewAssetLoader.ResourcesPathHandler implements androidx.webkit.WebViewAssetLoader.PathHandler {
+ ctor public WebViewAssetLoader.ResourcesPathHandler(android.content.Context);
+ method @WorkerThread public android.webkit.WebResourceResponse? handle(String);
+ }
+
+ public class WebViewClientCompat extends android.webkit.WebViewClient {
+ ctor public WebViewClientCompat();
+ method @RequiresApi(android.os.Build.VERSION_CODES.M) public final void onReceivedError(android.webkit.WebView, android.webkit.WebResourceRequest, android.webkit.WebResourceError);
+ method @UiThread public void onReceivedError(android.webkit.WebView, android.webkit.WebResourceRequest, androidx.webkit.WebResourceErrorCompat);
+ method @RequiresApi(android.os.Build.VERSION_CODES.O_MR1) public final void onSafeBrowsingHit(android.webkit.WebView, android.webkit.WebResourceRequest, int, android.webkit.SafeBrowsingResponse);
+ method @UiThread public void onSafeBrowsingHit(android.webkit.WebView, android.webkit.WebResourceRequest, int, androidx.webkit.SafeBrowsingResponseCompat);
+ }
+
+ public class WebViewCompat {
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.DOCUMENT_START_SCRIPT, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static androidx.webkit.ScriptHandler addDocumentStartJavaScript(android.webkit.WebView, String, java.util.Set<java.lang.String!>);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_LISTENER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static void addWebMessageListener(android.webkit.WebView, String, java.util.Set<java.lang.String!>, androidx.webkit.WebViewCompat.WebMessageListener);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.CREATE_WEB_MESSAGE_CHANNEL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static androidx.webkit.WebMessagePortCompat![] createWebMessageChannel(android.webkit.WebView);
+ method @AnyThread public static android.content.pm.PackageInfo? getCurrentWebViewPackage(android.content.Context);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static androidx.webkit.Profile getProfile(android.webkit.WebView);
+ method @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_PRIVACY_POLICY_URL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static android.net.Uri getSafeBrowsingPrivacyPolicyUrl();
+ method @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_VARIATIONS_HEADER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static String getVariationsHeader();
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_WEB_CHROME_CLIENT, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static android.webkit.WebChromeClient? getWebChromeClient(android.webkit.WebView);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_WEB_VIEW_CLIENT, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static android.webkit.WebViewClient getWebViewClient(android.webkit.WebView);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_WEB_VIEW_RENDERER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static androidx.webkit.WebViewRenderProcess? getWebViewRenderProcess(android.webkit.WebView);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static androidx.webkit.WebViewRenderProcessClient? getWebViewRenderProcessClient(android.webkit.WebView);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.MUTE_AUDIO, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static boolean isAudioMuted(android.webkit.WebView);
+ method @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean isMultiProcessEnabled();
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.VISUAL_STATE_CALLBACK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static void postVisualStateCallback(android.webkit.WebView, long, androidx.webkit.WebViewCompat.VisualStateCallback);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.POST_WEB_MESSAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static void postWebMessage(android.webkit.WebView, androidx.webkit.WebMessageCompat, android.net.Uri);
+ method @SuppressCompatibility @RequiresFeature(name=androidx.webkit.WebViewFeature.PRERENDER_WITH_URL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread @androidx.webkit.WebViewCompat.ExperimentalUrlPrerender public static void prerenderUrl(android.webkit.WebView, String, android.os.CancellationSignal?, java.util.concurrent.Executor, androidx.webkit.PrerenderOperationCallback);
+ method @SuppressCompatibility @RequiresFeature(name=androidx.webkit.WebViewFeature.PRERENDER_WITH_URL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread @androidx.webkit.WebViewCompat.ExperimentalUrlPrerender public static void prerenderUrl(android.webkit.WebView, String, android.os.CancellationSignal?, java.util.concurrent.Executor, androidx.webkit.SpeculativeLoadingParameters, androidx.webkit.PrerenderOperationCallback);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_LISTENER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static void removeWebMessageListener(android.webkit.WebView, String);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.MUTE_AUDIO, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static void setAudioMuted(android.webkit.WebView, boolean);
+ method @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.DEFAULT_TRAFFICSTATS_TAGGING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setDefaultTrafficStatsTag(int);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static void setProfile(android.webkit.WebView, String);
+ method @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_ALLOWLIST, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setSafeBrowsingAllowlist(java.util.Set<java.lang.String!>, android.webkit.ValueCallback<java.lang.Boolean!>?);
+ method @Deprecated @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_WHITELIST, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setSafeBrowsingWhitelist(java.util.List<java.lang.String!>, android.webkit.ValueCallback<java.lang.Boolean!>?);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static void setWebViewRenderProcessClient(android.webkit.WebView, androidx.webkit.WebViewRenderProcessClient?);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static void setWebViewRenderProcessClient(android.webkit.WebView, java.util.concurrent.Executor, androidx.webkit.WebViewRenderProcessClient);
+ method @Deprecated @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.START_SAFE_BROWSING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void startSafeBrowsing(android.content.Context, android.webkit.ValueCallback<java.lang.Boolean!>?);
+ method @SuppressCompatibility @AnyThread @androidx.webkit.WebViewCompat.ExperimentalAsyncStartUp public static void startUpWebView(androidx.webkit.WebViewStartUpConfig, androidx.webkit.WebViewCompat.WebViewStartUpCallback);
+ }
+
+ @SuppressCompatibility @RequiresOptIn(level=androidx.annotation.RequiresOptIn.Level.ERROR) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.FIELD}) public static @interface WebViewCompat.ExperimentalAsyncStartUp {
+ }
+
+ @SuppressCompatibility @RequiresOptIn(level=androidx.annotation.RequiresOptIn.Level.ERROR) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.FIELD}) public static @interface WebViewCompat.ExperimentalUrlPrerender {
+ }
+
+ public static interface WebViewCompat.VisualStateCallback {
+ method @UiThread public void onComplete(long);
+ }
+
+ public static interface WebViewCompat.WebMessageListener {
+ method @UiThread public void onPostMessage(android.webkit.WebView, androidx.webkit.WebMessageCompat, android.net.Uri, boolean, androidx.webkit.JavaScriptReplyProxy);
+ }
+
+ @SuppressCompatibility @androidx.webkit.WebViewCompat.ExperimentalAsyncStartUp public static interface WebViewCompat.WebViewStartUpCallback {
+ method public void onSuccess(androidx.webkit.WebViewStartUpResult);
+ }
+
+ public class WebViewFeature {
+ method public static boolean isFeatureSupported(String);
+ method public static boolean isStartupFeatureSupported(android.content.Context, String);
+ field public static final String ALGORITHMIC_DARKENING = "ALGORITHMIC_DARKENING";
+ field public static final String ATTRIBUTION_REGISTRATION_BEHAVIOR = "ATTRIBUTION_REGISTRATION_BEHAVIOR";
+ field public static final String BACK_FORWARD_CACHE = "BACK_FORWARD_CACHE";
+ field public static final String CREATE_WEB_MESSAGE_CHANNEL = "CREATE_WEB_MESSAGE_CHANNEL";
+ field public static final String DEFAULT_TRAFFICSTATS_TAGGING = "DEFAULT_TRAFFICSTATS_TAGGING";
+ field public static final String DELETE_BROWSING_DATA = "DELETE_BROWSING_DATA";
+ field public static final String DISABLED_ACTION_MODE_MENU_ITEMS = "DISABLED_ACTION_MODE_MENU_ITEMS";
+ field public static final String DOCUMENT_START_SCRIPT = "DOCUMENT_START_SCRIPT";
+ field public static final String ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY = "ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY";
+ field public static final String FORCE_DARK = "FORCE_DARK";
+ field public static final String FORCE_DARK_STRATEGY = "FORCE_DARK_STRATEGY";
+ field public static final String GET_COOKIE_INFO = "GET_COOKIE_INFO";
+ field public static final String GET_VARIATIONS_HEADER = "GET_VARIATIONS_HEADER";
+ field public static final String GET_WEB_CHROME_CLIENT = "GET_WEB_CHROME_CLIENT";
+ field public static final String GET_WEB_VIEW_CLIENT = "GET_WEB_VIEW_CLIENT";
+ field public static final String GET_WEB_VIEW_RENDERER = "GET_WEB_VIEW_RENDERER";
+ field public static final String MULTI_PROCESS = "MULTI_PROCESS";
+ field public static final String MULTI_PROFILE = "MULTI_PROFILE";
+ field public static final String MUTE_AUDIO = "MUTE_AUDIO";
+ field public static final String OFF_SCREEN_PRERASTER = "OFF_SCREEN_PRERASTER";
+ field public static final String POST_WEB_MESSAGE = "POST_WEB_MESSAGE";
+ field @SuppressCompatibility @androidx.webkit.WebViewCompat.ExperimentalUrlPrerender public static final String PRERENDER_WITH_URL = "PRERENDER_URL_V2";
+ field @SuppressCompatibility @androidx.webkit.Profile.ExperimentalUrlPrefetch public static final String PROFILE_URL_PREFETCH = "PREFETCH_URL_V3";
+ field public static final String PROXY_OVERRIDE = "PROXY_OVERRIDE";
+ field public static final String PROXY_OVERRIDE_REVERSE_BYPASS = "PROXY_OVERRIDE_REVERSE_BYPASS";
+ field public static final String RECEIVE_HTTP_ERROR = "RECEIVE_HTTP_ERROR";
+ field public static final String RECEIVE_WEB_RESOURCE_ERROR = "RECEIVE_WEB_RESOURCE_ERROR";
+ field public static final String SAFE_BROWSING_ALLOWLIST = "SAFE_BROWSING_ALLOWLIST";
+ field public static final String SAFE_BROWSING_ENABLE = "SAFE_BROWSING_ENABLE";
+ field public static final String SAFE_BROWSING_HIT = "SAFE_BROWSING_HIT";
+ field public static final String SAFE_BROWSING_PRIVACY_POLICY_URL = "SAFE_BROWSING_PRIVACY_POLICY_URL";
+ field public static final String SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY = "SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY";
+ field public static final String SAFE_BROWSING_RESPONSE_PROCEED = "SAFE_BROWSING_RESPONSE_PROCEED";
+ field public static final String SAFE_BROWSING_RESPONSE_SHOW_INTERSTITIAL = "SAFE_BROWSING_RESPONSE_SHOW_INTERSTITIAL";
+ field @Deprecated public static final String SAFE_BROWSING_WHITELIST = "SAFE_BROWSING_WHITELIST";
+ field public static final String SERVICE_WORKER_BASIC_USAGE = "SERVICE_WORKER_BASIC_USAGE";
+ field public static final String SERVICE_WORKER_BLOCK_NETWORK_LOADS = "SERVICE_WORKER_BLOCK_NETWORK_LOADS";
+ field public static final String SERVICE_WORKER_CACHE_MODE = "SERVICE_WORKER_CACHE_MODE";
+ field public static final String SERVICE_WORKER_CONTENT_ACCESS = "SERVICE_WORKER_CONTENT_ACCESS";
+ field public static final String SERVICE_WORKER_FILE_ACCESS = "SERVICE_WORKER_FILE_ACCESS";
+ field public static final String SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST = "SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST";
+ field public static final String SHOULD_OVERRIDE_WITH_REDIRECTS = "SHOULD_OVERRIDE_WITH_REDIRECTS";
+ field public static final String SPECULATIVE_LOADING = "SPECULATIVE_LOADING_STATUS";
+ field @SuppressCompatibility @androidx.webkit.Profile.ExperimentalUrlPrefetch public static final String SPECULATIVE_LOADING_CONFIG = "SPECULATIVE_LOADING_CONFIG";
+ field public static final String STARTUP_FEATURE_CONFIGURE_PARTITIONED_COOKIES = "STARTUP_FEATURE_CONFIGURE_PARTITIONED_COOKIES";
+ field public static final String STARTUP_FEATURE_SET_DATA_DIRECTORY_SUFFIX = "STARTUP_FEATURE_SET_DATA_DIRECTORY_SUFFIX";
+ field public static final String STARTUP_FEATURE_SET_DIRECTORY_BASE_PATHS = "STARTUP_FEATURE_SET_DIRECTORY_BASE_PATHS";
+ field public static final String START_SAFE_BROWSING = "START_SAFE_BROWSING";
+ field public static final String TRACING_CONTROLLER_BASIC_USAGE = "TRACING_CONTROLLER_BASIC_USAGE";
+ field public static final String USER_AGENT_METADATA = "USER_AGENT_METADATA";
+ field public static final String VISUAL_STATE_CALLBACK = "VISUAL_STATE_CALLBACK";
+ field public static final String WEBVIEW_MEDIA_INTEGRITY_API_STATUS = "WEBVIEW_MEDIA_INTEGRITY_API_STATUS";
+ field public static final String WEB_AUTHENTICATION = "WEB_AUTHENTICATION";
+ field public static final String WEB_MESSAGE_ARRAY_BUFFER = "WEB_MESSAGE_ARRAY_BUFFER";
+ field public static final String WEB_MESSAGE_CALLBACK_ON_MESSAGE = "WEB_MESSAGE_CALLBACK_ON_MESSAGE";
+ field public static final String WEB_MESSAGE_LISTENER = "WEB_MESSAGE_LISTENER";
+ field public static final String WEB_MESSAGE_PORT_CLOSE = "WEB_MESSAGE_PORT_CLOSE";
+ field public static final String WEB_MESSAGE_PORT_POST_MESSAGE = "WEB_MESSAGE_PORT_POST_MESSAGE";
+ field public static final String WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK = "WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK";
+ field public static final String WEB_RESOURCE_ERROR_GET_CODE = "WEB_RESOURCE_ERROR_GET_CODE";
+ field public static final String WEB_RESOURCE_ERROR_GET_DESCRIPTION = "WEB_RESOURCE_ERROR_GET_DESCRIPTION";
+ field public static final String WEB_RESOURCE_REQUEST_IS_REDIRECT = "WEB_RESOURCE_REQUEST_IS_REDIRECT";
+ field public static final String WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE = "WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE";
+ field public static final String WEB_VIEW_RENDERER_TERMINATE = "WEB_VIEW_RENDERER_TERMINATE";
+ }
+
+ @RequiresFeature(name=androidx.webkit.WebViewFeature.WEBVIEW_MEDIA_INTEGRITY_API_STATUS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public class WebViewMediaIntegrityApiStatusConfig {
+ ctor public WebViewMediaIntegrityApiStatusConfig(androidx.webkit.WebViewMediaIntegrityApiStatusConfig.Builder);
+ method public int getDefaultStatus();
+ method public java.util.Map<java.lang.String!,java.lang.Integer!> getOverrideRules();
+ field public static final int WEBVIEW_MEDIA_INTEGRITY_API_DISABLED = 0; // 0x0
+ field public static final int WEBVIEW_MEDIA_INTEGRITY_API_ENABLED = 2; // 0x2
+ field public static final int WEBVIEW_MEDIA_INTEGRITY_API_ENABLED_WITHOUT_APP_IDENTITY = 1; // 0x1
+ }
+
+ public static final class WebViewMediaIntegrityApiStatusConfig.Builder {
+ ctor public WebViewMediaIntegrityApiStatusConfig.Builder(int);
+ method public androidx.webkit.WebViewMediaIntegrityApiStatusConfig.Builder addOverrideRule(String, int);
+ method public androidx.webkit.WebViewMediaIntegrityApiStatusConfig build();
+ }
+
+ public abstract class WebViewRenderProcess {
+ ctor public WebViewRenderProcess();
+ method public abstract boolean terminate();
+ }
+
+ public abstract class WebViewRenderProcessClient {
+ ctor public WebViewRenderProcessClient();
+ method public abstract void onRenderProcessResponsive(android.webkit.WebView, androidx.webkit.WebViewRenderProcess?);
+ method public abstract void onRenderProcessUnresponsive(android.webkit.WebView, androidx.webkit.WebViewRenderProcess?);
+ }
+
+ @SuppressCompatibility @androidx.webkit.WebViewCompat.ExperimentalAsyncStartUp public final class WebViewStartUpConfig {
+ method public java.util.concurrent.Executor getBackgroundExecutor();
+ method public boolean shouldRunUiThreadStartUpTasks();
+ }
+
+ @SuppressCompatibility @androidx.webkit.WebViewCompat.ExperimentalAsyncStartUp public static final class WebViewStartUpConfig.Builder {
+ ctor public WebViewStartUpConfig.Builder(java.util.concurrent.Executor);
+ method public androidx.webkit.WebViewStartUpConfig build();
+ method public androidx.webkit.WebViewStartUpConfig.Builder setShouldRunUiThreadStartUpTasks(boolean);
+ }
+
+ @SuppressCompatibility @androidx.webkit.WebViewCompat.ExperimentalAsyncStartUp public interface WebViewStartUpResult {
+ method public java.util.List<androidx.webkit.BlockingStartUpLocation!>? getBlockingStartUpLocations();
+ method public Long? getMaxTimePerTaskInUiThreadMillis();
+ method public Long? getTotalTimeInUiThreadMillis();
+ }
+
+}
+
diff --git a/webkit/webkit/src/main/java/androidx/webkit/URLUtilCompat.java b/webkit/webkit/src/main/java/androidx/webkit/URLUtilCompat.java
index 4761400..487fc51 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/URLUtilCompat.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/URLUtilCompat.java
@@ -34,7 +34,6 @@
*
* @see android.webkit.URLUtil
*/
-@SuppressWarnings("AcronymName") // Compat class for similarly named URLUtil in Android SDK
public final class URLUtilCompat {
private URLUtilCompat() {} // Class should not be instantiated
diff --git a/work/work-runtime/src/main/java/androidx/work/ListenableWorker.java b/work/work-runtime/src/main/java/androidx/work/ListenableWorker.java
index 3d5d37b..2fd3013 100644
--- a/work/work-runtime/src/main/java/androidx/work/ListenableWorker.java
+++ b/work/work-runtime/src/main/java/androidx/work/ListenableWorker.java
@@ -394,8 +394,8 @@
* Returns an instance of {@link Result} that can be used to indicate that the work
* completed with a permanent failure. Any work that depends on this will also be marked as
* failed and will not be run. <b>If you need child workers to run, you need to use
- * {@link #success()} or {@link #success(Data)}</b>; failure indicates a permanent stoppage
- * of the chain of work.
+ * {@link #success()} or {@link #success(Data) success(Data)}</b>; failure indicates a
+ * permanent stoppage of the chain of work.
*
* @return An instance of {@link Result} indicating failure when executing work
*/
@@ -407,8 +407,8 @@
* Returns an instance of {@link Result} that can be used to indicate that the work
* completed with a permanent failure. Any work that depends on this will also be marked as
* failed and will not be run. <b>If you need child workers to run, you need to use
- * {@link #success()} or {@link #success(Data)}</b>; failure indicates a permanent stoppage
- * of the chain of work.
+ * {@link #success()} or {@link #success(Data) success(Data)}</b>; failure indicates a
+ * permanent stoppage of the chain of work.
*
* @param outputData A {@link Data} object that can be used to keep track of why the work
* failed
diff --git a/xr/arcore/integration-tests/whitebox/build.gradle b/xr/arcore/integration-tests/whitebox/build.gradle
new file mode 100644
index 0000000..69337e6
--- /dev/null
+++ b/xr/arcore/integration-tests/whitebox/build.gradle
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+/**
+ * This file was created using the `create_project.py` script located in the
+ * `<AndroidX root>/development/project-creator` directory.
+ *
+ * Please use that script when creating a new project, rather than copying an existing project and
+ * modifying its settings.
+ */
+
+import androidx.build.ApkCopyHelperKt
+import androidx.build.KotlinTarget
+import androidx.build.LibraryType
+
+plugins {
+ id("AndroidXPlugin")
+ id("AndroidXComposePlugin")
+ id("com.android.application")
+ id("org.jetbrains.kotlin.android")
+}
+
+android {
+ defaultConfig {
+ // TODO: This should be lower, possibly 21.
+ // Address API calls that require higher versions.
+ minSdkVersion 30
+ }
+ buildFeatures { viewBinding = true }
+ namespace = "androidx.xr.arcore.apps.whitebox"
+}
+
+androidx {
+ name = "ARCore Whitebox"
+ type = LibraryType.TEST_APPLICATION
+ inceptionYear = "2024"
+ description = "Test app that exercises the ARCore APIs."
+ kotlinTarget = KotlinTarget.KOTLIN_1_9
+ // TODO: b/326456246
+ optOutJSpecify = true
+}
+
+dependencies {
+ implementation(project(":xr:arcore:arcore"))
+ implementation(project(":xr:assets"))
+ implementation(project(":xr:compose:compose"))
+ implementation(project(":xr:scenecore:scenecore"))
+
+ implementation(libs.kotlinStdlib)
+ implementation(libs.kotlinCoroutinesGuava)
+ implementation("androidx.activity:activity-compose:1.9.3")
+ implementation("androidx.appcompat:appcompat:1.7.0")
+ implementation("androidx.compose.foundation:foundation-layout:1.7.5")
+ implementation("androidx.compose.material3:material3:1.3.1")
+ implementation("androidx.compose.runtime:runtime:1.7.5")
+ implementation("androidx.compose.ui:ui:1.7.5")
+ implementation("androidx.lifecycle:lifecycle-runtime:2.8.7")
+}
+
+// Making this APK available via Android Build so the QA team can
+// access it in a convenient manner.
+ApkCopyHelperKt.setupAppApkCopy(project, "release")
diff --git a/xr/arcore/integration-tests/whitebox/src/main/AndroidManifest.xml b/xr/arcore/integration-tests/whitebox/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..706b854
--- /dev/null
+++ b/xr/arcore/integration-tests/whitebox/src/main/AndroidManifest.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools">
+ <uses-permission android:name="android.permission.SCENE_UNDERSTANDING" />
+ <application android:label="ARCore Whitebox" android:taskAffinity="">
+ <activity
+ android:name=".MainActivity"
+ android:exported="true"
+ android:theme="@style/Theme.AppCompat.NoActionBar">
+ <property
+ android:name="android.window.PROPERTY_XR_ACTIVITY_START_MODE"
+ android:value="XR_ACTIVITY_START_MODE_FULL_SPACE_MANAGED" />
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ <activity
+ android:name=".helloar.HelloArActivity"
+ android:exported="true"
+ android:theme="@style/Theme.AppCompat.NoActionBar">
+ </activity>
+ <activity
+ android:name=".persistentanchors.PersistentAnchorsActivity"
+ android:exported="true"
+ android:theme="@style/Theme.AppCompat.NoActionBar">
+ </activity>
+ </application>
+</manifest>
diff --git a/xr/arcore/integration-tests/whitebox/src/main/kotlin/androidx/xr/arcore/apps/whitebox/MainActivity.kt b/xr/arcore/integration-tests/whitebox/src/main/kotlin/androidx/xr/arcore/apps/whitebox/MainActivity.kt
new file mode 100644
index 0000000..5a4dc72
--- /dev/null
+++ b/xr/arcore/integration-tests/whitebox/src/main/kotlin/androidx/xr/arcore/apps/whitebox/MainActivity.kt
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2024 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.xr.arcore.apps.whitebox
+
+import android.content.Intent
+import android.os.Build
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Card
+import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.xr.arcore.apps.whitebox.helloar.HelloArActivity as HelloArActivity
+import androidx.xr.arcore.apps.whitebox.persistentanchors.PersistentAnchorsActivity as PersistentAnchorsActivity
+import java.text.SimpleDateFormat
+import java.util.Locale
+
+/** Entrypoint for testing various ARCore for Android XR functionalities. */
+class MainActivity : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setContent { WhiteboxHomeScreen() }
+ }
+}
+
+@Composable
+fun WhiteboxHomeScreen(modifier: Modifier = Modifier) {
+ Surface(modifier = modifier.fillMaxSize(), color = Color.White) {
+ Column() {
+ Text(
+ "AR Whitebox Test Application",
+ modifier = Modifier.padding(20.dp),
+ fontSize = 30.sp,
+ color = Color.Black,
+ )
+ VersionInfoCard()
+ WhiteboxSessionMenu()
+ }
+ }
+}
+
+@Composable
+fun VersionInfoCard() {
+ Card {
+ Column(modifier = Modifier.padding(16.dp)) {
+ Text("Build Fingerprint: ${Build.FINGERPRINT}")
+ Text(
+ "Date: ${SimpleDateFormat("dd MMMM yyyy, HH:mm:ss", Locale.ENGLISH).format(Build.TIME)}"
+ )
+ Text("CL Number: N/A")
+ }
+ }
+}
+
+@Composable
+fun WhiteboxSessionMenu() {
+ val context = LocalContext.current
+
+ Column(modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally) {
+ Text(
+ "Test Activity List",
+ modifier = Modifier.padding(20.dp),
+ fontSize = 20.sp,
+ fontWeight = FontWeight.Bold,
+ color = Color.Black,
+ )
+ HorizontalDivider()
+ TextButton(
+ onClick = { context.startActivity(Intent(context, HelloArActivity::class.java)) }
+ ) {
+ Text("Hello AR")
+ }
+ TextButton(
+ onClick = {
+ context.startActivity(Intent(context, PersistentAnchorsActivity::class.java))
+ }
+ ) {
+ Text("Persistent Anchors")
+ }
+ }
+}
diff --git a/xr/arcore/integration-tests/whitebox/src/main/kotlin/androidx/xr/arcore/apps/whitebox/common/Composables.kt b/xr/arcore/integration-tests/whitebox/src/main/kotlin/androidx/xr/arcore/apps/whitebox/common/Composables.kt
new file mode 100644
index 0000000..4377346
--- /dev/null
+++ b/xr/arcore/integration-tests/whitebox/src/main/kotlin/androidx/xr/arcore/apps/whitebox/common/Composables.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2024 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.xr.arcore.apps.whitebox.common
+
+import android.app.Activity
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.items
+import androidx.compose.material3.CardDefaults
+import androidx.compose.material3.FilledTonalButton
+import androidx.compose.material3.OutlinedCard
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import androidx.xr.arcore.Plane
+import androidx.xr.arcore.Trackable
+
+@Composable
+fun BackToMainActivityButton() {
+ val context = LocalContext.current
+ FilledTonalButton(onClick = { (context as Activity).finish() }) { Text("Go Back") }
+}
+
+@Composable
+fun TrackablesList(trackables: List<Trackable<Trackable.State>>) {
+ LazyColumn { items(trackables) { trackable -> TrackableCard(trackable) } }
+}
+
+@Composable
+fun TrackableCard(trackable: Trackable<Trackable.State>) {
+ val state = trackable.state.collectAsStateWithLifecycle()
+ OutlinedCard(
+ colors = CardDefaults.cardColors(),
+ modifier = Modifier.padding(8.dp).fillMaxWidth(),
+ ) {
+ Column(modifier = Modifier.padding(16.dp)) {
+ Text(text = "Trackable ID: ${trackable}")
+ Text(text = "Tracking State: ${state.value.trackingState}")
+ if (trackable is Plane) {
+ Text("Plane Type: ${trackable.type}")
+ PlaneStateInfo(state.value as Plane.State)
+ }
+ }
+ }
+}
+
+@Composable
+fun PlaneStateInfo(state: Plane.State) {
+ Text(
+ text = "Plane Label: ${state.label}",
+ color = convertPlaneLabelToColor(state.label),
+ )
+ Text(text = "Plane Center Pose: ${state.centerPose}")
+ Text(text = "Plane Extents: ${state.extents}")
+ Text(text = "Subsumed by Plane: ${state.subsumedBy}")
+ Text(text = "Plane Vertices: ${state.vertices}")
+}
+
+private fun convertPlaneLabelToColor(label: Plane.Label): Color =
+ when (label) {
+ Plane.Label.Wall -> Color.Green
+ Plane.Label.Floor -> Color.Blue
+ Plane.Label.Ceiling -> Color.Yellow
+ Plane.Label.Table -> Color.Magenta
+ else -> Color.Red
+ }
diff --git a/xr/arcore/integration-tests/whitebox/src/main/kotlin/androidx/xr/arcore/apps/whitebox/common/SessionLifecycleHelper.kt b/xr/arcore/integration-tests/whitebox/src/main/kotlin/androidx/xr/arcore/apps/whitebox/common/SessionLifecycleHelper.kt
new file mode 100644
index 0000000..87d8402
--- /dev/null
+++ b/xr/arcore/integration-tests/whitebox/src/main/kotlin/androidx/xr/arcore/apps/whitebox/common/SessionLifecycleHelper.kt
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2024 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.xr.arcore.apps.whitebox.common
+
+import android.util.Log
+import android.widget.Toast
+import androidx.activity.ComponentActivity
+import androidx.activity.result.ActivityResultLauncher
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions
+import androidx.activity.result.registerForActivityResult
+import androidx.lifecycle.DefaultLifecycleObserver
+import androidx.lifecycle.LifecycleOwner
+import androidx.xr.runtime.Session
+import androidx.xr.runtime.SessionCreatePermissionsNotGranted
+import androidx.xr.runtime.SessionCreateSuccess
+import androidx.xr.runtime.SessionResumePermissionsNotGranted
+import androidx.xr.runtime.SessionResumeSuccess
+
+/**
+ * Observer class to manage the lifecycle of the Jetpack XR Runtime Session based on the lifecycle
+ * owner (activity).
+ */
+class SessionLifecycleHelper(
+ internal val onCreateCallback: (Session) -> Unit,
+ internal val onResumeCallback: (() -> Unit)? = null,
+ internal val beforePauseCallback: (() -> Unit)? = null,
+) : DefaultLifecycleObserver {
+
+ internal lateinit var session: Session
+ internal lateinit var requestPermissionLauncher: ActivityResultLauncher<Array<String>>
+
+ override fun onCreate(owner: LifecycleOwner) {
+ // Sessions can only be instantiated with an instance of [ComponentActivity].
+ check(owner is ComponentActivity) { "owner is not an instance of ComponentActivity" }
+
+ registerRequestPermissionLauncher(owner)
+
+ when (val result = Session.create(owner)) {
+ is SessionCreateSuccess -> {
+ session = result.session
+ onCreateCallback.invoke(session)
+ }
+ is SessionCreatePermissionsNotGranted -> {
+ requestPermissionLauncher.launch(result.permissions.toTypedArray())
+ }
+ }
+ }
+
+ override fun onResume(owner: LifecycleOwner) {
+ if (!this::session.isInitialized) {
+ return
+ }
+ when (val result = session.resume()) {
+ is SessionResumeSuccess -> {
+ onResumeCallback?.invoke()
+ }
+ is SessionResumePermissionsNotGranted -> {
+ requestPermissionLauncher.launch(result.permissions.toTypedArray())
+ }
+ else -> {
+ showErrorMessage("Attempted to resume while session is null.")
+ }
+ }
+ }
+
+ override fun onPause(owner: LifecycleOwner) {
+ if (!this::session.isInitialized) {
+ return
+ }
+ beforePauseCallback?.invoke()
+ session.pause()
+ }
+
+ override fun onDestroy(owner: LifecycleOwner) {
+ if (!this::session.isInitialized) {
+ return
+ }
+ session.destroy()
+ }
+
+ private fun registerRequestPermissionLauncher(activity: ComponentActivity) {
+ requestPermissionLauncher =
+ activity.registerForActivityResult(
+ ActivityResultContracts.RequestMultiplePermissions()
+ ) { permissions ->
+ val allPermissionsGranted = permissions.all { it.value }
+ if (!allPermissionsGranted) {
+ Toast.makeText(
+ activity,
+ "Required permissions were not granted, closing activity. ",
+ Toast.LENGTH_LONG,
+ )
+ .show()
+ activity.finish()
+ } else {
+ activity.recreate()
+ }
+ }
+ }
+
+ private fun <F> showErrorMessage(error: F) {
+ Log.e(TAG, error.toString())
+ }
+
+ companion object {
+ private val TAG = this::class.simpleName
+ }
+}
diff --git a/xr/arcore/integration-tests/whitebox/src/main/kotlin/androidx/xr/arcore/apps/whitebox/helloar/HelloArActivity.kt b/xr/arcore/integration-tests/whitebox/src/main/kotlin/androidx/xr/arcore/apps/whitebox/helloar/HelloArActivity.kt
new file mode 100644
index 0000000..d8683a5
--- /dev/null
+++ b/xr/arcore/integration-tests/whitebox/src/main/kotlin/androidx/xr/arcore/apps/whitebox/helloar/HelloArActivity.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2024 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.xr.arcore.apps.whitebox.helloar
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Column
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import androidx.lifecycle.lifecycleScope
+import androidx.xr.arcore.apps.whitebox.common.BackToMainActivityButton
+import androidx.xr.arcore.apps.whitebox.common.SessionLifecycleHelper
+import androidx.xr.arcore.apps.whitebox.common.TrackablesList
+import androidx.xr.arcore.apps.whitebox.helloar.rendering.AnchorRenderer
+import androidx.xr.arcore.apps.whitebox.helloar.rendering.PlaneRenderer
+import androidx.xr.arcore.perceptionState
+import androidx.xr.runtime.Session
+import androidx.xr.scenecore.Session as JxrCoreSession
+
+/** Sample that demonstrates fundamental ARCore for Android XR usage. */
+class HelloArActivity : ComponentActivity() {
+
+ private lateinit var session: Session
+ private lateinit var sessionHelper: SessionLifecycleHelper
+
+ private lateinit var jxrCoreSession: JxrCoreSession
+
+ private var planeRenderer: PlaneRenderer? = null
+ private var anchorRenderer: AnchorRenderer? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // Create session and renderers.
+ sessionHelper =
+ SessionLifecycleHelper(
+ onCreateCallback = {
+ session = it
+ jxrCoreSession = JxrCoreSession.create(this)
+ planeRenderer = PlaneRenderer(session, jxrCoreSession, lifecycleScope)
+ anchorRenderer =
+ AnchorRenderer(
+ this,
+ planeRenderer!!,
+ session,
+ jxrCoreSession,
+ lifecycleScope
+ )
+ setContent { HelloWorld(session) }
+ },
+ onResumeCallback = {
+ planeRenderer?.startRendering()
+ anchorRenderer?.startRendering()
+ },
+ beforePauseCallback = {
+ planeRenderer?.stopRendering()
+ anchorRenderer?.stopRendering()
+ },
+ )
+ lifecycle.addObserver(sessionHelper)
+ }
+}
+
+@Composable
+fun HelloWorld(session: Session) {
+ val state by session.state.collectAsStateWithLifecycle()
+ val perceptionState = state.perceptionState
+
+ Column(modifier = Modifier.background(color = Color.White)) {
+ BackToMainActivityButton()
+ Text(text = "CoreState: ${state.timeMark}")
+ if (perceptionState != null) {
+ TrackablesList(perceptionState.trackables.toList())
+ } else {
+ Text("PerceptionState is null.")
+ }
+ }
+}
diff --git a/xr/arcore/integration-tests/whitebox/src/main/kotlin/androidx/xr/arcore/apps/whitebox/helloar/rendering/AnchorModel.kt b/xr/arcore/integration-tests/whitebox/src/main/kotlin/androidx/xr/arcore/apps/whitebox/helloar/rendering/AnchorModel.kt
new file mode 100644
index 0000000..a721aff
--- /dev/null
+++ b/xr/arcore/integration-tests/whitebox/src/main/kotlin/androidx/xr/arcore/apps/whitebox/helloar/rendering/AnchorModel.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2024 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.xr.arcore.apps.whitebox.helloar.rendering
+
+import androidx.xr.arcore.Anchor
+import androidx.xr.scenecore.Entity
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.StateFlow
+
+/** Represents a rendered anchor model. */
+data class AnchorModel(
+ val id: Int,
+ val stateFlow: StateFlow<Anchor.State>,
+ internal val entity: Entity,
+ internal val renderJob: Job?,
+) {}
diff --git a/xr/arcore/integration-tests/whitebox/src/main/kotlin/androidx/xr/arcore/apps/whitebox/helloar/rendering/AnchorRenderer.kt b/xr/arcore/integration-tests/whitebox/src/main/kotlin/androidx/xr/arcore/apps/whitebox/helloar/rendering/AnchorRenderer.kt
new file mode 100644
index 0000000..a56dacb
--- /dev/null
+++ b/xr/arcore/integration-tests/whitebox/src/main/kotlin/androidx/xr/arcore/apps/whitebox/helloar/rendering/AnchorRenderer.kt
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2024 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.xr.arcore.apps.whitebox.helloar.rendering
+
+import android.app.Activity
+import android.util.Log
+import androidx.xr.arcore.Anchor
+import androidx.xr.arcore.AnchorCreateResourcesExhausted
+import androidx.xr.arcore.AnchorCreateSuccess
+import androidx.xr.arcore.Plane
+import androidx.xr.arcore.TrackingState
+import androidx.xr.arcore.hitTest
+import androidx.xr.runtime.Session
+import androidx.xr.runtime.math.Pose
+import androidx.xr.runtime.math.Quaternion
+import androidx.xr.runtime.math.Ray
+import androidx.xr.runtime.math.Vector3
+import androidx.xr.scenecore.GltfModel
+import androidx.xr.scenecore.InputEvent
+import androidx.xr.scenecore.InteractableComponent
+import androidx.xr.scenecore.Session as JxrCoreSession
+import kotlinx.coroutines.CompletableJob
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.guava.await
+import kotlinx.coroutines.launch
+
+/** Class that keeps track of anchors rendered as GLTF models in a SceneCore session. */
+internal class AnchorRenderer(
+ val activity: Activity,
+ val planeRenderer: PlaneRenderer,
+ val session: Session,
+ val renderSession: JxrCoreSession,
+ val coroutineScope: CoroutineScope,
+) {
+
+ private lateinit var gltfAnchorModel: GltfModel
+
+ private val renderedAnchors: MutableList<AnchorModel> = mutableListOf<AnchorModel>()
+
+ private lateinit var updateJob: CompletableJob
+
+ internal fun startRendering() {
+ updateJob =
+ SupervisorJob(
+ coroutineScope.launch() {
+ gltfAnchorModel =
+ renderSession.createGltfResourceAsync("models/xyzArrows.glb").await()
+ planeRenderer.renderedPlanes.collect { attachInteractableComponents(it) }
+ }
+ )
+ }
+
+ internal fun stopRendering() {
+ updateJob.complete()
+ clearRenderedAnchors()
+ }
+
+ private fun clearRenderedAnchors() {
+ for (anchor in renderedAnchors) {
+ anchor.entity.dispose()
+ }
+ renderedAnchors.clear()
+ }
+
+ private fun attachInteractableComponents(planeModels: Collection<PlaneModel>) {
+ for (planeModel in planeModels) {
+ if (planeModel.entity.getComponents().isEmpty()) {
+ planeModel.entity.addComponent(
+ InteractableComponent.create(renderSession, activity.mainExecutor) { event ->
+ if (event.action.equals(InputEvent.ACTION_DOWN)) {
+ val up =
+ renderSession.spatialUser.head?.getActivitySpacePose()?.up
+ ?: Vector3.Up
+ val perceptionRayPose =
+ renderSession.activitySpace.transformPoseTo(
+ Pose(
+ event.origin,
+ Quaternion.fromLookTowards(event.direction, up)
+ ),
+ renderSession.perceptionSpace,
+ )
+ val perceptionRay =
+ Ray(perceptionRayPose.translation, perceptionRayPose.forward)
+ hitTest(session, perceptionRay)
+ .firstOrNull {
+ // TODO(b/372054517): Re-enable creating anchors on Unknown
+ // planes once we can
+ // support rendering them.
+ (it.trackable as? Plane)?.state?.value?.label !=
+ Plane.Label.Unknown
+ }
+ ?.let { hitResult ->
+ try {
+ when (
+ val anchorResult =
+ Anchor.create(session, hitResult.hitPose)
+ ) {
+ is AnchorCreateSuccess ->
+ renderedAnchors.add(
+ createAnchorModel(anchorResult.anchor)
+ )
+ is AnchorCreateResourcesExhausted -> {}
+ }
+ } catch (e: IllegalStateException) {
+ Log.e(
+ activity::class.simpleName,
+ "Failed to create anchor: ${e.message}"
+ )
+ }
+ }
+ }
+ }
+ )
+ }
+ }
+ }
+
+ private fun createAnchorModel(anchor: Anchor): AnchorModel {
+ val entity = renderSession.createGltfEntity(gltfAnchorModel, Pose())
+ entity.setScale(.1f)
+ val renderJob =
+ coroutineScope.launch(updateJob) {
+ anchor.state.collect { state ->
+ if (state.trackingState == TrackingState.Tracking) {
+ entity.setPose(
+ renderSession.perceptionSpace.transformPoseTo(
+ state.pose,
+ renderSession.activitySpace
+ )
+ )
+ } else if (state.trackingState == TrackingState.Stopped) {
+ entity.setHidden(true)
+ }
+ }
+ }
+ return AnchorModel(anchor.hashCode(), anchor.state, entity, renderJob)
+ }
+}
diff --git a/xr/arcore/integration-tests/whitebox/src/main/kotlin/androidx/xr/arcore/apps/whitebox/helloar/rendering/PlaneModel.kt b/xr/arcore/integration-tests/whitebox/src/main/kotlin/androidx/xr/arcore/apps/whitebox/helloar/rendering/PlaneModel.kt
new file mode 100644
index 0000000..7fb4fd8
--- /dev/null
+++ b/xr/arcore/integration-tests/whitebox/src/main/kotlin/androidx/xr/arcore/apps/whitebox/helloar/rendering/PlaneModel.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2024 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.xr.arcore.apps.whitebox.helloar.rendering
+
+import androidx.xr.arcore.Plane
+import androidx.xr.scenecore.Entity
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.StateFlow
+
+/** Represents a rendered plane model. */
+data class PlaneModel(
+ val id: Int,
+ val type: Plane.Type,
+ val stateFlow: StateFlow<Plane.State>,
+ internal val entity: Entity,
+ internal val renderJob: Job?,
+) {}
diff --git a/xr/arcore/integration-tests/whitebox/src/main/kotlin/androidx/xr/arcore/apps/whitebox/helloar/rendering/PlaneRenderer.kt b/xr/arcore/integration-tests/whitebox/src/main/kotlin/androidx/xr/arcore/apps/whitebox/helloar/rendering/PlaneRenderer.kt
new file mode 100644
index 0000000..b60ab49
--- /dev/null
+++ b/xr/arcore/integration-tests/whitebox/src/main/kotlin/androidx/xr/arcore/apps/whitebox/helloar/rendering/PlaneRenderer.kt
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2024 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.xr.arcore.apps.whitebox.helloar.rendering
+
+import android.app.Activity
+import android.content.res.Resources
+import android.graphics.Color
+import android.view.View
+import android.widget.TextView
+import androidx.xr.arcore.Plane
+import androidx.xr.arcore.TrackingState
+import androidx.xr.runtime.Session
+import androidx.xr.runtime.math.Pose
+import androidx.xr.runtime.math.Quaternion
+import androidx.xr.runtime.math.Vector2
+import androidx.xr.runtime.math.Vector3
+import androidx.xr.scenecore.Dimensions
+import androidx.xr.scenecore.PanelEntity
+import androidx.xr.scenecore.PixelDimensions
+import androidx.xr.scenecore.Session as JxrCoreSession
+import kotlinx.coroutines.CompletableJob
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.launch
+
+/** Class that keeps track of planes rendered as GLTF models in a SceneCore session. */
+internal class PlaneRenderer(
+ val session: Session,
+ val renderSession: JxrCoreSession,
+ val coroutineScope: CoroutineScope,
+) {
+
+ private val _renderedPlanes: MutableStateFlow<List<PlaneModel>> =
+ MutableStateFlow(mutableListOf<PlaneModel>())
+ internal val renderedPlanes: StateFlow<Collection<PlaneModel>> = _renderedPlanes.asStateFlow()
+
+ private lateinit var updateJob: CompletableJob
+
+ internal fun startRendering() {
+ updateJob =
+ SupervisorJob(
+ coroutineScope.launch { Plane.subscribe(session).collect { updatePlaneModels(it) } }
+ )
+ }
+
+ internal fun stopRendering() {
+ updateJob.complete()
+ _renderedPlanes.value = emptyList<PlaneModel>()
+ }
+
+ private fun updatePlaneModels(planes: Collection<Plane>) {
+ val planesToRender = _renderedPlanes.value.toMutableList()
+ // Create renderers for new planes.
+ for (plane in planes) {
+ if (_renderedPlanes.value.none { it.id == plane.hashCode() }) {
+ addPlaneModel(plane, planesToRender)
+ }
+ }
+ // Stop rendering dropped planes.
+ for (renderedPlane in _renderedPlanes.value) {
+ if (planes.none { it.hashCode() == renderedPlane.id }) {
+ removePlaneModel(renderedPlane, planesToRender)
+ }
+ }
+ // Emit to notify collectors that collection has been updated.
+ _renderedPlanes.value = planesToRender
+ }
+
+ private fun addPlaneModel(plane: Plane, planesToRender: MutableList<PlaneModel>) {
+ val view = createPanelDebugViewUsingCompose(plane, renderSession.activity)
+ val entity = createPlanePanelEntity(plane, view)
+ // The counter starts at max to trigger the resize on the first update loop since emulators
+ // only
+ // update their static planes once.
+ var counter = PANEL_RESIZE_UPDATE_COUNT
+ // Make the render job a child of the update job so it completes when the parent completes.
+ val renderJob =
+ coroutineScope.launch(updateJob) {
+ plane.state.collect { state ->
+ if (state.trackingState == TrackingState.Tracking) {
+ if (state.label == Plane.Label.Unknown) {
+ entity.setHidden(true)
+ } else {
+ entity.setHidden(false)
+ counter++
+ entity.setPose(
+ renderSession.perceptionSpace
+ .transformPoseTo(state.centerPose, renderSession.activitySpace)
+ // Planes are X-Y while Panels are X-Z, so we need to rotate the
+ // X-axis by -90
+ // degrees to align them.
+ .compose(PANEL_TO_PLANE_ROTATION)
+ )
+
+ updateViewText(view, plane, state)
+ if (counter > PANEL_RESIZE_UPDATE_COUNT) {
+ val panelExtentsInPixels = convertMetersToPixels(state.extents)
+ entity.setPixelDimensions(
+ PixelDimensions(
+ width = panelExtentsInPixels.x.toInt(),
+ height = panelExtentsInPixels.y.toInt(),
+ )
+ )
+ counter = 0
+ }
+ }
+ } else if (state.trackingState == TrackingState.Stopped) {
+ entity.setHidden(true)
+ }
+ }
+ }
+
+ planesToRender.add(PlaneModel(plane.hashCode(), plane.type, plane.state, entity, renderJob))
+ }
+
+ private fun createPlanePanelEntity(plane: Plane, view: View): PanelEntity {
+ return renderSession.createPanelEntity(
+ view,
+ Dimensions(320f, 320f),
+ Dimensions(1f, 1f, 1f),
+ plane.hashCode().toString(),
+ plane.state.value.centerPose,
+ )
+ }
+
+ private fun createPanelDebugViewUsingCompose(plane: Plane, activity: Activity): View {
+ val view = TextView(activity.applicationContext)
+ view.text = "Plane: ${plane.hashCode()}"
+ view.setAutoSizeTextTypeWithDefaults(TextView.AUTO_SIZE_TEXT_TYPE_UNIFORM)
+ view.setBackgroundColor(Color.WHITE)
+ return view
+ }
+
+ private fun updateViewText(view: View, plane: Plane, state: Plane.State) {
+ val textView = view as TextView
+ textView.setBackgroundColor(convertPlaneLabelToColor(state.label))
+ textView.text = "Plane: ${plane.hashCode()}"
+ }
+
+ private fun convertPlaneLabelToColor(label: Plane.Label): Int =
+ when (label) {
+ Plane.Label.Wall -> Color.GREEN
+ Plane.Label.Floor -> Color.BLUE
+ Plane.Label.Ceiling -> Color.YELLOW
+ Plane.Label.Table -> Color.MAGENTA
+ // Planes with Unknown Label are currently not rendered.
+ else -> Color.RED
+ }
+
+ private fun convertMetersToPixels(input: Vector2): Vector2 = input * PX_PER_METER
+
+ private fun removePlaneModel(planeModel: PlaneModel, planesToRender: MutableList<PlaneModel>) {
+ planeModel.renderJob?.cancel()
+ planeModel.entity.dispose()
+ planesToRender.remove(planeModel)
+ }
+
+ private companion object {
+ private val PX_PER_METER = Resources.getSystem().displayMetrics.density * 1111.11f
+ private val PANEL_TO_PLANE_ROTATION =
+ Pose(Vector3(), Quaternion.fromEulerAngles(-90f, 0f, 0f))
+ private const val PANEL_RESIZE_UPDATE_COUNT = 50
+ }
+}
diff --git a/xr/arcore/integration-tests/whitebox/src/main/kotlin/androidx/xr/arcore/apps/whitebox/persistentanchors/PersistentAnchorsActivity.kt b/xr/arcore/integration-tests/whitebox/src/main/kotlin/androidx/xr/arcore/apps/whitebox/persistentanchors/PersistentAnchorsActivity.kt
new file mode 100644
index 0000000..58a806f
--- /dev/null
+++ b/xr/arcore/integration-tests/whitebox/src/main/kotlin/androidx/xr/arcore/apps/whitebox/persistentanchors/PersistentAnchorsActivity.kt
@@ -0,0 +1,255 @@
+/*
+ * Copyright 2024 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.xr.arcore.apps.whitebox.persistentanchors
+
+import android.app.Activity
+import android.os.Bundle
+import android.util.Log
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Button
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.platform.ViewCompositionStrategy
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.ViewModelStoreOwner
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.setViewTreeLifecycleOwner
+import androidx.lifecycle.setViewTreeViewModelStoreOwner
+import androidx.savedstate.SavedStateRegistryOwner
+import androidx.savedstate.setViewTreeSavedStateRegistryOwner
+import androidx.xr.arcore.Anchor
+import androidx.xr.arcore.AnchorCreateSuccess
+import androidx.xr.arcore.apps.whitebox.common.BackToMainActivityButton
+import androidx.xr.arcore.apps.whitebox.common.SessionLifecycleHelper
+import androidx.xr.runtime.Session
+import androidx.xr.runtime.math.Pose
+import androidx.xr.runtime.math.Vector3
+import androidx.xr.scenecore.Dimensions
+import androidx.xr.scenecore.Entity
+import androidx.xr.scenecore.Session as JxrCoreSession
+import java.util.UUID
+import kotlin.collections.List
+import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.launch
+
+/** Activity to test the Persistent Anchor APIs. */
+class PersistentAnchorsActivity : ComponentActivity() {
+
+ private lateinit var session: Session
+ private lateinit var sessionHelper: SessionLifecycleHelper
+ private lateinit var jxrCoreSession: JxrCoreSession
+ private lateinit var movableEntity: Entity
+ private val movableEntityOffset = Pose(Vector3(0f, 0f, -2.0f))
+ private val uuids = MutableStateFlow<List<UUID>>(emptyList())
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ sessionHelper =
+ SessionLifecycleHelper(
+ onCreateCallback = {
+ session = it
+ jxrCoreSession = JxrCoreSession.create(this)
+ createTargetPanel()
+ setContent { MainPanel() }
+ },
+ onResumeCallback = { onResumeCallback() },
+ )
+ lifecycle.addObserver(sessionHelper)
+ }
+
+ private fun createTargetPanel() {
+ val composeView = ComposeView(this)
+ composeView.setContent { TargetPanel() }
+ configureComposeView(composeView, this)
+ movableEntity =
+ jxrCoreSession.createPanelEntity(
+ composeView,
+ Dimensions(640f, 640f),
+ Dimensions(1f, 1f, 1f),
+ "movableEntity",
+ movableEntityOffset,
+ )
+ movableEntity.setParent(jxrCoreSession.activitySpace)
+ }
+
+ private fun onResumeCallback() {
+ lifecycleScope.launch {
+ // First load will fail, so we launch a second load after a delay which should succeed.
+ uuids.emit(Anchor.getPersistedAnchorUuids(session))
+ delay(2.seconds)
+ uuids.emit(Anchor.getPersistedAnchorUuids(session))
+ }
+ lifecycleScope.launch { session.state.collect { updatePlaneEntity() } }
+ }
+
+ private fun updatePlaneEntity() {
+ jxrCoreSession.spatialUser.head?.let {
+ movableEntity.setPose(
+ it.transformPoseTo(movableEntityOffset, jxrCoreSession.activitySpace)
+ )
+ }
+ }
+
+ private fun configureComposeView(composeView: ComposeView, activity: Activity) {
+ composeView.setViewCompositionStrategy(
+ ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
+ )
+ composeView.setViewTreeLifecycleOwner(activity as LifecycleOwner)
+ composeView.setViewTreeViewModelStoreOwner(activity as ViewModelStoreOwner)
+ composeView.setViewTreeSavedStateRegistryOwner(activity as SavedStateRegistryOwner)
+ }
+
+ @Composable
+ private fun MainPanel() {
+ val uuidsState = uuids.collectAsStateWithLifecycle()
+
+ Column(
+ modifier =
+ Modifier.background(color = Color.White)
+ .fillMaxHeight()
+ .fillMaxWidth()
+ .padding(horizontal = 20.dp)
+ ) {
+ BackToMainActivityButton()
+ Text(modifier = Modifier.padding(top = 20.dp), text = "Persisted anchors:")
+ for (uuid in uuidsState.value) {
+ Row(modifier = Modifier.fillMaxWidth().padding(vertical = 20.dp)) {
+ Text(text = "UUID: $uuid", fontSize = 24.sp)
+ Button(onClick = { loadAnchor(uuid) }) { Text("Load anchor") }
+ Button(onClick = { unpersistAnchor(uuid) }) { Text("Unpersist anchor") }
+ }
+ }
+ }
+ }
+
+ @Composable
+ private fun TargetPanel() {
+ Column(
+ modifier =
+ Modifier.background(color = Color.White)
+ .fillMaxHeight()
+ .fillMaxWidth()
+ .padding(horizontal = 20.dp),
+ verticalArrangement = Arrangement.Center,
+ ) {
+ Button(onClick = { addAnchor() }) { Text(text = "Add anchor", fontSize = 38.sp) }
+ }
+ }
+
+ private fun addAnchor() {
+ val anchorPose =
+ jxrCoreSession.activitySpace.transformPoseTo(
+ movableEntity.getPose(),
+ jxrCoreSession.perceptionSpace,
+ )
+ val anchor = (Anchor.create(session, anchorPose) as AnchorCreateSuccess).anchor
+ createAnchorPanel(anchor)
+ }
+
+ private fun createAnchorPanel(anchor: Anchor) {
+ val composeView = ComposeView(this)
+ configureComposeView(composeView, this)
+ val anchorEntity = jxrCoreSession.createAnchorEntity(anchor)
+ val panelEntity =
+ jxrCoreSession.createPanelEntity(
+ composeView,
+ Dimensions(640f, 640f),
+ Dimensions(1f, 1f, 1f),
+ "anchorEntity ${anchor.hashCode()}",
+ Pose(),
+ )
+ panelEntity.setParent(anchorEntity)
+ composeView.setContent { AnchorPanel(anchor, panelEntity) }
+ }
+
+ @Composable
+ private fun AnchorPanel(anchor: Anchor, entity: Entity) {
+ val anchorState = anchor.state.collectAsStateWithLifecycle()
+ Column(
+ modifier =
+ Modifier.background(color = Color.White)
+ .fillMaxHeight()
+ .fillMaxWidth()
+ .padding(horizontal = 20.dp),
+ verticalArrangement = Arrangement.Center,
+ ) {
+ Text(
+ modifier = Modifier.padding(top = 10.dp),
+ text = "Tracking State: ${anchorState.value.trackingState}",
+ fontSize = 32.sp,
+ )
+ Button(modifier = Modifier.padding(top = 10.dp), onClick = { persistAnchor(anchor) }) {
+ Text(text = "Persist anchor", fontSize = 32.sp)
+ }
+ Button(
+ modifier = Modifier.padding(top = 10.dp),
+ onClick = { deleteEntity(anchor, entity) }
+ ) {
+ Text(text = "Delete anchor", fontSize = 32.sp)
+ }
+ }
+ }
+
+ private fun persistAnchor(anchor: Anchor) {
+ lifecycleScope.launch {
+ try {
+ anchor.persist()
+ uuids.emit(Anchor.getPersistedAnchorUuids(session))
+ } catch (e: RuntimeException) {
+ Log.e("ARCore", "Error persisting anchor: ${e.message}")
+ }
+ }
+ }
+
+ private fun deleteEntity(anchor: Anchor, entity: Entity) {
+ entity.dispose()
+ anchor.detach()
+ }
+
+ private fun unpersistAnchor(uuid: UUID) {
+ Anchor.unpersist(session, uuid)
+ lifecycleScope.launch { uuids.emit(uuids.value - uuid) }
+ }
+
+ private fun loadAnchor(uuid: UUID) {
+ val anchor = (Anchor.load(session, uuid) as AnchorCreateSuccess).anchor
+ lifecycleScope.launch {
+ // We need to wait until the anchor is tracked before querying its pose.
+ delay(1.seconds)
+ createAnchorPanel(anchor)
+ }
+ }
+}
diff --git a/xr/assets/README.md b/xr/assets/README.md
new file mode 100644
index 0000000..01e95eb
--- /dev/null
+++ b/xr/assets/README.md
@@ -0,0 +1,8 @@
+# Jetpack XR Assets
+
+This library contains a collection of assets used across the Jetpack XR
+libraries for testing/sample purposes. Additional assets may not be added in
+other repositories to avoid file duplication.
+
+All files in this directory were created for this purpose and are released
+under the terms of the Apache 2.0 license.
diff --git a/xr/assets/build.gradle b/xr/assets/build.gradle
new file mode 100644
index 0000000..0116f70
--- /dev/null
+++ b/xr/assets/build.gradle
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+/**
+ * This file was created using the `create_project.py` script located in the
+ * `<AndroidX root>/development/project-creator` directory.
+ *
+ * Please use that script when creating a new project, rather than copying an existing project and
+ * modifying its settings.
+ */
+
+import androidx.build.LibraryType
+
+plugins {
+ id("AndroidXPlugin")
+ id("com.android.library")
+ id("org.jetbrains.kotlin.android")
+}
+
+android {
+ namespace = "androidx.xr.assets"
+}
+
+androidx {
+ name = "XR Assets"
+ type = LibraryType.INTERNAL_TEST_LIBRARY
+ inceptionYear = "2024"
+ description = "Collection of assets for use across the XR libraries."
+}
diff --git a/xr/assets/src/main/assets/models/xyzArrows.glb b/xr/assets/src/main/assets/models/xyzArrows.glb
new file mode 100644
index 0000000..c43f3e7
--- /dev/null
+++ b/xr/assets/src/main/assets/models/xyzArrows.glb
Binary files differ
diff --git a/xr/compose/material3/material3/build.gradle b/xr/compose/material3/material3/build.gradle
index 0cad526..ec48d06 100644
--- a/xr/compose/material3/material3/build.gradle
+++ b/xr/compose/material3/material3/build.gradle
@@ -42,7 +42,7 @@
implementation(project(":compose:material3:material3"))
implementation(project(":compose:material3:material3-adaptive-navigation-suite"))
- implementation(project(":xr:compose:compose"))
+ implementation("androidx.xr.compose:compose:1.0.0-alpha01")
}
android {