Merge "Refactor handleProp methods out to a separate file." 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/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..a7d1d5c 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,6 +20,7 @@
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
/** The helper class to resolve AppFunction related symbols. */
@@ -113,5 +114,8 @@
val methodName = functionDeclaration.simpleName.asString()
return "${packageName}.${className}#${methodName}"
}
+
+ /** Returns the file containing the class declaration and app functions. */
+ fun getSourceFile(): KSFile? = classDeclaration.containingFile
}
}
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..6f90f16 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,14 @@
// 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"
}
// 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..f723a5cb 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,12 @@
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 kotlin.reflect.KClass
+import kotlin.reflect.cast
/**
* Checks if the type reference is of the given type.
@@ -27,12 +31,45 @@
* @throws ProcessingException If unable to resolve the type.
*/
fun KSTypeReference.isOfType(type: ClassName): Boolean {
- val ksType = this.resolve()
val typeName =
- ksType.declaration.qualifiedName
+ resolveTypeName()
?: throw ProcessingException(
"Unable to resolve the type to check if it is of type [${type}]",
this
)
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.resolveTypeName()
+ ?: throw ProcessingException(
+ "Unable to resolve type for [$shortName]",
+ it.annotationType
+ )
+ typeName.asString() == annotationClass.canonicalName
+ }
+ }
+
+private fun KSTypeReference.resolveTypeName(): KSName? = resolve().declaration.qualifiedName
+
+/** 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..39b1ebb 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,25 @@
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")
)
}
+
+ // 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/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/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/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..3325d22 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
@@ -243,8 +242,8 @@
*/
internal fun generateApi(
metalavaClasspath: FileCollection,
- projectXml: File?,
- files: CompilationInputs,
+ projectXml: File,
+ sourcePaths: Collection<File>,
apiLocation: ApiLocation,
apiLintMode: ApiLintMode,
includeRestrictToLibraryGroupApis: Boolean,
@@ -268,7 +267,7 @@
generateApi(
metalavaClasspath,
projectXml,
- files,
+ sourcePaths,
apiLocation,
generateApiMode,
apiLintMode,
@@ -287,8 +286,8 @@
*/
private fun generateApi(
metalavaClasspath: FileCollection,
- projectXml: File?,
- files: CompilationInputs,
+ projectXml: File,
+ sourcePaths: Collection<File>,
outputLocation: ApiLocation,
generateApiMode: GenerateApiMode,
apiLintMode: ApiLintMode,
@@ -300,10 +299,8 @@
) {
val args =
getGenerateApiArgs(
- files.bootClasspath,
- files.dependencyClasspath,
projectXml,
- files.sourcePaths.files,
+ sourcePaths,
outputLocation,
generateApiMode,
apiLintMode,
@@ -318,9 +315,7 @@
* [GenerateApiMode.PublicApi].
*/
fun getGenerateApiArgs(
- bootClasspath: FileCollection,
- dependencyClasspath: FileCollection,
- projectXml: File?,
+ projectXml: File,
sourcePaths: Collection<File>,
outputLocation: ApiLocation?,
generateApiMode: GenerateApiMode,
@@ -333,19 +328,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-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombination.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombination.kt
index 185aaed..5c817b5 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombination.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombination.kt
@@ -1231,12 +1231,10 @@
when (targetAspectRatio[cameraMetadata, streamConfigurationMapCompat]) {
TargetAspectRatio.RATIO_4_3 -> AspectRatioUtil.ASPECT_RATIO_4_3
TargetAspectRatio.RATIO_16_9 -> AspectRatioUtil.ASPECT_RATIO_16_9
- TargetAspectRatio.RATIO_MAX_JPEG -> {
- val maxJpegSize =
- getUpdatedSurfaceSizeDefinitionByFormat(ImageFormat.JPEG)
- .getMaximumSize(ImageFormat.JPEG)
- Rational(maxJpegSize.width, maxJpegSize.height)
- }
+ TargetAspectRatio.RATIO_MAX_JPEG ->
+ getUpdatedSurfaceSizeDefinitionByFormat(ImageFormat.JPEG)
+ .getMaximumSize(ImageFormat.JPEG)
+ ?.let { maxJpegSize -> Rational(maxJpegSize.width, maxJpegSize.height) }
else -> null
}
val resultList: MutableList<Size>
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombinationTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombinationTest.kt
index 13a7539..464c132 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombinationTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombinationTest.kt
@@ -897,6 +897,44 @@
.isEqualTo(SurfaceConfig.create(ConfigType.JPEG, ConfigSize.ULTRA_MAXIMUM))
}
+ @Test
+ fun transformSurfaceConfigWithUnsupportedFormatRecordSize() {
+ setupCamera(
+ CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY,
+ supportedFormats =
+ intArrayOf(ImageFormat.YUV_420_888, ImageFormat.JPEG, ImageFormat.PRIVATE)
+ )
+ val supportedSurfaceCombination =
+ SupportedSurfaceCombination(context, fakeCameraMetadata, mockEncoderProfilesAdapter)
+ val surfaceConfig =
+ supportedSurfaceCombination.transformSurfaceConfig(
+ CameraMode.DEFAULT,
+ JPEG_R,
+ recordSize
+ )
+ val expectedSurfaceConfig = SurfaceConfig.create(ConfigType.JPEG_R, ConfigSize.RECORD)
+ assertThat(surfaceConfig).isEqualTo(expectedSurfaceConfig)
+ }
+
+ @Test
+ fun transformSurfaceConfigWithUnsupportedFormatMaximumSize() {
+ setupCamera(
+ CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY,
+ supportedFormats =
+ intArrayOf(ImageFormat.YUV_420_888, ImageFormat.JPEG, ImageFormat.PRIVATE)
+ )
+ val supportedSurfaceCombination =
+ SupportedSurfaceCombination(context, fakeCameraMetadata, mockEncoderProfilesAdapter)
+ val surfaceConfig =
+ supportedSurfaceCombination.transformSurfaceConfig(
+ CameraMode.DEFAULT,
+ JPEG_R,
+ maximumSize
+ )
+ val expectedSurfaceConfig = SurfaceConfig.create(ConfigType.JPEG_R, ConfigSize.MAXIMUM)
+ assertThat(surfaceConfig).isEqualTo(expectedSurfaceConfig)
+ }
+
// //////////////////////////////////////////////////////////////////////////////////////////
//
// Resolution selection tests for LEGACY-level guaranteed configurations
@@ -3349,7 +3387,14 @@
characteristics
)
- whenever(mockMap.getOutputSizes(ArgumentMatchers.anyInt())).thenReturn(supportedSizes)
+ whenever(
+ mockMap.getOutputSizes(
+ ArgumentMatchers.intThat { format ->
+ supportedFormats?.contains(format) != false
+ }
+ )
+ )
+ .thenReturn(supportedSizes)
// ImageFormat.PRIVATE was supported since API level 23. Before that, the supported
// output sizes need to be retrieved via SurfaceTexture.class.
whenever(mockMap.getOutputSizes(SurfaceTexture::class.java)).thenReturn(supportedSizes)
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-camera2/src/main/java/androidx/camera/camera2/internal/SupportedSurfaceCombination.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/SupportedSurfaceCombination.java
index 4dc8635..af8059d5 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/SupportedSurfaceCombination.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/SupportedSurfaceCombination.java
@@ -1131,7 +1131,8 @@
case TargetAspectRatio.RATIO_MAX_JPEG:
Size maxJpegSize = getUpdatedSurfaceSizeDefinitionByFormat(
ImageFormat.JPEG).getMaximumSize(ImageFormat.JPEG);
- ratio = new Rational(maxJpegSize.getWidth(), maxJpegSize.getHeight());
+ ratio = maxJpegSize == null ? null : new Rational(maxJpegSize.getWidth(),
+ maxJpegSize.getHeight());
break;
case TargetAspectRatio.RATIO_ORIGINAL:
ratio = null;
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.kt b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.kt
index 7f11ecb..eec2fef 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.kt
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/SupportedSurfaceCombinationTest.kt
@@ -1012,6 +1012,52 @@
.isEqualTo(SurfaceConfig.create(ConfigType.JPEG, ConfigSize.ULTRA_MAXIMUM))
}
+ @Test
+ fun transformSurfaceConfigWithUnsupportedFormatRecordSize() {
+ setupCameraAndInitCameraX(
+ supportedFormats =
+ intArrayOf(ImageFormat.YUV_420_888, ImageFormat.JPEG, ImageFormat.PRIVATE)
+ )
+ val supportedSurfaceCombination =
+ SupportedSurfaceCombination(
+ context,
+ DEFAULT_CAMERA_ID,
+ cameraManagerCompat!!,
+ mockCamcorderProfileHelper
+ )
+ val surfaceConfig =
+ supportedSurfaceCombination.transformSurfaceConfig(
+ CameraMode.DEFAULT,
+ JPEG_R,
+ RECORD_SIZE
+ )
+ val expectedSurfaceConfig = SurfaceConfig.create(ConfigType.JPEG_R, ConfigSize.RECORD)
+ assertThat(surfaceConfig).isEqualTo(expectedSurfaceConfig)
+ }
+
+ @Test
+ fun transformSurfaceConfigWithUnsupportedFormatMaximumSize() {
+ setupCameraAndInitCameraX(
+ supportedFormats =
+ intArrayOf(ImageFormat.YUV_420_888, ImageFormat.JPEG, ImageFormat.PRIVATE)
+ )
+ val supportedSurfaceCombination =
+ SupportedSurfaceCombination(
+ context,
+ DEFAULT_CAMERA_ID,
+ cameraManagerCompat!!,
+ mockCamcorderProfileHelper
+ )
+ val surfaceConfig =
+ supportedSurfaceCombination.transformSurfaceConfig(
+ CameraMode.DEFAULT,
+ JPEG_R,
+ MAXIMUM_SIZE
+ )
+ val expectedSurfaceConfig = SurfaceConfig.create(ConfigType.JPEG_R, ConfigSize.MAXIMUM)
+ assertThat(surfaceConfig).isEqualTo(expectedSurfaceConfig)
+ }
+
// //////////////////////////////////////////////////////////////////////////////////////////
//
// Resolution selection tests for LEGACY-level guaranteed configurations
@@ -3634,7 +3680,15 @@
Mockito.mock(StreamConfigurationMap::class.java).also { map ->
supportedSizes?.let {
// Sets up the supported sizes
- Mockito.`when`(map.getOutputSizes(anyInt())).thenReturn(it)
+ Mockito.`when`(
+ map.getOutputSizes(
+ ArgumentMatchers.intThat { format ->
+ supportedFormats?.contains(format) != false
+ }
+ )
+ )
+ .thenReturn(it)
+
// ImageFormat.PRIVATE was supported since API level 23. Before that, the
// supported
// output sizes need to be retrieved via SurfaceTexture.class.
diff --git a/camera/camera-core/api/current.txt b/camera/camera-core/api/current.txt
index 893b313..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);
}
@@ -259,15 +259,15 @@
}
public final class FlashState {
- property public static final int FLASH_STATE_FIRED;
- property public static final int FLASH_STATE_READY;
- property public static final int FLASH_STATE_UNAVAILABLE;
- property public static final int FLASH_STATE_UNKNOWN;
- field public static final int FLASH_STATE_FIRED = 1; // 0x1
- field public static final int FLASH_STATE_READY = 3; // 0x3
- field public static final int FLASH_STATE_UNAVAILABLE = 2; // 0x2
- field public static final int FLASH_STATE_UNKNOWN = 0; // 0x0
+ property public static final int FIRED;
+ property public static final int NOT_FIRED;
+ property public static final int UNAVAILABLE;
+ property public static final int UNKNOWN;
+ field public static final int FIRED = 1; // 0x1
field public static final androidx.camera.core.FlashState INSTANCE;
+ field public static final int NOT_FIRED = 3; // 0x3
+ field public static final int UNAVAILABLE = 2; // 0x2
+ field public static final int UNKNOWN = 0; // 0x0
}
public final class FocusMeteringAction {
diff --git a/camera/camera-core/api/restricted_current.txt b/camera/camera-core/api/restricted_current.txt
index 893b313..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);
}
@@ -259,15 +259,15 @@
}
public final class FlashState {
- property public static final int FLASH_STATE_FIRED;
- property public static final int FLASH_STATE_READY;
- property public static final int FLASH_STATE_UNAVAILABLE;
- property public static final int FLASH_STATE_UNKNOWN;
- field public static final int FLASH_STATE_FIRED = 1; // 0x1
- field public static final int FLASH_STATE_READY = 3; // 0x3
- field public static final int FLASH_STATE_UNAVAILABLE = 2; // 0x2
- field public static final int FLASH_STATE_UNKNOWN = 0; // 0x0
+ property public static final int FIRED;
+ property public static final int NOT_FIRED;
+ property public static final int UNAVAILABLE;
+ property public static final int UNKNOWN;
+ field public static final int FIRED = 1; // 0x1
field public static final androidx.camera.core.FlashState INSTANCE;
+ field public static final int NOT_FIRED = 3; // 0x3
+ field public static final int UNAVAILABLE = 2; // 0x2
+ field public static final int UNKNOWN = 0; // 0x0
}
public final class FocusMeteringAction {
diff --git a/camera/camera-core/src/androidTest/java/androidx/camera/core/internal/utils/ImageUtilDeviceTest.kt b/camera/camera-core/src/androidTest/java/androidx/camera/core/internal/utils/ImageUtilDeviceTest.kt
index efa93b9..96d10ce 100644
--- a/camera/camera-core/src/androidTest/java/androidx/camera/core/internal/utils/ImageUtilDeviceTest.kt
+++ b/camera/camera-core/src/androidTest/java/androidx/camera/core/internal/utils/ImageUtilDeviceTest.kt
@@ -124,7 +124,7 @@
0,
0,
Matrix(),
- FlashState.FLASH_STATE_UNKNOWN
+ FlashState.UNKNOWN
),
WIDTH,
HEIGHT
@@ -146,7 +146,7 @@
0,
0,
Matrix(),
- FlashState.FLASH_STATE_UNKNOWN
+ FlashState.UNKNOWN
),
WIDTH,
HEIGHT
@@ -228,7 +228,7 @@
0,
0,
Matrix(),
- FlashState.FLASH_STATE_UNKNOWN
+ FlashState.UNKNOWN
)
)
image.format = ImageFormat.PRIVATE
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/AndroidImageProxy.java b/camera/camera-core/src/main/java/androidx/camera/core/AndroidImageProxy.java
index 3e05248..326b4ac 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/AndroidImageProxy.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/AndroidImageProxy.java
@@ -58,7 +58,7 @@
image.getTimestamp(),
0,
new Matrix(),
- FlashState.FLASH_STATE_UNKNOWN);
+ FlashState.UNKNOWN);
}
@Override
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/FlashState.kt b/camera/camera-core/src/main/java/androidx/camera/core/FlashState.kt
index 21b56c6..c042355 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/FlashState.kt
+++ b/camera/camera-core/src/main/java/androidx/camera/core/FlashState.kt
@@ -23,8 +23,7 @@
/**
* The camera flash state values represent the state of the physical flash unit of a camera.
*
- * The possible values are [FLASH_STATE_UNKNOWN], [FLASH_STATE_FIRED], [FLASH_STATE_UNAVAILABLE],
- * and [FLASH_STATE_READY].
+ * The possible values are [UNKNOWN], [FIRED], [UNAVAILABLE], and [NOT_FIRED].
*
* In case of any error, how it is notified depends on the API that is used for obtaining the
* [FlashState]. For example, if the flash state is obtained by invoking
@@ -34,7 +33,7 @@
*/
public object FlashState {
/** The camera flash state. */
- @IntDef(FLASH_STATE_UNKNOWN, FLASH_STATE_FIRED, FLASH_STATE_UNAVAILABLE, FLASH_STATE_READY)
+ @IntDef(UNKNOWN, FIRED, UNAVAILABLE, NOT_FIRED)
@Retention(AnnotationRetention.SOURCE)
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public annotation class FlashState
@@ -47,7 +46,7 @@
*
* @see FlashState
*/
- public const val FLASH_STATE_UNKNOWN: Int = 0
+ public const val UNKNOWN: Int = 0
/**
* State indicating the flash was fired.
@@ -59,7 +58,7 @@
*
* @see FlashState
*/
- public const val FLASH_STATE_FIRED: Int = 1
+ public const val FIRED: Int = 1
/**
* State indicating that flash is unavailable.
@@ -69,14 +68,14 @@
*
* @see FlashState
*/
- public const val FLASH_STATE_UNAVAILABLE: Int = 2
+ public const val UNAVAILABLE: Int = 2
/**
- * State indicating that flash is ready and can be fired if required.
+ * State indicating that flash has not been fired.
*
- * This is used when the flash is available to be fired but not fired yet.
+ * This is used when the flash is available but has not been fired.
*
* @see FlashState
*/
- public const val FLASH_STATE_READY: Int = 3
+ public const val NOT_FIRED: Int = 3
}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageInfo.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageInfo.java
index e14a903..40c25a6 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageInfo.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageInfo.java
@@ -17,7 +17,7 @@
package androidx.camera.core;
import static androidx.camera.core.FlashState.FlashState;
-import static androidx.camera.core.FlashState.FLASH_STATE_UNKNOWN;
+import static androidx.camera.core.FlashState.UNKNOWN;
import android.graphics.Matrix;
import android.hardware.camera2.CameraCharacteristics;
@@ -124,7 +124,7 @@
*/
@FlashState
default int getFlashState() {
- return FLASH_STATE_UNKNOWN;
+ return UNKNOWN;
}
/**
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraCaptureMetaData.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraCaptureMetaData.java
index e72e824..6cd4f2e 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraCaptureMetaData.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/CameraCaptureMetaData.java
@@ -17,10 +17,8 @@
package androidx.camera.core.impl;
-import static androidx.camera.core.FlashState.FLASH_STATE_FIRED;
-import static androidx.camera.core.FlashState.FLASH_STATE_READY;
-import static androidx.camera.core.FlashState.FLASH_STATE_UNAVAILABLE;
-import static androidx.camera.core.FlashState.FLASH_STATE_UNKNOWN;
+import static androidx.camera.core.FlashState.NOT_FIRED;
+import static androidx.camera.core.FlashState.UNAVAILABLE;
/**
* This class defines the enumeration constants used for querying the camera capture mode and
@@ -170,13 +168,13 @@
public @androidx.camera.core.FlashState.FlashState int toFlashState() {
switch (this) {
case NONE:
- return FLASH_STATE_UNAVAILABLE;
+ return UNAVAILABLE;
case READY:
- return FLASH_STATE_READY;
+ return NOT_FIRED;
case FIRED:
- return FLASH_STATE_FIRED;
+ return androidx.camera.core.FlashState.FIRED;
default:
- return FLASH_STATE_UNKNOWN;
+ return androidx.camera.core.FlashState.UNKNOWN;
}
}
}
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/camera/camera-core/src/main/java/androidx/camera/core/impl/SurfaceConfig.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/SurfaceConfig.java
index 0bf64c9..2b32965 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/SurfaceConfig.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/SurfaceConfig.java
@@ -16,6 +16,8 @@
package androidx.camera.core.impl;
+import static androidx.camera.core.impl.CameraMode.ULTRA_HIGH_RESOLUTION_CAMERA;
+
import android.graphics.ImageFormat;
import android.hardware.camera2.CameraCaptureSession.StateCallback;
import android.os.Handler;
@@ -158,12 +160,18 @@
configSize = ConfigSize.PREVIEW;
} else if (sizeArea <= SizeUtil.getArea(surfaceSizeDefinition.getRecordSize())) {
configSize = ConfigSize.RECORD;
- } else if (sizeArea <= SizeUtil.getArea(
- surfaceSizeDefinition.getMaximumSize(imageFormat))) {
- configSize = ConfigSize.MAXIMUM;
} else {
+ Size maximumSize = surfaceSizeDefinition.getMaximumSize(imageFormat);
Size ultraMaximumSize = surfaceSizeDefinition.getUltraMaximumSize(imageFormat);
- if (ultraMaximumSize != null && sizeArea <= SizeUtil.getArea(ultraMaximumSize)) {
+ // On some devices, when extensions is on, some extra formats might be supported
+ // for extensions. But those formats are not supported in the normal mode. In
+ // that case, MaximumSize could be null. Directly make configSize as MAXIMUM for
+ // the case.
+ if ((maximumSize == null || sizeArea <= SizeUtil.getArea(maximumSize))
+ && cameraMode != ULTRA_HIGH_RESOLUTION_CAMERA) {
+ configSize = ConfigSize.MAXIMUM;
+ } else if (ultraMaximumSize != null && sizeArea <= SizeUtil.getArea(
+ ultraMaximumSize)) {
configSize = ConfigSize.ULTRA_MAXIMUM;
}
}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/SurfaceSizeDefinition.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/SurfaceSizeDefinition.java
index 4e3c5f6..1bb17b0 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/SurfaceSizeDefinition.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/SurfaceSizeDefinition.java
@@ -125,7 +125,7 @@
* Returns the MAXIMUM size for the specified format, or {@code null} null if there is no
* data for the format.
*/
- public @NonNull Size getMaximumSize(int format) {
+ public @Nullable Size getMaximumSize(int format) {
return getMaximumSizeMap().get(format);
}
diff --git a/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/util/CaptureUtils.kt b/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/util/CaptureUtils.kt
index b5e8349..35d0117 100644
--- a/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/util/CaptureUtils.kt
+++ b/camera/integration-tests/viewtestapp/src/main/java/androidx/camera/integration/view/util/CaptureUtils.kt
@@ -104,9 +104,9 @@
val flashState =
when (image.imageInfo.flashState) {
- FlashState.FLASH_STATE_FIRED -> "FIRED"
- FlashState.FLASH_STATE_UNAVAILABLE -> "UNAVAILABLE"
- FlashState.FLASH_STATE_READY -> "READY"
+ FlashState.FIRED -> "FIRED"
+ FlashState.UNAVAILABLE -> "UNAVAILABLE"
+ FlashState.NOT_FIRED -> "NOT_FIRED"
else -> "UNKNOWN"
}
Toast.makeText(context, "Flash state: $flashState", Toast.LENGTH_SHORT).show()
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/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/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..6712b1e 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,8 @@
<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>
+ <!-- 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-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..48ebe5e 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;
diff --git a/compose/material3/material3/api/restricted_current.txt b/compose/material3/material3/api/restricted_current.txt
index 273f29f..48ebe5e 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;
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/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/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.
*
* ![Bottom app bar
* image](https://developer.android.com/images/reference/androidx/compose/material3/bottom-app-bar.png)
@@ -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.
*
* ![Bottom app bar
* image](https://developer.android.com/images/reference/androidx/compose/material3/bottom-app-bar.png)
@@ -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.
*
* ![Bottom app bar
* image](https://developer.android.com/images/reference/androidx/compose/material3/bottom-app-bar.png)
@@ -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.
*
* ![Bottom app bar
* image](https://developer.android.com/images/reference/androidx/compose/material3/bottom-app-bar.png)
@@ -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.
*
- * ![Bottom app bar
- * image](https://developer.android.com/images/reference/androidx/compose/material3/bottom-app-bar.png)
+ * 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/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/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/AndroidAccessibilityTest.kt
index 164d5628..7a030d67 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
@@ -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
@@ -2581,6 +2582,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() {
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/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/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/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-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/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/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/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 59c4949..8e57aae 100644
--- a/libraryversions.toml
+++ b/libraryversions.toml
@@ -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"
@@ -106,7 +106,7 @@
NAVIGATION3 = "0.1.0-dev01"
PAGING = "3.4.0-alpha01"
PALETTE = "1.1.0-alpha01"
-PDF = "1.0.0-alpha05"
+PDF = "1.0.0-alpha06"
PERCENTLAYOUT = "1.1.0-alpha01"
PERFORMANCE = "1.0.0-alpha01"
PREFERENCE = "1.3.0-alpha01"
@@ -115,8 +115,8 @@
PRIVACYSANDBOX_ADS = "1.1.0-beta12"
PRIVACYSANDBOX_PLUGINS = "1.0.0-alpha03"
PRIVACYSANDBOX_SDKRUNTIME = "1.0.0-alpha16"
-PRIVACYSANDBOX_TOOLS = "1.0.0-alpha10"
-PRIVACYSANDBOX_UI = "1.0.0-alpha12"
+PRIVACYSANDBOX_TOOLS = "1.0.0-alpha11"
+PRIVACYSANDBOX_UI = "1.0.0-alpha13"
PROFILEINSTALLER = "1.5.0-alpha01"
RECOMMENDATION = "1.1.0-alpha01"
RECYCLERVIEW = "1.5.0-alpha01"
@@ -132,7 +132,7 @@
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/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/src/main/java/androidx/navigation/ActivityNavigator.kt b/navigation/navigation-runtime/src/main/java/androidx/navigation/ActivityNavigator.kt
index 4fb24ac..6838cad 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/ActivityNavigator.kt
+++ b/navigation/navigation-runtime/src/main/java/androidx/navigation/ActivityNavigator.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/NavBackStackEntryState.kt b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavBackStackEntryState.kt
index 2fec191..be20880 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavBackStackEntryState.kt
+++ b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavBackStackEntryState.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/main/java/androidx/navigation/NavController.kt
index d592f33..4eed05f 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt
+++ b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt
@@ -21,7 +21,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 +32,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 +44,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 +111,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 +241,7 @@
public fun onDestinationChanged(
controller: NavController,
destination: NavDestination,
- arguments: Bundle?
+ arguments: SavedState?
)
}
@@ -333,7 +335,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 +1007,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 +1033,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 +1058,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 +1354,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 +1371,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 +1418,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 +1512,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 +1533,7 @@
deepLinkArgs = null
val destinationArgs = destination.addInDefaultArgs(matchingDeepLink.matchingArgs)
if (destinationArgs != null) {
- globalArgs.putAll(destinationArgs)
+ globalArgs.write { putAll(destinationArgs) }
}
}
}
@@ -1545,15 +1549,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 +1609,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 +1627,7 @@
private fun handleDeepLink(
deepLink: IntArray,
- args: Array<Bundle?>,
+ args: Array<SavedState?>,
newTask: Boolean
): Boolean {
if (newTask) {
@@ -1883,7 +1888,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 +1907,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 +1930,7 @@
@MainThread
public open fun navigate(
@IdRes resId: Int,
- args: Bundle?,
+ args: SavedState?,
navOptions: NavOptions?,
navigatorExtras: Navigator.Extras?
) {
@@ -1939,7 +1944,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 +1952,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 +2113,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 +2134,7 @@
@MainThread
private fun navigate(
node: NavDestination,
- args: Bundle?,
+ args: SavedState?,
navOptions: NavOptions?,
navigatorExtras: Navigator.Extras?
) {
@@ -2205,7 +2209,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 +2258,7 @@
private fun restoreStateInternal(
id: Int,
- args: Bundle?,
+ args: SavedState?,
navOptions: NavOptions?,
navigatorExtras: Navigator.Extras?
): Boolean {
@@ -2303,7 +2307,7 @@
private fun executeRestoreState(
entries: List<NavBackStackEntry>,
- args: Bundle?,
+ args: SavedState?,
navOptions: NavOptions?,
navigatorExtras: Navigator.Extras?
): Boolean {
@@ -2373,7 +2377,7 @@
private fun addEntryToBackStack(
node: NavDestination,
- finalArgs: Bundle?,
+ finalArgs: SavedState?,
backStackEntry: NavBackStackEntry,
restoredEntries: List<NavBackStackEntry> = emptyList()
) {
@@ -2430,7 +2434,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 +2584,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 +2653,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 +2700,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 +2716,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/NavDeepLinkBuilder.kt b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavDeepLinkBuilder.kt
index 3e65714..649ba64 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavDeepLinkBuilder.kt
+++ b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavDeepLinkBuilder.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/NavInflater.kt b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavInflater.kt
index f8e46da..a3e0c58 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavInflater.kt
+++ b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavInflater.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/main/java/androidx/navigation/Navigation.kt
index 4597770..4cf9a03 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/Navigation.kt
+++ b/navigation/navigation-runtime/src/main/java/androidx/navigation/Navigation.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/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/PdfView.kt b/pdf/pdf-viewer/src/main/kotlin/androidx/pdf/view/PdfView.kt
index 0c1973b..2621d74 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
@@ -215,6 +215,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.
@@ -521,6 +522,7 @@
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)
@@ -605,7 +607,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 +652,7 @@
scrollPositionToRestore = positionToRestore
zoomToRestore = localStateToRestore.zoom
oldWidth = localStateToRestore.viewWidth
+ isInitialZoomDone = localStateToRestore.isInitialZoomDone
} else {
scrollToRestoredPosition(positionToRestore, localStateToRestore.zoom)
}
@@ -927,7 +931,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)
}
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/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/savedstate/savedstate/api/current.txt b/savedstate/savedstate/api/current.txt
index 8a63bfe..7249870 100644
--- a/savedstate/savedstate/api/current.txt
+++ b/savedstate/savedstate/api/current.txt
@@ -219,6 +219,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..c5c3df3 100644
--- a/savedstate/savedstate/api/restricted_current.txt
+++ b/savedstate/savedstate/api/restricted_current.txt
@@ -244,6 +244,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..e96770a 100644
--- a/savedstate/savedstate/bcv/native/current.txt
+++ b/savedstate/savedstate/bcv/native/current.txt
@@ -165,12 +165,14 @@
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|[email protected](kotlinx.serialization.KSerializer<0:0>;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|[email protected](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|[email protected](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|[email protected](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|[email protected](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/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 b12b850..cf922fc 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/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/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 927217c..68bb715 100644
--- a/wear/compose/integration-tests/navigation/build.gradle
+++ b/wear/compose/integration-tests/navigation/build.gradle
@@ -49,6 +49,7 @@
implementation(project(":compose:foundation:foundation-layout"))
implementation(project(":compose:runtime:runtime"))
implementation(project(":wear:compose:compose-material"))
+ implementation(project(":wear:compose:compose-material3"))
implementation(project(":wear:compose:compose-foundation"))
implementation(project(":wear:compose:compose-foundation-samples"))
implementation(project(":wear:compose:compose-material-samples"))
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 fe2e2ee..5e56425 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
@@ -32,10 +32,14 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.wear.compose.foundation.edgeSwipeToDismiss
+import androidx.wear.compose.foundation.pager.rememberPagerState
import androidx.wear.compose.foundation.rememberSwipeToDismissBoxState
import androidx.wear.compose.material.CompactChip
import androidx.wear.compose.material.MaterialTheme
import androidx.wear.compose.material.Text
+import androidx.wear.compose.material3.AppScaffold
+import androidx.wear.compose.material3.HorizontalPagerScaffold
+import androidx.wear.compose.material3.ScreenScaffold
import androidx.wear.compose.navigation.SwipeDismissableNavHost
import androidx.wear.compose.navigation.composable
import androidx.wear.compose.navigation.rememberSwipeDismissableNavController
@@ -71,6 +75,11 @@
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") },
+ )
}
}
composable(SCREEN2) {
@@ -127,6 +136,22 @@
)
}
}
+ composable(PAGER_SCAFFOLD_SCREEN) {
+ AppScaffold {
+ val pagerState = rememberPagerState(pageCount = { 10 })
+
+ HorizontalPagerScaffold(pagerState = pagerState) { page ->
+ ScreenScaffold {
+ Box(
+ modifier = Modifier.fillMaxSize(),
+ contentAlignment = Alignment.Center
+ ) {
+ Text("Page $page")
+ }
+ }
+ }
+ }
+ }
}
}
}
@@ -137,3 +162,4 @@
private const val SCREEN2 = "screen2"
private const val SCREEN3 = "screen3"
private const val EDGE_SWIPE_SCREEN = "edge_swipe_screen"
+private const val PAGER_SCAFFOLD_SCREEN = "pager_scaffold_screen"
diff --git a/wear/protolayout/protolayout/api/current.txt b/wear/protolayout/protolayout/api/current.txt
index da4e766..9763e74 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 {
diff --git a/wear/protolayout/protolayout/api/restricted_current.txt b/wear/protolayout/protolayout/api/restricted_current.txt
index da4e766..9763e74 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 {
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/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/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