Merge "Fixed failing integration test for FastScrollViewIntegrationTest" into androidx-main
diff --git a/benchmark/benchmark-common/build.gradle b/benchmark/benchmark-common/build.gradle
index 6646ae9..70622c4 100644
--- a/benchmark/benchmark-common/build.gradle
+++ b/benchmark/benchmark-common/build.gradle
@@ -21,6 +21,8 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
+
+import androidx.build.KotlinTarget
import androidx.build.LibraryType
import androidx.build.AndroidXConfig
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
@@ -94,6 +96,7 @@
inceptionYear = "2018"
description = "Android Benchmark - Common"
legacyDisableKotlinStrictApiMode = true
+ kotlinTarget = KotlinTarget.KOTLIN_1_9
}
tasks.withType(KotlinCompile).configureEach {
diff --git a/benchmark/benchmark-junit4/build.gradle b/benchmark/benchmark-junit4/build.gradle
index 4a0d751..aafa2a8 100644
--- a/benchmark/benchmark-junit4/build.gradle
+++ b/benchmark/benchmark-junit4/build.gradle
@@ -21,6 +21,8 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
+
+import androidx.build.KotlinTarget
import androidx.build.LibraryType
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
@@ -63,6 +65,7 @@
inceptionYear = "2019"
description = "Android Benchmark - JUnit4"
legacyDisableKotlinStrictApiMode = true
+ kotlinTarget = KotlinTarget.KOTLIN_1_9
}
tasks.withType(KotlinCompile).configureEach {
diff --git a/benchmark/benchmark-macro-junit4/build.gradle b/benchmark/benchmark-macro-junit4/build.gradle
index 5b0de69..4f9ef1f 100644
--- a/benchmark/benchmark-macro-junit4/build.gradle
+++ b/benchmark/benchmark-macro-junit4/build.gradle
@@ -21,6 +21,8 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
+
+import androidx.build.KotlinTarget
import androidx.build.LibraryType
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
@@ -75,4 +77,5 @@
inceptionYear = "2020"
description = "Android Benchmark - Macrobenchmark JUnit4"
legacyDisableKotlinStrictApiMode = true
+ kotlinTarget = KotlinTarget.KOTLIN_1_9
}
diff --git a/benchmark/benchmark-macro/build.gradle b/benchmark/benchmark-macro/build.gradle
index 23786dd..3ffb560 100644
--- a/benchmark/benchmark-macro/build.gradle
+++ b/benchmark/benchmark-macro/build.gradle
@@ -21,6 +21,8 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
+
+import androidx.build.KotlinTarget
import androidx.build.LibraryType
import androidx.build.AndroidXConfig
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
@@ -176,4 +178,5 @@
targetAppProject = project(":benchmark:integration-tests:macrobenchmark-target")
targetAppVariant = "release"
}
+ kotlinTarget = KotlinTarget.KOTLIN_1_9
}
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXGradleProperties.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXGradleProperties.kt
index aea92fb..e178e58 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXGradleProperties.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXGradleProperties.kt
@@ -143,7 +143,7 @@
* If true, enable the ArrayNullnessMigration lint check to transition to type-use nullness
* annotations. Defaults to false.
*/
-const val MIGRATE_ARRAY_ANNOTATIONS = "androidx.migrateArrayAnnotations"
+const val USE_JSPECIFY_ANNOTATIONS = "androidx.useJSpecifyAnnotations"
/** If true, yarn dependencies are fetched from an offline mirror */
const val YARN_OFFLINE_MODE = "androidx.yarnOfflineMode"
@@ -186,7 +186,7 @@
FilteredAnchorTask.PROP_TASK_NAME,
FilteredAnchorTask.PROP_PATH_PREFIX,
INCLUDE_OPTIONAL_PROJECTS,
- MIGRATE_ARRAY_ANNOTATIONS,
+ USE_JSPECIFY_ANNOTATIONS,
YARN_OFFLINE_MODE,
FORCE_KOTLIN_2_0_TARGET,
FORCE_BENCHMARK_AOT_COMPILATION,
@@ -289,10 +289,10 @@
findBooleanProperty(ALLOW_CUSTOM_COMPILE_SDK) ?: true
/**
- * Whether to enable the ArrayNullnessMigration lint check for moving nullness annotations on arrays
- * when switching a project to type-use nullness annotations.
+ * Whether to enable the JSpecifyNullnessMigration lint check for moving nullness annotations when
+ * switching a project to the JSpecify type-use nullness annotations.
*/
-fun Project.migrateArrayAnnotations() = findBooleanProperty(MIGRATE_ARRAY_ANNOTATIONS) ?: false
+fun Project.useJSpecifyAnnotations() = findBooleanProperty(USE_JSPECIFY_ANNOTATIONS) ?: false
fun Project.findBooleanProperty(propName: String) = booleanPropertyProvider(propName).get()
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
index f875c2f..cd8fc46 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
@@ -417,6 +417,16 @@
evaluatedProject.kotlinExtensionOrNull?.let { kotlinExtension ->
kotlinExtension.coreLibrariesVersion = kotlinVersionStringProvider.get()
}
+ if (evaluatedProject.androidXExtension.shouldPublish()) {
+ tasks.register(
+ CheckKotlinApiTargetTask.TASK_NAME,
+ CheckKotlinApiTargetTask::class.java
+ ) {
+ it.kotlinTarget.set(kotlinVersionProvider)
+ it.outputFile.set(layout.buildDirectory.file("kotlinApiTargetCheckReport.txt"))
+ }
+ addToBuildOnServer(CheckKotlinApiTargetTask.TASK_NAME)
+ }
}
// Resolve classpath conflicts caused by kotlin-stdlib-jdk7 and -jdk8 artifacts by amending
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/CheckKotlinApiTargetTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/CheckKotlinApiTargetTask.kt
new file mode 100644
index 0000000..62dbdd0
--- /dev/null
+++ b/buildSrc/private/src/main/kotlin/androidx/build/CheckKotlinApiTargetTask.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.build
+
+import org.gradle.api.DefaultTask
+import org.gradle.api.file.RegularFileProperty
+import org.gradle.api.provider.Property
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.Internal
+import org.gradle.api.tasks.OutputFile
+import org.gradle.api.tasks.TaskAction
+import org.gradle.work.DisableCachingByDefault
+import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
+
+/** Check if the kotlin-stdlib transitive dependencies are the same as the project specified one. */
+@DisableCachingByDefault(because = "not worth caching")
+abstract class CheckKotlinApiTargetTask : DefaultTask() {
+
+ @get:Input abstract val kotlinTarget: Property<KotlinVersion>
+
+ @get:Internal val projectPath: String = project.path
+
+ @get:Input
+ val allDependencies: List<Pair<String, String>> =
+ project.configurations
+ .filter {
+ it.isCanBeResolved &&
+ !it.name.lowercase().contains("test") &&
+ !it.name.lowercase().contains("metadata") &&
+ !it.name.endsWith("CInterop")
+ }
+ .flatMap { config ->
+ config.resolvedConfiguration.firstLevelModuleDependencies.map {
+ "${it.moduleName}:${it.moduleVersion}" to config.name
+ }
+ }
+
+ @get:OutputFile abstract val outputFile: RegularFileProperty
+
+ @TaskAction
+ fun check() {
+ val incompatibleConfigurations =
+ allDependencies
+ .asSequence()
+ .filter { it.first.startsWith("kotlin-stdlib:") }
+ .map { it.first.substringAfter(":") to it.second }
+ .map { KotlinVersion.fromVersion(it.first.substringBeforeLast('.')) to it.second }
+ .filter { it.first != kotlinTarget.get() }
+ .map { it.second }
+ .toList()
+
+ val outputFile = outputFile.get().asFile
+ outputFile.parentFile.mkdirs()
+
+ if (incompatibleConfigurations.isNotEmpty()) {
+ val errorMessage =
+ incompatibleConfigurations.joinToString(
+ prefix =
+ "The project's kotlin-stdlib target is ${kotlinTarget.get()} but these " +
+ "configurations are pulling in different versions of kotlin-stdlib: ",
+ postfix =
+ "\nRun ./gradlew $projectPath:dependencies to see which dependency is " +
+ "pulling in the incompatible kotlin-stdlib"
+ )
+ outputFile.writeText("FAILURE: $errorMessage")
+ throw IllegalStateException(errorMessage)
+ }
+ }
+
+ companion object {
+ const val TASK_NAME = "checkKotlinApiTarget"
+ }
+}
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/Ktfmt.kt b/buildSrc/private/src/main/kotlin/androidx/build/Ktfmt.kt
index 1b01fa2..202a030 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/Ktfmt.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/Ktfmt.kt
@@ -19,21 +19,20 @@
import androidx.build.logging.TERMINAL_RED
import androidx.build.logging.TERMINAL_RESET
import androidx.build.uptodatedness.cacheEvenIfNoOutputs
-import com.facebook.ktfmt.format.Formatter
-import com.facebook.ktfmt.format.Formatter.format
+import java.io.ByteArrayOutputStream
import java.io.File
import java.nio.file.Paths
import javax.inject.Inject
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.async
-import kotlinx.coroutines.awaitAll
-import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.runBlocking
import org.gradle.api.DefaultTask
import org.gradle.api.Project
+import org.gradle.api.attributes.java.TargetJvmEnvironment
+import org.gradle.api.attributes.java.TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE
+import org.gradle.api.file.ConfigurableFileCollection
+import org.gradle.api.file.FileCollection
import org.gradle.api.file.FileTree
import org.gradle.api.model.ObjectFactory
import org.gradle.api.tasks.CacheableTask
+import org.gradle.api.tasks.Classpath
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.Internal
@@ -43,21 +42,19 @@
import org.gradle.api.tasks.SkipWhenEmpty
import org.gradle.api.tasks.TaskAction
import org.gradle.api.tasks.options.Option
-import org.intellij.lang.annotations.Language
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+import org.gradle.kotlin.dsl.named
+import org.gradle.process.ExecOperations
fun Project.configureKtfmt() {
- tasks.register("ktFormat", KtfmtFormatTask::class.java)
+ val ktfmtClasspath = getKtfmtConfiguration()
+ tasks.register("ktFormat", KtfmtFormatTask::class.java) { task ->
+ task.ktfmtClasspath.from(ktfmtClasspath)
+ }
val ktCheckTask =
tasks.register("ktCheck", KtfmtCheckTask::class.java) { task ->
+ task.ktfmtClasspath.from(ktfmtClasspath)
task.cacheEvenIfNoOutputs()
- // Workaround for https://github.com/gradle/gradle/issues/29205
- // Our ktfmt tasks declare "src" as an input, while our KotlinCompile tasks use
- // something like src/main/java as an input
- // Currently Gradle can sometimes get confused when loading a parent and child directory
- // at the same time, so we ask Gradle to avoid running both tasks in parallel
- task.mustRunAfter(project.tasks.withType(KotlinCompile::class.java))
}
// afterEvaluate because Gradle's default "check" task doesn't exist yet
@@ -78,11 +75,27 @@
)
private val ExcludedDirectoryGlobs = ExcludedDirectories.map { "**/$it/**/*.kt" }
+private const val MainClass = "com.facebook.ktfmt.cli.Main"
private const val InputDir = "src"
private const val IncludedFiles = "**/*.kt"
+private fun Project.getKtfmtConfiguration(): FileCollection {
+ val conf = configurations.detachedConfiguration(dependencies.create(getLibraryByName("ktfmt")))
+ conf.attributes {
+ it.attribute(
+ TARGET_JVM_ENVIRONMENT_ATTRIBUTE,
+ project.objects.named(TargetJvmEnvironment.STANDARD_JVM)
+ )
+ }
+ return files(conf)
+}
+
@CacheableTask
abstract class BaseKtfmtTask : DefaultTask() {
+ @get:Inject abstract val execOperations: ExecOperations
+
+ @get:Classpath abstract val ktfmtClasspath: ConfigurableFileCollection
+
@get:Inject abstract val objects: ObjectFactory
@get:Internal val projectPath: String = project.path
@@ -114,54 +127,39 @@
protected fun runKtfmt(format: Boolean) {
if (getInputFiles().files.isEmpty()) return
- runBlocking(Dispatchers.IO) {
- val result = processInputFiles()
- val incorrectlyFormatted = result.filter { !it.isCorrectlyFormatted }
- if (incorrectlyFormatted.isNotEmpty()) {
- if (format) {
- incorrectlyFormatted.forEach { it.input.writeText(it.formattedCode) }
- } else {
- error(
- "Found ${incorrectlyFormatted.size} files that are not correctly " +
- "formatted:\n" +
- incorrectlyFormatted.map { it.input }.joinToString("\n") +
- """
-
- ********************************************************************************
- You can attempt to automatically fix these issues with:
- ./gradlew $projectPath:ktFormat
- ********************************************************************************
- """
- .trimIndent()
- )
- }
- }
+ val outputStream = ByteArrayOutputStream()
+ execOperations.javaexec { javaExecSpec ->
+ javaExecSpec.standardOutput = outputStream
+ javaExecSpec.mainClass.set(MainClass)
+ javaExecSpec.classpath = ktfmtClasspath
+ javaExecSpec.args = getArgsList(format = format)
+ javaExecSpec.jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED")
+ overrideDirectory?.let { javaExecSpec.workingDir = it }
+ }
+ val output = outputStream.toString()
+ if (output.isNotEmpty()) {
+ processOutput(output)
+ }
+ if (output.isNotEmpty()) {
+ error(processOutput(output))
}
}
- /** Run ktfmt on all the files in [getInputFiles] in parallel. */
- private suspend fun processInputFiles(): List<KtfmtResult> {
- return coroutineScope { getInputFiles().files.map { async { processFile(it) } }.awaitAll() }
- }
+ open fun processOutput(output: String): String =
+ """
+ Failed check for the following files:
+ $output
+ """
+ .trimIndent()
- /** Run ktfmt on the [input] file. */
- private fun processFile(input: File): KtfmtResult {
- val originCode = input.readText()
- val formattedCode = format(Formatter.KOTLINLANG_FORMAT, originCode)
- return KtfmtResult(
- input = input,
- isCorrectlyFormatted = originCode == formattedCode,
- formattedCode = formattedCode
- )
+ private fun getArgsList(format: Boolean): List<String> {
+ val arguments = mutableListOf("--kotlinlang-style")
+ if (!format) arguments.add("--dry-run")
+ arguments.addAll(getInputFiles().files.map { it.absolutePath })
+ return arguments
}
}
-internal data class KtfmtResult(
- val input: File,
- val isCorrectlyFormatted: Boolean,
- @Language("kotlin") val formattedCode: String,
-)
-
@CacheableTask
abstract class KtfmtFormatTask : BaseKtfmtTask() {
init {
@@ -189,6 +187,18 @@
fun runCheck() {
runKtfmt(format = false)
}
+
+ override fun processOutput(output: String): String =
+ """
+ Failed check for the following files:
+ $output
+
+ ********************************************************************************
+ ${TERMINAL_RED}You can automatically fix these issues with:
+ ./gradlew $projectPath:ktFormat$TERMINAL_RESET
+ ********************************************************************************
+ """
+ .trimIndent()
}
@CacheableTask
@@ -243,33 +253,35 @@
@TaskAction
fun runCheck() {
- try {
- runKtfmt(format = format)
- } catch (e: IllegalStateException) {
- val kotlinFiles =
- files.filter { file ->
- val isKotlinFile = file.endsWith(".kt") || file.endsWith(".ktx")
- val inExcludedDir =
- Paths.get(file).any { subPath ->
- ExcludedDirectories.contains(subPath.toString())
- }
+ runKtfmt(format = format)
+ }
- isKotlinFile && !inExcludedDir
- }
- error(
- """
+ override fun processOutput(output: String): String {
+ val kotlinFiles =
+ files.filter { file ->
+ val isKotlinFile = file.endsWith(".kt") || file.endsWith(".ktx")
+ val inExcludedDir =
+ Paths.get(file).any { subPath ->
+ ExcludedDirectories.contains(subPath.toString())
+ }
- ********************************************************************************
- ${TERMINAL_RED}You can attempt to automatically fix these issues with:
- ./gradlew :ktCheckFile --format ${kotlinFiles.joinToString(separator = " "){ "--file $it" }}$TERMINAL_RESET
- ********************************************************************************
- """
- .trimIndent()
- )
- }
+ isKotlinFile && !inExcludedDir
+ }
+ return """
+ Failed check for the following files:
+ $output
+
+ ********************************************************************************
+ ${TERMINAL_RED}You can attempt to automatically fix these issues with:
+ ./gradlew :ktCheckFile --format ${kotlinFiles.joinToString(separator = " "){ "--file $it" }}$TERMINAL_RESET
+ ********************************************************************************
+ """
+ .trimIndent()
}
}
fun Project.configureKtfmtCheckFile() {
- tasks.register("ktCheckFile", KtfmtCheckFileTask::class.java)
+ tasks.register("ktCheckFile", KtfmtCheckFileTask::class.java) { task ->
+ task.ktfmtClasspath.from(getKtfmtConfiguration())
+ }
}
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/LintConfiguration.kt b/buildSrc/private/src/main/kotlin/androidx/build/LintConfiguration.kt
index 563931b..25c421d 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/LintConfiguration.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/LintConfiguration.kt
@@ -341,10 +341,10 @@
disable.add("IllegalExperimentalApiUsage")
}
- // Only allow the ArrayMigration check to be run when opted-in, since this is meant to be
- // run once per project when switching to type-use nullness annotations.
- if (!project.migrateArrayAnnotations()) {
- disable.add("ArrayMigration")
+ // Only allow the JSpecifyNullness check to be run when opted-in, while migrating projects
+ // to use JSpecify annotations.
+ if (!project.useJSpecifyAnnotations()) {
+ disable.add("JSpecifyNullness")
}
fatal.add("UastImplementation") // go/hide-uast-impl
diff --git a/buildSrc/shared-dependencies.gradle b/buildSrc/shared-dependencies.gradle
index 356e5ef..bc37d9f 100644
--- a/buildSrc/shared-dependencies.gradle
+++ b/buildSrc/shared-dependencies.gradle
@@ -39,8 +39,6 @@
}
implementation(libs.xerces)
- implementation(libs.ktfmt)
- implementation(libs.kotlinCoroutinesCore)
implementation(libs.shadow) // used by BundleInsideHelper.kt
api(libs.apacheAnt) // used in AarManifestTransformerTask.kt for unziping
implementation(libs.toml)
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 7259c36..26930dd 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
@@ -129,10 +129,10 @@
if (dynamicRangeResolver.is10BitDynamicRangeSupported()) {
generate10BitSupportedCombinationList()
+ }
- if (isUltraHdrSupported()) {
- generateUltraHdrSupportedCombinationList()
- }
+ if (isUltraHdrSupported()) {
+ generateUltraHdrSupportedCombinationList()
}
if (isPreviewStabilizationSupported) {
@@ -194,7 +194,12 @@
return featureSettingsToSupportedCombinationsMap[featureSettings]!!
}
var supportedSurfaceCombinations: MutableList<SurfaceCombination> = mutableListOf()
- if (featureSettings.requiredMaxBitDepth == DynamicRange.BIT_DEPTH_8_BIT) {
+ if (featureSettings.isUltraHdrOn) {
+ // For Ultra HDR output, only the default camera mode is currently supported.
+ if (featureSettings.cameraMode == CameraMode.DEFAULT) {
+ supportedSurfaceCombinations.addAll(surfaceCombinationsUltraHdr)
+ }
+ } else if (featureSettings.requiredMaxBitDepth == DynamicRange.BIT_DEPTH_8_BIT) {
when (featureSettings.cameraMode) {
CameraMode.CONCURRENT_CAMERA ->
supportedSurfaceCombinations = concurrentSurfaceCombinations
@@ -213,11 +218,7 @@
} else if (featureSettings.requiredMaxBitDepth == DynamicRange.BIT_DEPTH_10_BIT) {
// For 10-bit outputs, only the default camera mode is currently supported.
if (featureSettings.cameraMode == CameraMode.DEFAULT) {
- if (featureSettings.isUltraHdrOn) {
- supportedSurfaceCombinations.addAll(surfaceCombinationsUltraHdr)
- } else {
- supportedSurfaceCombinations.addAll(surfaceCombinations10Bit)
- }
+ supportedSurfaceCombinations.addAll(surfaceCombinations10Bit)
}
}
featureSettingsToSupportedCombinationsMap[featureSettings] = supportedSurfaceCombinations
@@ -393,6 +394,11 @@
isPreviewStabilizationOn: Boolean,
isUltraHdrOn: Boolean
): FeatureSettings {
+ require(!(cameraMode != CameraMode.DEFAULT && isUltraHdrOn)) {
+ "Camera device Id is $cameraId. Ultra HDR is not " +
+ "currently supported in ${CameraMode.toLabelString(cameraMode)} camera mode."
+ }
+
val requiredMaxBitDepth = getRequiredMaxBitDepth(resolvedDynamicRanges)
require(
!(cameraMode != CameraMode.DEFAULT &&
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/CameraCompatModule.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/CameraCompatModule.kt
index 577e4db..83c1aa3 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/CameraCompatModule.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/CameraCompatModule.kt
@@ -18,6 +18,7 @@
import androidx.camera.camera2.pipe.integration.compat.workaround.AutoFlashAEModeDisabler
import androidx.camera.camera2.pipe.integration.compat.workaround.InactiveSurfaceCloser
+import androidx.camera.camera2.pipe.integration.compat.workaround.Lock3ABehaviorWhenCaptureImage
import androidx.camera.camera2.pipe.integration.compat.workaround.MeteringRegionCorrection
import androidx.camera.camera2.pipe.integration.compat.workaround.TemplateParamsOverride
import androidx.camera.camera2.pipe.integration.compat.workaround.UseFlashModeTorchFor3aUpdate
@@ -34,6 +35,7 @@
UseFlashModeTorchFor3aUpdate.Bindings::class,
UseTorchAsFlash.Bindings::class,
TemplateParamsOverride.Bindings::class,
+ Lock3ABehaviorWhenCaptureImage.Bindings::class,
],
)
public abstract class CameraCompatModule
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/StreamConfigurationMapCompatBaseImpl.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/StreamConfigurationMapCompatBaseImpl.kt
index ec7f41f..7aa48d6 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/StreamConfigurationMapCompatBaseImpl.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/StreamConfigurationMapCompatBaseImpl.kt
@@ -19,14 +19,26 @@
import android.graphics.SurfaceTexture
import android.hardware.camera2.params.StreamConfigurationMap
import android.util.Size
+import androidx.camera.core.Logger
import androidx.camera.core.impl.ImageFormatConstants
+private const val TAG = "StreamConfigurationMapCompatBaseImpl"
+
internal open class StreamConfigurationMapCompatBaseImpl(
val streamConfigurationMap: StreamConfigurationMap?
) : StreamConfigurationMapCompat.StreamConfigurationMapCompatImpl {
override fun getOutputFormats(): Array<Int>? {
- return streamConfigurationMap?.outputFormats?.toTypedArray()
+ // b/361590210: try-catch to workaround the NullPointerException issue when using
+ // StreamConfigurationMap provided by Robolectric.
+ val outputFormats =
+ try {
+ streamConfigurationMap?.outputFormats
+ } catch (e: NullPointerException) {
+ Logger.e(TAG, "Failed to get output formats from StreamConfigurationMap", e)
+ null
+ }
+ return outputFormats?.toTypedArray()
}
override fun getOutputSizes(format: Int): Array<Size>? {
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CameraQuirks.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CameraQuirks.kt
index 7988db32..81e63e1 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CameraQuirks.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CameraQuirks.kt
@@ -240,6 +240,14 @@
) {
quirks.add(ImageCaptureFailedForVideoSnapshotQuirk())
}
+ if (
+ quirkSettings.shouldEnableQuirk(
+ LockAeAndCaptureImageBreakCameraQuirk::class.java,
+ LockAeAndCaptureImageBreakCameraQuirk.isEnabled(cameraMetadata)
+ )
+ ) {
+ quirks.add(LockAeAndCaptureImageBreakCameraQuirk())
+ }
Quirks(quirks).also {
Logger.d(TAG, "camera2-pipe-integration CameraQuirks = " + Quirks.toString(it))
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/LockAeAndCaptureImageBreakCameraQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/LockAeAndCaptureImageBreakCameraQuirk.kt
new file mode 100644
index 0000000..95ba980
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/LockAeAndCaptureImageBreakCameraQuirk.kt
@@ -0,0 +1,49 @@
+/*
+ * 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.camera.camera2.pipe.integration.compat.quirk
+
+import android.annotation.SuppressLint
+import android.hardware.camera2.CameraCharacteristics.LENS_FACING
+import android.hardware.camera2.CameraMetadata.LENS_FACING_BACK
+import android.os.Build
+import androidx.camera.camera2.pipe.CameraMetadata
+import androidx.camera.camera2.pipe.integration.compat.workaround.Lock3ABehaviorWhenCaptureImage
+import androidx.camera.core.impl.Quirk
+
+/**
+ * QuirkSummary
+ * - Bug Id: b/360106037
+ * - Description: Quirk indicating that locking AE (Auto Exposure) and taking pictures can lead to
+ * an abnormal camera service state on Pixel 3 back camera. Although the picture is successfully
+ * taken, the camera service becomes unresponsive without any error callbacks. Reopening the
+ * camera can restore its functionality.
+ * - Device(s): Pixel 3.
+ *
+ * @see Lock3ABehaviorWhenCaptureImage
+ */
+@SuppressLint("CameraXQuirksClassDetector")
+public class LockAeAndCaptureImageBreakCameraQuirk : Quirk {
+
+ public companion object {
+ public fun isEnabled(cameraMetadata: CameraMetadata): Boolean {
+ return isPixel3 && cameraMetadata[LENS_FACING] == LENS_FACING_BACK
+ }
+
+ private val isPixel3: Boolean
+ get() = "Pixel 3".equals(Build.MODEL, ignoreCase = true)
+ }
+}
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/Lock3ABehaviorWhenCaptureImage.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/Lock3ABehaviorWhenCaptureImage.kt
new file mode 100644
index 0000000..f893efd
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/Lock3ABehaviorWhenCaptureImage.kt
@@ -0,0 +1,106 @@
+/*
+ * 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.camera.camera2.pipe.integration.compat.workaround
+
+import androidx.camera.camera2.pipe.Lock3ABehavior
+import androidx.camera.camera2.pipe.integration.compat.quirk.CameraQuirks
+import androidx.camera.camera2.pipe.integration.compat.quirk.LockAeAndCaptureImageBreakCameraQuirk
+import dagger.Module
+import dagger.Provides
+
+/**
+ * Provides customized 3A lock behaviors before capturing an image.
+ *
+ * @property hasAeLockBehavior Indicates whether there is a specific AE (Auto Exposure) lock
+ * behavior defined. If true, the [aeLockBehavior] will be used; otherwise, the default AE
+ * behavior will be applied.
+ * @property aeLockBehavior The specific AE lock behavior to apply if [hasAeLockBehavior] is true.
+ * If null and [hasAeLockBehavior] is true, AE lock will be effectively disabled.
+ * @property hasAfLockBehavior Indicates whether there is a specific AF (Auto Focus) lock behavior
+ * defined. If true, the [afLockBehavior] will be used; otherwise, the default AF behavior will be
+ * applied.
+ * @property afLockBehavior The specific AF lock behavior to apply if [hasAfLockBehavior] is true.
+ * If null and [hasAfLockBehavior] is true, AF lock will be effectively disabled.
+ * @property hasAwbLockBehavior Indicates whether there is a specific AWB (Auto White Balance) lock
+ * behavior defined. If true, the [awbLockBehavior] will be used; otherwise, the default AWB
+ * behavior will be applied.
+ * @property awbLockBehavior The specific AWB lock behavior to apply if [hasAwbLockBehavior] is
+ * true. If null and [hasAwbLockBehavior] is true, AWB lock will be effectively disabled.
+ * @see LockAeAndCaptureImageBreakCameraQuirk
+ */
+public class Lock3ABehaviorWhenCaptureImage(
+ private val hasAeLockBehavior: Boolean = false,
+ private val aeLockBehavior: Lock3ABehavior? = null,
+ private val hasAfLockBehavior: Boolean = false,
+ private val afLockBehavior: Lock3ABehavior? = null,
+ private val hasAwbLockBehavior: Boolean = false,
+ private val awbLockBehavior: Lock3ABehavior? = null,
+) {
+
+ /**
+ * Gets customized 3A lock behaviors, using provided defaults if no specific behavior is set.
+ *
+ * This method checks the `has*LockBehavior` properties to determine if a custom behavior is
+ * defined for each 3A lock type (AE, AF, AWB). If a custom behavior is defined, it will be
+ * returned; otherwise, the corresponding `default*Behavior` will be used.
+ *
+ * @param defaultAeBehavior Default AE lock behavior if none is specified.
+ * @param defaultAfBehavior Default AF lock behavior if none is specified.
+ * @param defaultAwbBehavior Default AWB lock behavior if none is specified.
+ * @return A Triple containing the customized AE, AF, and AWB lock behaviors.
+ */
+ public fun getLock3ABehaviors(
+ defaultAeBehavior: Lock3ABehavior? = null,
+ defaultAfBehavior: Lock3ABehavior? = null,
+ defaultAwbBehavior: Lock3ABehavior? = null
+ ): Triple<Lock3ABehavior?, Lock3ABehavior?, Lock3ABehavior?> =
+ Triple(
+ if (hasAeLockBehavior) aeLockBehavior else defaultAeBehavior,
+ if (hasAfLockBehavior) afLockBehavior else defaultAfBehavior,
+ if (hasAwbLockBehavior) awbLockBehavior else defaultAwbBehavior
+ )
+
+ @Module
+ public abstract class Bindings {
+ public companion object {
+ @Provides
+ public fun provideLock3ABehaviorBeforeCaptureImage(
+ cameraQuirks: CameraQuirks
+ ): Lock3ABehaviorWhenCaptureImage =
+ if (
+ cameraQuirks.quirks.contains(LockAeAndCaptureImageBreakCameraQuirk::class.java)
+ ) {
+ doNotLockAe3ABehavior
+ } else {
+ noCustomizedLock3ABehavior
+ }
+ }
+ }
+
+ public companion object {
+ public val noCustomizedLock3ABehavior: Lock3ABehaviorWhenCaptureImage by lazy {
+ Lock3ABehaviorWhenCaptureImage()
+ }
+
+ public val doNotLockAe3ABehavior: Lock3ABehaviorWhenCaptureImage by lazy {
+ Lock3ABehaviorWhenCaptureImage(
+ hasAeLockBehavior = true,
+ aeLockBehavior = null // Explicitly disable AE lock
+ )
+ }
+ }
+}
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CapturePipeline.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CapturePipeline.kt
index 372696c..1d21a90 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CapturePipeline.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CapturePipeline.kt
@@ -49,6 +49,7 @@
import androidx.camera.camera2.pipe.core.Log.debug
import androidx.camera.camera2.pipe.core.Log.info
import androidx.camera.camera2.pipe.integration.adapter.CaptureConfigAdapter
+import androidx.camera.camera2.pipe.integration.compat.workaround.Lock3ABehaviorWhenCaptureImage
import androidx.camera.camera2.pipe.integration.compat.workaround.UseTorchAsFlash
import androidx.camera.camera2.pipe.integration.compat.workaround.isFlashAvailable
import androidx.camera.camera2.pipe.integration.compat.workaround.shouldStopRepeatingBeforeCapture
@@ -110,6 +111,7 @@
private val threads: UseCaseThreads,
private val requestListener: ComboRequestListener,
private val useTorchAsFlash: UseTorchAsFlash,
+ private val lock3ABehaviorWhenCaptureImage: Lock3ABehaviorWhenCaptureImage,
cameraProperties: CameraProperties,
private val useCaseCameraState: UseCaseCameraState,
useCaseGraphConfig: UseCaseGraphConfig,
@@ -378,10 +380,16 @@
graph
.acquireSession()
.use {
+ val (aeLockBehavior, afLockBehavior, awbLockBehavior) =
+ lock3ABehaviorWhenCaptureImage.getLock3ABehaviors(
+ defaultAeBehavior = Lock3ABehavior.AFTER_CURRENT_SCAN,
+ defaultAfBehavior = Lock3ABehavior.AFTER_CURRENT_SCAN,
+ defaultAwbBehavior = Lock3ABehavior.AFTER_CURRENT_SCAN,
+ )
it.lock3A(
- aeLockBehavior = Lock3ABehavior.AFTER_CURRENT_SCAN,
- afLockBehavior = Lock3ABehavior.AFTER_CURRENT_SCAN,
- awbLockBehavior = Lock3ABehavior.AFTER_CURRENT_SCAN,
+ aeLockBehavior = aeLockBehavior,
+ afLockBehavior = afLockBehavior,
+ awbLockBehavior = awbLockBehavior,
convergedTimeLimitNs = convergedTimeLimitNs,
lockedTimeLimitNs = CHECK_3A_TIMEOUT_IN_NS
)
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraRequestControl.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraRequestControl.kt
index de128bc..0731bda 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraRequestControl.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraRequestControl.kt
@@ -354,9 +354,9 @@
runIfNotClosed {
useGraphSessionOrFailed {
it.update3A(
- aeRegions = METERING_REGIONS_DEFAULT.asList(),
- afRegions = METERING_REGIONS_DEFAULT.asList(),
- awbRegions = METERING_REGIONS_DEFAULT.asList()
+ aeRegions = aeRegions ?: METERING_REGIONS_DEFAULT.asList(),
+ afRegions = afRegions ?: METERING_REGIONS_DEFAULT.asList(),
+ awbRegions = awbRegions ?: METERING_REGIONS_DEFAULT.asList()
)
}
} ?: submitFailedResult
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 92be8fb..5d25a6d 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
@@ -1655,16 +1655,34 @@
}
}
+ @Config(minSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ @Test
+ fun checkUltraHdrCombinationsSupported_when8bit() {
+ // Device might support Ultra HDR but not 10-bit.
+ setupCamera(supportedFormats = intArrayOf(JPEG_R))
+ val supportedSurfaceCombination =
+ SupportedSurfaceCombination(context, fakeCameraMetadata, mockEncoderProfilesAdapter)
+
+ GuaranteedConfigurationsUtil.getUltraHdrSupportedCombinationList().forEach {
+ assertThat(
+ supportedSurfaceCombination.checkSupported(
+ SupportedSurfaceCombination.FeatureSettings(
+ CameraMode.DEFAULT,
+ requiredMaxBitDepth = DynamicRange.BIT_DEPTH_8_BIT,
+ isUltraHdrOn = true
+ ),
+ it.surfaceConfigList
+ )
+ )
+ .isTrue()
+ }
+ }
+
/** JPEG_R/MAXIMUM when Ultra HDR is ON. */
@Config(minSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@Test
fun canSelectCorrectSizes_onlyJpegr_whenUltraHdrIsOn() {
- val jpegUseCase =
- createUseCase(
- CaptureType.IMAGE_CAPTURE,
- dynamicRange = HLG_10_BIT,
- imageFormat = JPEG_R
- ) // JPEG
+ val jpegUseCase = createUseCase(CaptureType.IMAGE_CAPTURE, imageFormat = JPEG_R) // JPEG
val useCaseExpectedResultMap =
mutableMapOf<UseCase, Size>().apply { put(jpegUseCase, maximumSize) }
getSuggestedSpecsAndVerify(
@@ -1683,7 +1701,27 @@
val jpegUseCase =
createUseCase(
CaptureType.IMAGE_CAPTURE,
- dynamicRange = HLG_10_BIT,
+ imageFormat = JPEG_R,
+ ) // JPEG
+ val useCaseExpectedResultMap =
+ mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase, previewSize)
+ put(jpegUseCase, maximumSize)
+ }
+ getSuggestedSpecsAndVerify(
+ useCasesExpectedResultMap = useCaseExpectedResultMap,
+ supportedOutputFormats = intArrayOf(JPEG_R),
+ )
+ }
+
+ /** HLG10 PRIV/PREVIEW + JPEG_R/MAXIMUM when Ultra HDR is ON. */
+ @Config(minSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ @Test
+ fun canSelectCorrectSizes_hlg10PrivPlusJpegr_whenUltraHdrIsOn() {
+ val privUseCase = createUseCase(CaptureType.PREVIEW, dynamicRange = HLG_10_BIT) // PRIV
+ val jpegUseCase =
+ createUseCase(
+ CaptureType.IMAGE_CAPTURE,
imageFormat = JPEG_R,
) // JPEG
val useCaseExpectedResultMap =
@@ -1708,7 +1746,6 @@
val jpegUseCase =
createUseCase(
CaptureType.IMAGE_CAPTURE,
- dynamicRange = HLG_10_BIT,
imageFormat = JPEG_R,
) // JPEG
val useCaseExpectedResultMap =
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/StreamConfigurationMapCompatTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/StreamConfigurationMapCompatTest.kt
index 3e8cf9f..4b07606 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/StreamConfigurationMapCompatTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/StreamConfigurationMapCompatTest.kt
@@ -98,4 +98,22 @@
assertThat(streamConfigurationMapCompat.getHighResolutionOutputSizes(ImageFormat.JPEG))
.isNull()
}
+
+ @Test
+ fun getOutputFormats_notThrowingNullPointerException() {
+ val builder = StreamConfigurationMapBuilder.newBuilder()
+ val compat =
+ StreamConfigurationMapCompat(
+ builder.build(),
+ OutputSizesCorrector(FakeCameraMetadata(), builder.build())
+ )
+
+ // b/361590210: check the workaround for NullPointerException issue (on API 23+) of
+ // StreamConfigurationMap provided by Robolectric is applied.
+ if (Build.VERSION.SDK_INT >= 23) {
+ assertThat(compat.getOutputFormats()).isNull()
+ } else {
+ assertThat(compat.getOutputFormats()).isNotNull()
+ }
+ }
}
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/workaround/Lock3ABehaviorWhenCaptureImageTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/workaround/Lock3ABehaviorWhenCaptureImageTest.kt
new file mode 100644
index 0000000..206bca8
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/compat/workaround/Lock3ABehaviorWhenCaptureImageTest.kt
@@ -0,0 +1,92 @@
+/*
+ * 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.camera.camera2.pipe.integration.compat.workaround
+
+import androidx.camera.camera2.pipe.Lock3ABehavior
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.internal.DoNotInstrument
+
+@RunWith(RobolectricTestRunner::class)
+@DoNotInstrument
+class Lock3ABehaviorWhenCaptureImageTest {
+
+ @Test
+ fun getLock3ABehaviors_noCustomBehaviors_returnsDefaults() {
+ val lock3ABehavior = Lock3ABehaviorWhenCaptureImage()
+ val defaultAeBehavior = null
+ val defaultAfBehavior = Lock3ABehavior.AFTER_NEW_SCAN
+ val defaultAwbBehavior = Lock3ABehavior.AFTER_CURRENT_SCAN
+
+ val (ae, af, awb) =
+ lock3ABehavior.getLock3ABehaviors(
+ defaultAeBehavior,
+ defaultAfBehavior,
+ defaultAwbBehavior
+ )
+
+ assertThat(ae).isEqualTo(defaultAeBehavior)
+ assertThat(af).isEqualTo(defaultAfBehavior)
+ assertThat(awb).isEqualTo(defaultAwbBehavior)
+ }
+
+ @Test
+ fun getLock3ABehaviors_withCustomBehaviors_returnsCustom() {
+ val customAeBehavior = null
+ val customAfBehavior = Lock3ABehavior.AFTER_NEW_SCAN
+ val customAwbBehavior = Lock3ABehavior.AFTER_CURRENT_SCAN
+ val lock3ABehavior =
+ Lock3ABehaviorWhenCaptureImage(
+ hasAeLockBehavior = true,
+ aeLockBehavior = customAeBehavior,
+ hasAfLockBehavior = true,
+ afLockBehavior = customAfBehavior,
+ hasAwbLockBehavior = true,
+ awbLockBehavior = customAwbBehavior
+ )
+
+ val (ae, af, awb) = lock3ABehavior.getLock3ABehaviors() // No defaults needed
+
+ assertThat(ae).isEqualTo(customAeBehavior)
+ assertThat(af).isEqualTo(customAfBehavior)
+ assertThat(awb).isEqualTo(customAwbBehavior)
+ }
+
+ @Test
+ fun getLock3ABehaviors_mixedBehaviors_returnsCorrectly() {
+ val customAfBehavior = Lock3ABehavior.AFTER_NEW_SCAN
+ val lock3ABehavior =
+ Lock3ABehaviorWhenCaptureImage(
+ hasAfLockBehavior = true,
+ afLockBehavior = customAfBehavior
+ )
+ val defaultAeBehavior = null
+ val defaultAwbBehavior = Lock3ABehavior.AFTER_CURRENT_SCAN
+
+ val (ae, af, awb) =
+ lock3ABehavior.getLock3ABehaviors(
+ defaultAeBehavior,
+ defaultAwbBehavior = defaultAwbBehavior
+ )
+
+ assertThat(ae).isEqualTo(defaultAeBehavior) // Default used
+ assertThat(af).isEqualTo(customAfBehavior) // Custom used
+ assertThat(awb).isEqualTo(defaultAwbBehavior) // Default used
+ }
+}
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/CapturePipelineTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/CapturePipelineTest.kt
index 35fb7e0..f968130 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/CapturePipelineTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/CapturePipelineTest.kt
@@ -48,11 +48,15 @@
import androidx.camera.camera2.pipe.integration.compat.quirk.CameraQuirks
import androidx.camera.camera2.pipe.integration.compat.workaround.AeFpsRange
import androidx.camera.camera2.pipe.integration.compat.workaround.CapturePipelineTorchCorrection
+import androidx.camera.camera2.pipe.integration.compat.workaround.Lock3ABehaviorWhenCaptureImage
+import androidx.camera.camera2.pipe.integration.compat.workaround.Lock3ABehaviorWhenCaptureImage.Companion.doNotLockAe3ABehavior
+import androidx.camera.camera2.pipe.integration.compat.workaround.Lock3ABehaviorWhenCaptureImage.Companion.noCustomizedLock3ABehavior
import androidx.camera.camera2.pipe.integration.compat.workaround.NoOpAutoFlashAEModeDisabler
import androidx.camera.camera2.pipe.integration.compat.workaround.NoOpTemplateParamsOverride
import androidx.camera.camera2.pipe.integration.compat.workaround.NotUseFlashModeTorchFor3aUpdate
import androidx.camera.camera2.pipe.integration.compat.workaround.NotUseTorchAsFlash
import androidx.camera.camera2.pipe.integration.compat.workaround.OutputSizesCorrector
+import androidx.camera.camera2.pipe.integration.compat.workaround.UseTorchAsFlash
import androidx.camera.camera2.pipe.integration.compat.workaround.UseTorchAsFlashImpl
import androidx.camera.camera2.pipe.integration.config.UseCaseGraphConfig
import androidx.camera.camera2.pipe.integration.interop.CaptureRequestOptions
@@ -153,6 +157,9 @@
var waitForAwbAtLock3AForCapture: Boolean = false
var cancelAfAtUnlock3AForCapture: Boolean = false
+ var aeLockBehavior: Lock3ABehavior? = null
+ var afLockBehavior: Lock3ABehavior? = null
+ var awbLockBehavior: Lock3ABehavior? = null
override suspend fun lock3A(
aeMode: AeMode?,
@@ -171,6 +178,9 @@
convergedTimeLimitNs: Long,
lockedTimeLimitNs: Long
): Deferred<Result3A> {
+ this.aeLockBehavior = aeLockBehavior
+ this.afLockBehavior = afLockBehavior
+ this.awbLockBehavior = awbLockBehavior
lock3ASemaphore.release()
return CompletableDeferred(Result3A(Result3A.Status.OK))
}
@@ -359,19 +369,7 @@
templateParamsOverride = NoOpTemplateParamsOverride,
)
- capturePipeline =
- CapturePipelineImpl(
- configAdapter = fakeCaptureConfigAdapter,
- cameraProperties = fakeCameraProperties,
- requestListener = comboRequestListener,
- threads = fakeUseCaseThreads,
- torchControl = torchControl,
- useCaseGraphConfig = fakeUseCaseGraphConfig,
- useCaseCameraState = fakeUseCaseCameraState,
- useTorchAsFlash = NotUseTorchAsFlash,
- sessionProcessorManager = null,
- flashControl = flashControl,
- )
+ capturePipeline = createCapturePipeline()
}
@After
@@ -475,19 +473,7 @@
private suspend fun TestScope.withTorchAsFlashQuirk_shouldOpenTorch(imageCaptureMode: Int) {
// Arrange.
- capturePipeline =
- CapturePipelineImpl(
- configAdapter = fakeCaptureConfigAdapter,
- cameraProperties = fakeCameraProperties,
- requestListener = comboRequestListener,
- threads = fakeUseCaseThreads,
- torchControl = torchControl,
- useCaseGraphConfig = fakeUseCaseGraphConfig,
- useCaseCameraState = fakeUseCaseCameraState,
- useTorchAsFlash = UseTorchAsFlashImpl,
- sessionProcessorManager = null,
- flashControl = flashControl,
- )
+ capturePipeline = createCapturePipeline(useTorchAsFlash = UseTorchAsFlashImpl)
val requestList = mutableListOf<Request>()
fakeCameraGraphSession.requestHandler = { requests -> requestList.addAll(requests) }
@@ -602,22 +588,69 @@
@Test
fun miniLatency_flashRequired_withFlashTypeTorch_shouldLock3A(): Unit = runTest {
withFlashTypeTorch_shouldLock3A(
+ capturePipeline,
ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY,
- ImageCapture.FLASH_MODE_ON
+ ImageCapture.FLASH_MODE_ON,
+ expectedLock3ABehaviors =
+ Triple(
+ Lock3ABehavior.AFTER_CURRENT_SCAN,
+ Lock3ABehavior.AFTER_CURRENT_SCAN,
+ Lock3ABehavior.AFTER_CURRENT_SCAN
+ )
)
}
@Test
+ fun miniLatency_flashRequired_withFlashTypeTorch_doNotLockAe3ABehavior_shouldLock3A(): Unit =
+ runTest {
+ val capturePipeline =
+ createCapturePipeline(lock3ABehaviorWhenCaptureImage = doNotLockAe3ABehavior)
+ withFlashTypeTorch_shouldLock3A(
+ capturePipeline,
+ ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY,
+ ImageCapture.FLASH_MODE_ON,
+ expectedLock3ABehaviors =
+ Triple(
+ null,
+ Lock3ABehavior.AFTER_CURRENT_SCAN,
+ Lock3ABehavior.AFTER_CURRENT_SCAN
+ )
+ )
+ }
+
+ @Test
fun maxQuality_withFlashTypeTorch_shouldLock3A(): Unit = runTest {
withFlashTypeTorch_shouldLock3A(
+ capturePipeline,
ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY,
- ImageCapture.FLASH_MODE_OFF
+ ImageCapture.FLASH_MODE_OFF,
+ expectedLock3ABehaviors =
+ Triple(
+ Lock3ABehavior.AFTER_CURRENT_SCAN,
+ Lock3ABehavior.AFTER_CURRENT_SCAN,
+ Lock3ABehavior.AFTER_CURRENT_SCAN
+ )
+ )
+ }
+
+ @Test
+ fun maxQuality_withFlashTypeTorch_doNotLockAe3ABehavior_shouldLock3A(): Unit = runTest {
+ val capturePipeline =
+ createCapturePipeline(lock3ABehaviorWhenCaptureImage = doNotLockAe3ABehavior)
+ withFlashTypeTorch_shouldLock3A(
+ capturePipeline,
+ ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY,
+ ImageCapture.FLASH_MODE_OFF,
+ expectedLock3ABehaviors =
+ Triple(null, Lock3ABehavior.AFTER_CURRENT_SCAN, Lock3ABehavior.AFTER_CURRENT_SCAN)
)
}
private suspend fun TestScope.withFlashTypeTorch_shouldLock3A(
+ capturePipeline: CapturePipeline,
imageCaptureMode: Int,
- flashMode: Int
+ flashMode: Int,
+ expectedLock3ABehaviors: Triple<Lock3ABehavior?, Lock3ABehavior?, Lock3ABehavior?>,
) {
// Arrange.
val requestList = mutableListOf<Request>()
@@ -636,6 +669,10 @@
// Assert 1, should call lock3A, but not call unlock3A (before capturing is finished).
assertThat(fakeCameraGraphSession.lock3ASemaphore.tryAcquire(this)).isTrue()
assertThat(fakeCameraGraphSession.unlock3ASemaphore.tryAcquire(this)).isFalse()
+ // Ensure correct Lock3ABehaviors are set.
+ assertThat(fakeCameraGraphSession.aeLockBehavior).isEqualTo(expectedLock3ABehaviors.first)
+ assertThat(fakeCameraGraphSession.afLockBehavior).isEqualTo(expectedLock3ABehaviors.second)
+ assertThat(fakeCameraGraphSession.awbLockBehavior).isEqualTo(expectedLock3ABehaviors.third)
// Complete the capture request.
assertThat(fakeCameraGraphSession.submitSemaphore.tryAcquire(this)).isTrue()
@@ -1221,6 +1258,24 @@
assertThat(screenFlash.awaitClear(3000)).isTrue()
}
+ private fun createCapturePipeline(
+ useTorchAsFlash: UseTorchAsFlash = NotUseTorchAsFlash,
+ lock3ABehaviorWhenCaptureImage: Lock3ABehaviorWhenCaptureImage = noCustomizedLock3ABehavior
+ ) =
+ CapturePipelineImpl(
+ configAdapter = fakeCaptureConfigAdapter,
+ cameraProperties = fakeCameraProperties,
+ requestListener = comboRequestListener,
+ threads = fakeUseCaseThreads,
+ torchControl = torchControl,
+ useCaseGraphConfig = fakeUseCaseGraphConfig,
+ useCaseCameraState = fakeUseCaseCameraState,
+ useTorchAsFlash = useTorchAsFlash,
+ lock3ABehaviorWhenCaptureImage = lock3ABehaviorWhenCaptureImage,
+ sessionProcessorManager = null,
+ flashControl = flashControl,
+ )
+
// TODO(wenhungteng@): Porting overrideAeModeForStillCapture_quirkAbsent_notOverride,
// overrideAeModeForStillCapture_aePrecaptureStarted_override,
// overrideAeModeForStillCapture_aePrecaptureFinish_notOverride,
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/StillCaptureRequestTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/StillCaptureRequestTest.kt
index a62aef8..5698804 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/StillCaptureRequestTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/StillCaptureRequestTest.kt
@@ -23,6 +23,7 @@
import androidx.camera.camera2.pipe.integration.adapter.CaptureConfigAdapter
import androidx.camera.camera2.pipe.integration.adapter.RobolectricCameraPipeTestRunner
import androidx.camera.camera2.pipe.integration.adapter.ZslControlNoOpImpl
+import androidx.camera.camera2.pipe.integration.compat.workaround.Lock3ABehaviorWhenCaptureImage.Companion.noCustomizedLock3ABehavior
import androidx.camera.camera2.pipe.integration.compat.workaround.NoOpTemplateParamsOverride
import androidx.camera.camera2.pipe.integration.compat.workaround.NotUseFlashModeTorchFor3aUpdate
import androidx.camera.camera2.pipe.integration.compat.workaround.NotUseTorchAsFlash
@@ -435,6 +436,7 @@
useCaseGraphConfig = fakeUseCaseGraphConfig,
useCaseCameraState = fakeUseCaseCameraState,
useTorchAsFlash = NotUseTorchAsFlash,
+ lock3ABehaviorWhenCaptureImage = noCustomizedLock3ABehavior,
sessionProcessorManager = null,
flashControl =
FlashControl(
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 2f89dcf..630a50a 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
@@ -182,10 +182,10 @@
if (mDynamicRangeResolver.is10BitDynamicRangeSupported()) {
generate10BitSupportedCombinationList();
+ }
- if (isUltraHdrSupported()) {
- generateUltraHdrSupportedCombinationList();
- }
+ if (isUltraHdrSupported()) {
+ generateUltraHdrSupportedCombinationList();
}
mIsStreamUseCaseSupported = StreamUseCaseUtil.isStreamUseCaseSupported(mCharacteristics);
@@ -288,7 +288,12 @@
List<SurfaceCombination> supportedSurfaceCombinations = new ArrayList<>();
- if (featureSettings.getRequiredMaxBitDepth() == DynamicRange.BIT_DEPTH_8_BIT) {
+ if (featureSettings.isUltraHdrOn()) {
+ // For Ultra HDR output, only the default camera mode is currently supported.
+ if (featureSettings.getCameraMode() == CameraMode.DEFAULT) {
+ supportedSurfaceCombinations.addAll(mSurfaceCombinationsUltraHdr);
+ }
+ } else if (featureSettings.getRequiredMaxBitDepth() == DynamicRange.BIT_DEPTH_8_BIT) {
switch (featureSettings.getCameraMode()) {
case CameraMode.CONCURRENT_CAMERA:
supportedSurfaceCombinations = mConcurrentSurfaceCombinations;
@@ -305,11 +310,7 @@
} else if (featureSettings.getRequiredMaxBitDepth() == DynamicRange.BIT_DEPTH_10_BIT) {
// For 10-bit outputs, only the default camera mode is currently supported.
if (featureSettings.getCameraMode() == CameraMode.DEFAULT) {
- if (featureSettings.isUltraHdrOn()) {
- supportedSurfaceCombinations.addAll(mSurfaceCombinationsUltraHdr);
- } else {
- supportedSurfaceCombinations.addAll(mSurfaceCombinations10Bit);
- }
+ supportedSurfaceCombinations.addAll(mSurfaceCombinations10Bit);
}
}
@@ -865,6 +866,13 @@
boolean isPreviewStabilizationOn, boolean isUltraHdrOn) {
int requiredMaxBitDepth = getRequiredMaxBitDepth(resolvedDynamicRanges);
+ if (cameraMode != CameraMode.DEFAULT && isUltraHdrOn) {
+ throw new IllegalArgumentException(String.format("Camera device id is %s. Ultra HDR "
+ + "is not currently supported in %s camera mode.",
+ mCameraId,
+ CameraMode.toLabelString(cameraMode)));
+ }
+
if (cameraMode != CameraMode.DEFAULT
&& requiredMaxBitDepth == DynamicRange.BIT_DEPTH_10_BIT) {
throw new IllegalArgumentException(String.format("Camera device id is %s. 10 bit "
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/StreamConfigurationMapCompatBaseImpl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/StreamConfigurationMapCompatBaseImpl.java
index 3b97170..ec4cc78 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/StreamConfigurationMapCompatBaseImpl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/StreamConfigurationMapCompatBaseImpl.java
@@ -24,11 +24,14 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
+import androidx.camera.core.Logger;
import androidx.camera.core.impl.ImageFormatConstants;
class StreamConfigurationMapCompatBaseImpl
implements StreamConfigurationMapCompat.StreamConfigurationMapCompatImpl {
+ private static final String TAG = "StreamConfigurationMapCompatBaseImpl";
+
final StreamConfigurationMap mStreamConfigurationMap;
StreamConfigurationMapCompatBaseImpl(@NonNull StreamConfigurationMap map) {
@@ -38,7 +41,14 @@
@Nullable
@Override
public int[] getOutputFormats() {
- return mStreamConfigurationMap.getOutputFormats();
+ // b/361590210: try-catch to workaround the NullPointerException issue when using
+ // StreamConfigurationMap provided by Robolectric.
+ try {
+ return mStreamConfigurationMap.getOutputFormats();
+ } catch (NullPointerException e) {
+ Logger.e(TAG, "Failed to get output formats from StreamConfigurationMap", e);
+ return null;
+ }
}
@Nullable
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 b4059b9..d665ba5 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
@@ -1739,6 +1739,7 @@
// Resolution selection tests for Ultra HDR
//
// //////////////////////////////////////////////////////////////////////////////////////////
+
@Config(minSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@Test
fun checkUltraHdrCombinationsSupported() {
@@ -1768,6 +1769,33 @@
}
}
+ @Config(minSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ @Test
+ fun checkUltraHdrCombinationsSupported_when8bit() {
+ // Device might support Ultra HDR but not 10-bit.
+ setupCameraAndInitCameraX(supportedFormats = intArrayOf(JPEG_R))
+ val supportedSurfaceCombination =
+ SupportedSurfaceCombination(
+ context,
+ DEFAULT_CAMERA_ID,
+ cameraManagerCompat!!,
+ mockCamcorderProfileHelper
+ )
+
+ GuaranteedConfigurationsUtil.getUltraHdrSupportedCombinationList().forEach {
+ assertThat(
+ supportedSurfaceCombination.checkSupported(
+ createFeatureSettings(
+ requiredMaxBitDepth = BIT_DEPTH_8_BIT,
+ isUltraHdrOn = true
+ ),
+ it.surfaceConfigList
+ )
+ )
+ .isTrue()
+ }
+ }
+
/** JPEG_R/MAXIMUM when Ultra HDR is ON. */
@Config(minSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@Test
@@ -1775,7 +1803,6 @@
val jpegUseCase =
createUseCase(
CaptureType.IMAGE_CAPTURE,
- dynamicRange = HLG_10_BIT,
imageFormat = JPEG_R,
) // JPEG
val useCaseExpectedResultMap =
@@ -1796,7 +1823,27 @@
val jpegUseCase =
createUseCase(
CaptureType.IMAGE_CAPTURE,
- dynamicRange = HLG_10_BIT,
+ imageFormat = JPEG_R,
+ ) // JPEG
+ val useCaseExpectedResultMap =
+ mutableMapOf<UseCase, Size>().apply {
+ put(privUseCase, PREVIEW_SIZE)
+ put(jpegUseCase, MAXIMUM_SIZE)
+ }
+ getSuggestedSpecsAndVerify(
+ useCasesExpectedSizeMap = useCaseExpectedResultMap,
+ supportedOutputFormats = intArrayOf(JPEG_R),
+ )
+ }
+
+ /** HLG10 PRIV/PREVIEW + JPEG_R/MAXIMUM when Ultra HDR is ON. */
+ @Config(minSdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ @Test
+ fun canSelectCorrectSizes_hlg10PrivPlusJpegr_whenUltraHdrIsOn() {
+ val privUseCase = createUseCase(CaptureType.PREVIEW, dynamicRange = HLG_10_BIT) // PRIV
+ val jpegUseCase =
+ createUseCase(
+ CaptureType.IMAGE_CAPTURE,
imageFormat = JPEG_R,
) // JPEG
val useCaseExpectedResultMap =
@@ -1821,7 +1868,6 @@
val jpegUseCase =
createUseCase(
CaptureType.IMAGE_CAPTURE,
- dynamicRange = HLG_10_BIT,
imageFormat = JPEG_R,
) // JPEG
val useCaseExpectedResultMap =
@@ -3489,10 +3535,8 @@
set(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL, hardwareLevel)
set(CameraCharacteristics.SENSOR_ORIENTATION, sensorOrientation)
set(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE, pixelArraySize)
- // Only setup stream configuration map when the supported output sizes are specified.
- supportedSizes?.let {
- set(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP, mockMap)
- }
+ // Setup stream configuration map, no matter supported output sizes are specified.
+ set(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP, mockMap)
set(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES, deviceFPSRanges)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
dynamicRangeProfiles?.let {
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/compat/StreamConfigurationMapCompatTest.kt b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/compat/StreamConfigurationMapCompatTest.kt
index 29f150c..56f5d54 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/compat/StreamConfigurationMapCompatTest.kt
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/internal/compat/StreamConfigurationMapCompatTest.kt
@@ -100,4 +100,22 @@
assertThat(streamConfigurationMapCompat.getHighResolutionOutputSizes(ImageFormat.JPEG))
.isNull()
}
+
+ @Test
+ fun getOutputFormats_notThrowingNullPointerException() {
+ val cameraId = "0"
+ val compat =
+ StreamConfigurationMapCompat.toStreamConfigurationMapCompat(
+ StreamConfigurationMapBuilder.newBuilder().build(),
+ OutputSizesCorrector(cameraId)
+ )
+
+ // b/361590210: check the workaround for NullPointerException issue (on API 23+) of
+ // StreamConfigurationMap provided by Robolectric is applied.
+ if (Build.VERSION.SDK_INT >= 23) {
+ assertThat(compat.getOutputFormats()).isNull()
+ } else {
+ assertThat(compat.getOutputFormats()).isNotNull()
+ }
+ }
}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
index 5c15ced..20b2ecd 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
@@ -19,7 +19,6 @@
import static android.graphics.ImageFormat.JPEG_R;
import static androidx.camera.core.CameraEffect.IMAGE_CAPTURE;
-import static androidx.camera.core.DynamicRange.HDR_UNSPECIFIED_10_BIT;
import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_BUFFER_FORMAT;
import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_CAPTURE_CONFIG_UNPACKER;
import static androidx.camera.core.impl.ImageCaptureConfig.OPTION_DEFAULT_CAPTURE_CONFIG;
@@ -472,7 +471,7 @@
if (isOutputFormatUltraHdr(builder.getMutableConfig())) {
builder.getMutableConfig().insertOption(OPTION_INPUT_FORMAT, JPEG_R);
builder.getMutableConfig().insertOption(OPTION_INPUT_DYNAMIC_RANGE,
- HDR_UNSPECIFIED_10_BIT);
+ DynamicRange.UNSPECIFIED);
} else if (useSoftwareJpeg) {
builder.getMutableConfig().insertOption(OPTION_INPUT_FORMAT,
ImageFormat.YUV_420_888);
@@ -2323,7 +2322,7 @@
if (isOutputFormatUltraHdr(getMutableConfig())) {
getMutableConfig().insertOption(OPTION_INPUT_FORMAT, JPEG_R);
getMutableConfig().insertOption(OPTION_INPUT_DYNAMIC_RANGE,
- HDR_UNSPECIFIED_10_BIT);
+ DynamicRange.UNSPECIFIED);
} else {
getMutableConfig().insertOption(OPTION_INPUT_FORMAT, ImageFormat.JPEG);
}
@@ -2829,14 +2828,6 @@
*
* <p>If not set, the output format will default to {@link #OUTPUT_FORMAT_JPEG}.
*
- * <p>If an Ultra HDR output format is used, a {@link DynamicRange#HDR_UNSPECIFIED_10_BIT}
- * will be used as the dynamic range of this use case. Please note that some devices may not
- * be able to support configuring both SDR and HDR use cases at the same time, e.g. use
- * Ultra HDR ImageCapture with a SDR Preview. Configuring concurrent SDR and HDR on these
- * devices will result in an {@link IllegalArgumentException} to be thrown when invoking
- * {@code bindToLifecycle()}. Such device specific constraints can be queried by calling
- * {@link android.hardware.camera2.params.DynamicRangeProfiles#getProfileCaptureRequestConstraints(long)}.
- *
* @param outputFormat The output image format. Value is {@link #OUTPUT_FORMAT_JPEG} or
* {@link #OUTPUT_FORMAT_JPEG_ULTRA_HDR}.
* @return The current Builder.
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java b/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java
index 541a169..dc922ee 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/internal/CameraUseCaseAdapter.java
@@ -1031,9 +1031,16 @@
// TODO(b/309900490): since there are other places (e.g. SupportedSurfaceCombination in
// camera2) that feature combination constraints are enforced, it would be nice if they
// followed a similar pattern for checking constraints.
- if (hasExtension() && hasNonSdrConfig(useCases)) {
- throw new IllegalArgumentException("Extensions are only supported for use with "
- + "standard dynamic range.");
+ if (hasExtension()) {
+ if (hasNonSdrConfig(useCases)) {
+ throw new IllegalArgumentException("Extensions are only supported for use with "
+ + "standard dynamic range.");
+ }
+
+ if (hasUltraHdrImageCapture(useCases)) {
+ throw new IllegalArgumentException("Extensions are not supported for use with "
+ + "Ultra HDR image capture.");
+ }
}
// TODO(b/322311893): throw exception to block feature combination of effect with Ultra
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeImageCaptureCallback.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeOnImageCapturedCallback.kt
similarity index 64%
rename from camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeImageCaptureCallback.kt
rename to camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeOnImageCapturedCallback.kt
index b600a40..135f64c 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeImageCaptureCallback.kt
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeOnImageCapturedCallback.kt
@@ -25,14 +25,25 @@
import androidx.camera.core.impl.utils.Exif
import com.google.common.truth.Truth
import java.io.ByteArrayInputStream
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.withTimeoutOrNull
-private const val CAPTURE_TIMEOUT = 15_000.toLong() // 15 seconds
+/**
+ * A fake implementation of the [ImageCapture.OnImageCapturedCallback] that is used for test.
+ *
+ * @param captureCount Number of captures to wait for.
+ * @property closeImageOnSuccess Whether to close images immediately on [onCaptureSuccess]
+ * callbacks. This is true by default. If set to false, it is the user's responsibility to close
+ * the images.
+ */
+public class FakeOnImageCapturedCallback(
+ captureCount: Int = 1,
+ private val closeImageOnSuccess: Boolean = true
+) : ImageCapture.OnImageCapturedCallback() {
+ public data class CapturedImage(val image: ImageProxy, val properties: ImageProperties)
-/** A fake implementation of the [ImageCapture.OnImageCapturedCallback] and used for test. */
-public class FakeImageCaptureCallback(captureCount: Int = 1) :
- ImageCapture.OnImageCapturedCallback() {
/** Data class of various image properties which are tested. */
public data class ImageProperties(
val size: Size? = null,
@@ -43,20 +54,34 @@
)
private val latch = CountdownDeferred(captureCount)
- public val results: MutableList<ImageProperties> = mutableListOf()
+
+ /**
+ * List of [CapturedImage] obtained in [onCaptureSuccess] callback.
+ *
+ * If [closeImageOnSuccess] is true, the [CapturedImage.image] will be closed as soon as
+ * `onCaptureSuccess` is invoked. Otherwise, it will be the user's responsibility to close the
+ * images.
+ */
+ public val results: MutableList<CapturedImage> = mutableListOf()
public val errors: MutableList<ImageCaptureException> = mutableListOf()
override fun onCaptureSuccess(image: ImageProxy) {
results.add(
- ImageProperties(
- size = Size(image.width, image.height),
- format = image.format,
- rotationDegrees = image.imageInfo.rotationDegrees,
- cropRect = image.cropRect,
- exif = getExif(image),
+ CapturedImage(
+ image = image,
+ properties =
+ ImageProperties(
+ size = Size(image.width, image.height),
+ format = image.format,
+ rotationDegrees = image.imageInfo.rotationDegrees,
+ cropRect = image.cropRect,
+ exif = getExif(image),
+ )
)
)
- image.close()
+ if (closeImageOnSuccess) {
+ image.close()
+ }
latch.countDown()
}
@@ -76,12 +101,12 @@
return null
}
- public suspend fun awaitCaptures(timeout: Long = CAPTURE_TIMEOUT) {
+ public suspend fun awaitCaptures(timeout: Duration = CAPTURE_TIMEOUT) {
Truth.assertThat(withTimeoutOrNull(timeout) { latch.await() }).isNotNull()
}
public suspend fun awaitCapturesAndAssert(
- timeout: Long = CAPTURE_TIMEOUT,
+ timeout: Duration = CAPTURE_TIMEOUT,
capturedImagesCount: Int = 0,
errorsCount: Int = 0
) {
@@ -110,4 +135,8 @@
deferredItems.forEach { it.await() }
}
}
+
+ public companion object {
+ private val CAPTURE_TIMEOUT = 15.seconds
+ }
}
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeOnImageSavedCallback.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeOnImageSavedCallback.kt
new file mode 100644
index 0000000..4ddec70
--- /dev/null
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeOnImageSavedCallback.kt
@@ -0,0 +1,85 @@
+/*
+ * 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.camera.testing.impl.fakes
+
+import androidx.camera.core.ImageCapture
+import androidx.camera.core.ImageCaptureException
+import com.google.common.truth.Truth
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.withTimeoutOrNull
+
+/**
+ * A fake implementation of the [ImageCapture.OnImageCapturedCallback] that is used for test.
+ *
+ * @param captureCount Number of captures to wait for.
+ */
+public class FakeOnImageSavedCallback(captureCount: Int = 1) : ImageCapture.OnImageSavedCallback {
+ private val latch = CountdownDeferred(captureCount)
+ public val results: MutableList<ImageCapture.OutputFileResults> = mutableListOf()
+ public val errors: MutableList<ImageCaptureException> = mutableListOf()
+
+ override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
+ results.add(outputFileResults)
+ latch.countDown()
+ }
+
+ override fun onError(exception: ImageCaptureException) {
+ errors.add(exception)
+ latch.countDown()
+ }
+
+ public suspend fun awaitCaptures(timeout: Duration = CAPTURE_TIMEOUT) {
+ Truth.assertThat(withTimeoutOrNull(timeout) { latch.await() }).isNotNull()
+ }
+
+ public suspend fun awaitCapturesAndAssert(
+ timeout: Duration = CAPTURE_TIMEOUT,
+ capturedImagesCount: Int = 0,
+ errorsCount: Int = 0
+ ) {
+ Truth.assertThat(withTimeoutOrNull(timeout) { latch.await() }).isNotNull()
+ Truth.assertThat(results.size).isEqualTo(capturedImagesCount)
+ Truth.assertThat(errors.size).isEqualTo(errorsCount)
+ }
+
+ private class CountdownDeferred(val count: Int) {
+
+ private val deferredItems =
+ mutableListOf<CompletableDeferred<Unit>>().apply {
+ repeat(count) { add(CompletableDeferred()) }
+ }
+ private var index = 0
+
+ fun countDown() {
+ if (index < count) {
+ deferredItems[index++].complete(Unit)
+ } else {
+ throw IllegalStateException("Countdown already finished")
+ }
+ }
+
+ suspend fun await() {
+ deferredItems.forEach { it.await() }
+ }
+ }
+
+ public companion object {
+ private val CAPTURE_TIMEOUT = 15.seconds
+ }
+}
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageCaptureTest.kt b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageCaptureTest.kt
index 346e8e9..4c1e375 100644
--- a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageCaptureTest.kt
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageCaptureTest.kt
@@ -84,8 +84,8 @@
import androidx.camera.testing.impl.CoreAppTestUtil
import androidx.camera.testing.impl.SurfaceTextureProvider
import androidx.camera.testing.impl.WakelockEmptyActivityRule
-import androidx.camera.testing.impl.fakes.FakeImageCaptureCallback
import androidx.camera.testing.impl.fakes.FakeLifecycleOwner
+import androidx.camera.testing.impl.fakes.FakeOnImageCapturedCallback
import androidx.camera.testing.impl.fakes.FakeSessionProcessor
import androidx.camera.testing.impl.mocks.MockScreenFlash
import androidx.camera.video.Recorder
@@ -103,6 +103,8 @@
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicInteger
import kotlin.math.abs
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers
@@ -126,7 +128,7 @@
private val BACK_SELECTOR = CameraSelector.DEFAULT_BACK_CAMERA
private val FRONT_SELECTOR = CameraSelector.DEFAULT_FRONT_CAMERA
private const val BACK_LENS_FACING = CameraSelector.LENS_FACING_BACK
-private const val CAPTURE_TIMEOUT = 15_000.toLong() // 15 seconds
+private val CAPTURE_TIMEOUT = 15.seconds
private const val TOLERANCE = 1e-3f
private val EXIF_GAINMAP_PATTERNS =
listOf(
@@ -231,14 +233,14 @@
}
// Act.
- val callback = FakeImageCaptureCallback(captureCount = 1)
+ val callback = FakeOnImageCapturedCallback(captureCount = 1)
useCase.takePicture(mainExecutor, callback)
// Assert.
// Wait for the signal that the image has been captured.
callback.awaitCapturesAndAssert(capturedImagesCount = 1)
- val imageProperties = callback.results.first()
+ val imageProperties = callback.results.first().properties
var sizeEnvelope = imageProperties.size
// Some devices may not be able to fit the requested resolution. In this case, the returned
@@ -311,13 +313,13 @@
if (camera.cameraInfo.isZslSupported) {
val numImages = 5
- val callback = FakeImageCaptureCallback(captureCount = numImages)
+ val callback = FakeOnImageCapturedCallback(captureCount = numImages)
for (i in 0 until numImages) {
useCase.takePicture(mainExecutor, callback)
}
callback.awaitCapturesAndAssert(
- timeout = numImages * CAPTURE_TIMEOUT,
+ timeout = CAPTURE_TIMEOUT.times(numImages),
capturedImagesCount = numImages
)
}
@@ -401,12 +403,12 @@
}
// Act.
- val callback = FakeImageCaptureCallback(captureCount = numImages)
+ val callback = FakeOnImageCapturedCallback(captureCount = numImages)
repeat(numImages) { useCase.takePicture(mainExecutor, callback) }
// Assert.
callback.awaitCapturesAndAssert(
- timeout = numImages * CAPTURE_TIMEOUT,
+ timeout = CAPTURE_TIMEOUT.times(numImages),
capturedImagesCount = numImages
)
}
@@ -463,7 +465,7 @@
}
// Act.
- val callback = FakeImageCaptureCallback()
+ val callback = FakeOnImageCapturedCallback()
imageCapture.takePicture(mainExecutor, callback)
// Assert.
@@ -823,7 +825,7 @@
// Wait for the signal that all the images have been saved.
callback.awaitCapturesAndAssert(
- timeout = numImages * CAPTURE_TIMEOUT,
+ timeout = CAPTURE_TIMEOUT.times(numImages),
savedImagesCount = numImages
)
}
@@ -882,7 +884,7 @@
cameraProvider.bindToLifecycle(fakeLifecycleOwner, BACK_SELECTOR, useCase)
}
- val callback = FakeImageCaptureCallback(captureCount = 1)
+ val callback = FakeOnImageCapturedCallback(captureCount = 1)
useCase.takePicture(mainExecutor, callback)
@@ -913,14 +915,14 @@
cameraProvider.bindToLifecycle(fakeLifecycleOwner, BACK_SELECTOR, useCase)
}
- val callback = FakeImageCaptureCallback(captureCount = 1)
+ val callback = FakeOnImageCapturedCallback(captureCount = 1)
useCase.takePicture(mainExecutor, callback)
// Wait for the signal that the image has been captured.
callback.awaitCapturesAndAssert(capturedImagesCount = 1)
- val imageProperties = callback.results.first()
+ val imageProperties = callback.results.first().properties
assertThat(imageProperties.format).isEqualTo(ImageFormat.RAW10)
}
@@ -1037,13 +1039,13 @@
// directly know onStateAttached() callback has been received. Therefore, taking a
// picture and waiting for the capture success callback to know the use case's
// onStateAttached() callback has been received.
- val callback = FakeImageCaptureCallback(captureCount = 1)
+ val callback = FakeOnImageCapturedCallback(captureCount = 1)
imageCapture.takePicture(mainExecutor, callback)
// Wait for the signal that the image has been captured.
callback.awaitCapturesAndAssert(capturedImagesCount = 1)
- val callback2 = FakeImageCaptureCallback(captureCount = 3)
+ val callback2 = FakeOnImageCapturedCallback(captureCount = 3)
imageCapture.takePicture(mainExecutor, callback2)
imageCapture.takePicture(mainExecutor, callback2)
imageCapture.takePicture(mainExecutor, callback2)
@@ -1066,7 +1068,7 @@
cameraProvider.bindToLifecycle(fakeLifecycleOwner, BACK_SELECTOR, imageCapture)
}
- val callback = FakeImageCaptureCallback(captureCount = 3)
+ val callback = FakeOnImageCapturedCallback(captureCount = 3)
imageCapture.takePicture(mainExecutor, callback)
imageCapture.takePicture(mainExecutor, callback)
imageCapture.takePicture(mainExecutor, callback)
@@ -1094,7 +1096,7 @@
@Test
fun takePictureReturnsErrorNO_CAMERA_whenNotBound() = runBlocking {
val imageCapture = ImageCapture.Builder().build()
- val callback = FakeImageCaptureCallback(captureCount = 1)
+ val callback = FakeOnImageCapturedCallback(captureCount = 1)
imageCapture.takePicture(mainExecutor, callback)
@@ -1243,7 +1245,7 @@
cameraProvider.bindToLifecycle(fakeLifecycleOwner, BACK_SELECTOR, useCase)
}
- val callback = FakeImageCaptureCallback(captureCount = 1)
+ val callback = FakeOnImageCapturedCallback(captureCount = 1)
useCase.takePicture(mainExecutor, callback)
// Wait for the signal that the image has been captured.
@@ -1253,7 +1255,7 @@
// same as original one.
val expectedCroppingRatio = Rational(DEFAULT_RESOLUTION.width, DEFAULT_RESOLUTION.height)
- val imageProperties = callback.results.first()
+ val imageProperties = callback.results.first().properties
val cropRect = imageProperties.cropRect
// Rotate the captured ImageProxy's crop rect into the coordinate space of the final
@@ -1280,7 +1282,7 @@
cameraProvider.bindToLifecycle(fakeLifecycleOwner, BACK_SELECTOR, useCase)
}
- val callback = FakeImageCaptureCallback(captureCount = 1)
+ val callback = FakeOnImageCapturedCallback(captureCount = 1)
// Checks camera device sensor degrees to set target cropping aspect ratio match the
// sensor orientation.
@@ -1298,7 +1300,7 @@
// After target rotation is updated, the result cropping aspect ratio should still the
// same as original one.
- val imageProperties = callback.results.first()
+ val imageProperties = callback.results.first().properties
val cropRect = imageProperties.cropRect
// Rotate the captured ImageProxy's crop rect into the coordinate space of the final
@@ -1353,7 +1355,7 @@
cameraProvider.bindToLifecycle(fakeLifecycleOwner, BACK_SELECTOR, useCaseGroup)
}
- val callback = FakeImageCaptureCallback(captureCount = 1)
+ val callback = FakeOnImageCapturedCallback(captureCount = 1)
useCase.takePicture(mainExecutor, callback)
@@ -1362,7 +1364,7 @@
// After target rotation is updated, the result cropping aspect ratio should still the
// same as original one.
- val imageProperties = callback.results.first()
+ val imageProperties = callback.results.first().properties
val cropRect = imageProperties.cropRect
// Rotate the captured ImageProxy's crop rect into the coordinate space of the final
@@ -1447,13 +1449,13 @@
cameraProvider.bindToLifecycle(fakeLifecycleOwner, BACK_SELECTOR, useCase)
}
- val callback = FakeImageCaptureCallback(captureCount = 1)
+ val callback = FakeOnImageCapturedCallback(captureCount = 1)
useCase.takePicture(mainExecutor, callback)
// Wait for the signal that the image has been captured.
callback.awaitCapturesAndAssert(capturedImagesCount = 1)
- val imageProperties = callback.results.first()
+ val imageProperties = callback.results.first().properties
val cropRect = imageProperties.cropRect
val cropRectAspectRatio = Rational(cropRect!!.height(), cropRect.width())
@@ -1610,13 +1612,13 @@
)
}
- val callback = FakeImageCaptureCallback(captureCount = 1)
+ val callback = FakeOnImageCapturedCallback(captureCount = 1)
useCase.takePicture(mainExecutor, callback)
// Wait for the signal that the image has been captured.
callback.awaitCapturesAndAssert(capturedImagesCount = 1)
- val imageProperties = callback.results.first()
+ val imageProperties = callback.results.first().properties
// Check the output image rotation degrees value is correct.
assertThat(imageProperties.rotationDegrees)
@@ -1683,13 +1685,13 @@
)
}
- val callback = FakeImageCaptureCallback(captureCount = 1)
+ val callback = FakeOnImageCapturedCallback(captureCount = 1)
useCase.takePicture(mainExecutor, callback)
// Wait for the signal that the image has been captured.
callback.awaitCapturesAndAssert(capturedImagesCount = 1)
- val imageProperties = callback.results.first()
+ val imageProperties = callback.results.first().properties
// Check the output image rotation degrees value is correct.
assertThat(imageProperties.rotationDegrees)
.isEqualTo(camera.cameraInfo.getSensorRotationDegrees(useCase.targetRotation))
@@ -1721,13 +1723,13 @@
)
}
- val callback = FakeImageCaptureCallback(captureCount = 1)
+ val callback = FakeOnImageCapturedCallback(captureCount = 1)
useCase.takePicture(mainExecutor, callback)
// Wait for the signal that the image has been captured.
callback.awaitCapturesAndAssert(capturedImagesCount = 1)
- val imageProperties = callback.results.first()
+ val imageProperties = callback.results.first().properties
// Check the output image rotation degrees value is correct.
assertThat(imageProperties.rotationDegrees)
@@ -1771,13 +1773,13 @@
)
}
- val callback = FakeImageCaptureCallback(captureCount = 1)
+ val callback = FakeOnImageCapturedCallback(captureCount = 1)
imageCapture.takePicture(mainExecutor, callback)
// Wait for the signal that the image has been captured.
callback.awaitCapturesAndAssert(capturedImagesCount = 1)
- val imageProperties = callback.results.first()
+ val imageProperties = callback.results.first().properties
// Check the output image rotation degrees value is correct.
assertThat(imageProperties.rotationDegrees)
@@ -1819,13 +1821,13 @@
)
}
- val callback = FakeImageCaptureCallback(captureCount = 1)
+ val callback = FakeOnImageCapturedCallback(captureCount = 1)
imageCapture.takePicture(mainExecutor, callback)
// Wait for the signal that the image has been captured.
callback.awaitCapturesAndAssert(capturedImagesCount = 1)
- val imageProperties = callback.results.first()
+ val imageProperties = callback.results.first().properties
// Check the output image rotation degrees value is correct.
if (isRotationOptionSupportedDevice()) {
@@ -1981,7 +1983,7 @@
.isTrue()
// Act.
- val callback = FakeImageCaptureCallback(captureCount = 1)
+ val callback = FakeOnImageCapturedCallback(captureCount = 1)
withContext(Dispatchers.Main) {
// Test the reproduce step in b/235119898
cameraProvider.unbind(preview)
@@ -2008,7 +2010,7 @@
}
// wait for camera to start by taking a picture
- val callback1 = FakeImageCaptureCallback(captureCount = 1)
+ val callback1 = FakeOnImageCapturedCallback(captureCount = 1)
imageCapture.takePicture(mainExecutor, callback1)
try {
callback1.awaitCapturesAndAssert(capturedImagesCount = 1)
@@ -2017,7 +2019,7 @@
}
// Act.
- val callback2 = FakeImageCaptureCallback(captureCount = 1)
+ val callback2 = FakeOnImageCapturedCallback(captureCount = 1)
withContext(Dispatchers.Main) {
cameraProvider.unbind(videoCapture)
imageCapture.takePicture(mainExecutor, callback2)
@@ -2169,13 +2171,13 @@
assertThat(imageCapture.resolutionInfo!!.resolution).isEqualTo(maxHighResolutionOutputSize)
- val callback = FakeImageCaptureCallback(captureCount = 1)
+ val callback = FakeOnImageCapturedCallback(captureCount = 1)
imageCapture.takePicture(mainExecutor, callback)
// Wait for the signal that the image has been captured.
callback.awaitCapturesAndAssert(capturedImagesCount = 1)
- val imageProperties = callback.results.first()
+ val imageProperties = callback.results.first().properties
assertThat(imageProperties.size).isEqualTo(maxHighResolutionOutputSize)
}
@@ -2313,7 +2315,7 @@
}
suspend fun awaitCapturesAndAssert(
- timeout: Long = CAPTURE_TIMEOUT,
+ timeout: Duration = CAPTURE_TIMEOUT,
savedImagesCount: Int = 0,
errorsCount: Int = 0
) {
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/fakecamera/ImageCaptureTest.kt b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/fakecamera/ImageCaptureTest.kt
new file mode 100644
index 0000000..af52c44
--- /dev/null
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/fakecamera/ImageCaptureTest.kt
@@ -0,0 +1,239 @@
+/*
+ * 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.camera.integration.core.fakecamera
+
+import android.content.ContentValues
+import android.content.Context
+import android.os.Build
+import android.provider.MediaStore
+import androidx.camera.core.CameraSelector
+import androidx.camera.core.ImageCapture
+import androidx.camera.core.impl.utils.executor.CameraXExecutors
+import androidx.camera.lifecycle.ProcessCameraProvider
+import androidx.camera.testing.fakes.FakeAppConfig
+import androidx.camera.testing.fakes.FakeCamera
+import androidx.camera.testing.fakes.FakeCameraControl
+import androidx.camera.testing.impl.fakes.FakeLifecycleOwner
+import androidx.camera.testing.impl.fakes.FakeOnImageCapturedCallback
+import androidx.camera.testing.impl.fakes.FakeOnImageSavedCallback
+import androidx.test.core.app.ApplicationProvider
+import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
+import java.text.SimpleDateFormat
+import java.util.Locale
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.withContext
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+/**
+ * Tests using a fake camera instead of real camera by replacing the camera-camera2 layer with
+ * camera-testing layer.
+ *
+ * They are aimed to ensure that integration between camera-core and camera-testing work seamlessly.
+ */
+@RunWith(Parameterized::class)
+class ImageCaptureTest(
+ @CameraSelector.LensFacing private val lensFacing: Int,
+) {
+ @get:Rule
+ val temporaryFolder =
+ TemporaryFolder(ApplicationProvider.getApplicationContext<Context>().cacheDir)
+
+ private val context = ApplicationProvider.getApplicationContext<Context>()
+ private lateinit var cameraProvider: ProcessCameraProvider
+ private lateinit var camera: FakeCamera
+ private lateinit var cameraControl: FakeCameraControl
+ private lateinit var imageCapture: ImageCapture
+
+ @Before
+ fun setup() = runBlocking {
+ cameraProvider = getFakeConfigCameraProvider(context)
+ imageCapture = bindImageCapture()
+ }
+
+ @After
+ fun tearDown() = runBlocking {
+ if (::cameraProvider.isInitialized) {
+ withContext(Dispatchers.Main) { cameraProvider.shutdownAsync()[10, TimeUnit.SECONDS] }
+ }
+ }
+
+ // Duplicate to ImageCaptureTest on core-test-app JVM tests, any change here may need to be
+ // reflected there too
+ @Test
+ fun canSubmitTakePictureRequest(): Unit = runBlocking {
+ val countDownLatch = CountDownLatch(1)
+ cameraControl.setOnNewCaptureRequestListener { countDownLatch.countDown() }
+
+ imageCapture.takePicture(CameraXExecutors.directExecutor(), FakeOnImageCapturedCallback())
+
+ assertThat(countDownLatch.await(3, TimeUnit.SECONDS)).isTrue()
+ }
+
+ // Duplicate to ImageCaptureTest on core-test-app JVM tests, any change here may need to be
+ // reflected there too
+ @Ignore("b/318314454")
+ @Test
+ fun canCreateBitmapFromTakenImage_whenImageCapturedCallbackIsUsed(): Unit = runBlocking {
+ val callback = FakeOnImageCapturedCallback()
+ imageCapture.takePicture(CameraXExecutors.directExecutor(), callback)
+ callback.awaitCapturesAndAssert(capturedImagesCount = 1)
+ callback.results.first().image.toBitmap()
+ }
+
+ // Duplicate to ImageCaptureTest on core-test-app JVM tests, any change here may need to be
+ // reflected there too
+ @Ignore("b/318314454")
+ @Test
+ fun canFindImage_whenFileStorageAndImageSavedCallbackIsUsed(): Unit = runBlocking {
+ val saveLocation = temporaryFolder.newFile()
+ val previousLength = saveLocation.length()
+ val callback = FakeOnImageSavedCallback()
+
+ imageCapture.takePicture(
+ ImageCapture.OutputFileOptions.Builder(saveLocation).build(),
+ CameraXExecutors.directExecutor(),
+ callback
+ )
+
+ callback.awaitCapturesAndAssert(capturedImagesCount = 1)
+ assertThat(saveLocation.length()).isGreaterThan(previousLength)
+ }
+
+ // Duplicate to ImageCaptureTest on androidTest/fakecamera/ImageCaptureTest, any change here may
+ // need to be reflected there too
+ @Ignore("b/318314454")
+ @Test
+ fun canFindImage_whenMediaStoreAndImageSavedCallbackIsUsed(): Unit = runBlocking {
+ val initialCount = getMediaStoreCameraXImageCount()
+ val callback = FakeOnImageSavedCallback()
+ imageCapture.takePicture(
+ createMediaStoreOutputOptions(),
+ CameraXExecutors.directExecutor(),
+ callback
+ )
+ callback.awaitCapturesAndAssert(capturedImagesCount = 1)
+ assertThat(getMediaStoreCameraXImageCount()).isEqualTo(initialCount + 1)
+ }
+
+ private suspend fun bindImageCapture(): ImageCapture {
+ val imageCapture = ImageCapture.Builder().build()
+
+ withContext(Dispatchers.Main) {
+ cameraProvider.bindToLifecycle(
+ FakeLifecycleOwner().apply { startAndResume() },
+ CameraSelector.Builder().requireLensFacing(lensFacing).build(),
+ imageCapture
+ )
+ }
+
+ camera =
+ when (lensFacing) {
+ CameraSelector.LENS_FACING_BACK -> FakeAppConfig.getBackCamera()
+ CameraSelector.LENS_FACING_FRONT -> FakeAppConfig.getFrontCamera()
+ else -> throw AssertionError("Unsupported lens facing: $lensFacing")
+ }
+ cameraControl = camera.cameraControl as FakeCameraControl
+
+ return imageCapture
+ }
+
+ private fun getFakeConfigCameraProvider(context: Context): ProcessCameraProvider {
+ var cameraProvider: ProcessCameraProvider? = null
+ val latch = CountDownLatch(1)
+ ProcessCameraProvider.configureInstance(FakeAppConfig.create())
+ ProcessCameraProvider.getInstance(context)
+ .addListener(
+ {
+ cameraProvider = ProcessCameraProvider.getInstance(context).get()
+ latch.countDown()
+ },
+ CameraXExecutors.directExecutor()
+ )
+
+ Truth.assertWithMessage("ProcessCameraProvider.getInstance timed out!")
+ .that(latch.await(5, TimeUnit.SECONDS))
+ .isTrue()
+
+ return cameraProvider!!
+ }
+
+ private fun createMediaStoreOutputOptions(): ImageCapture.OutputFileOptions {
+ // Create time stamped name and MediaStore entry.
+ val name =
+ FILENAME_PREFIX +
+ SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-SSS", Locale.US)
+ .format(System.currentTimeMillis())
+ val contentValues =
+ ContentValues().apply {
+ put(MediaStore.MediaColumns.DISPLAY_NAME, name)
+ put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
+ if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
+ put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/CameraX-Image")
+ }
+ }
+
+ // Create output options object which contains file + metadata
+ return ImageCapture.OutputFileOptions.Builder(
+ context.contentResolver,
+ MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
+ contentValues
+ )
+ .build()
+ }
+
+ private fun getMediaStoreCameraXImageCount(): Int {
+ val projection = arrayOf(MediaStore.Images.Media._ID, MediaStore.Images.Media.DISPLAY_NAME)
+ val selection = "${MediaStore.Images.Media.DISPLAY_NAME} LIKE ?"
+ val selectionArgs = arrayOf("$FILENAME_PREFIX%")
+
+ val query =
+ ApplicationProvider.getApplicationContext<Context>()
+ .contentResolver
+ .query(
+ MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
+ projection,
+ selection,
+ selectionArgs,
+ null
+ )
+
+ return query?.use { cursor -> cursor.count } ?: 0
+ }
+
+ companion object {
+ private const val FILENAME_PREFIX = "cameraXPhoto"
+
+ @JvmStatic
+ @Parameterized.Parameters(name = "LensFacing = {0}")
+ fun data() =
+ listOf(
+ arrayOf(CameraSelector.LENS_FACING_BACK),
+ arrayOf(CameraSelector.LENS_FACING_FRONT),
+ )
+ }
+}
diff --git a/camera/integration-tests/coretestapp/src/test/java/androidx/camera/integration/core/ImageCaptureTest.kt b/camera/integration-tests/coretestapp/src/test/java/androidx/camera/integration/core/ImageCaptureTest.kt
index b09987b..459187c 100644
--- a/camera/integration-tests/coretestapp/src/test/java/androidx/camera/integration/core/ImageCaptureTest.kt
+++ b/camera/integration-tests/coretestapp/src/test/java/androidx/camera/integration/core/ImageCaptureTest.kt
@@ -16,9 +16,10 @@
package androidx.camera.integration.core
+import android.content.ContentValues
import android.content.Context
import android.os.Build
-import android.os.Looper
+import android.provider.MediaStore
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageCapture
import androidx.camera.core.impl.utils.executor.CameraXExecutors
@@ -27,13 +28,17 @@
import androidx.camera.testing.fakes.FakeAppConfig
import androidx.camera.testing.fakes.FakeCamera
import androidx.camera.testing.fakes.FakeCameraControl
-import androidx.camera.testing.impl.fakes.FakeImageCaptureCallback
import androidx.camera.testing.impl.fakes.FakeLifecycleOwner
+import androidx.camera.testing.impl.fakes.FakeOnImageCapturedCallback
+import androidx.camera.testing.impl.fakes.FakeOnImageSavedCallback
import androidx.test.core.app.ApplicationProvider
import androidx.testutils.MainDispatcherRule
import com.google.common.truth.Truth.assertThat
+import java.text.SimpleDateFormat
+import java.util.Locale
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
+import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
@@ -42,9 +47,9 @@
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
+import org.junit.rules.TemporaryFolder
import org.junit.runner.RunWith
import org.robolectric.ParameterizedRobolectricTestRunner
-import org.robolectric.Shadows.shadowOf
import org.robolectric.annotation.Config
import org.robolectric.annotation.internal.DoNotInstrument
@@ -59,26 +64,20 @@
@get:Rule val mainDispatcherRule = MainDispatcherRule(testDispatcher)
+ @get:Rule
+ val temporaryFolder =
+ TemporaryFolder(ApplicationProvider.getApplicationContext<Context>().cacheDir)
+
private val context = ApplicationProvider.getApplicationContext<Context>()
private lateinit var cameraProvider: ProcessCameraProvider
private lateinit var camera: FakeCamera
private lateinit var cameraControl: FakeCameraControl
private lateinit var imageCapture: ImageCapture
- companion object {
- @JvmStatic
- @ParameterizedRobolectricTestRunner.Parameters(name = "LensFacing = {0}")
- fun data() =
- listOf(
- arrayOf(CameraSelector.LENS_FACING_BACK),
- arrayOf(CameraSelector.LENS_FACING_FRONT),
- )
- }
-
@Before
fun setup() {
+ cameraProvider = getFakeConfigCameraProvider(context)
imageCapture = bindImageCapture()
- assertThat(imageCapture).isNotNull()
}
@After
@@ -88,28 +87,64 @@
}
}
+ // Duplicate to ImageCaptureTest on androidTest/fakecamera/ImageCaptureTest, any change here may
+ // need to be reflected there too
@Test
fun canSubmitTakePictureRequest(): Unit = runTest {
val countDownLatch = CountDownLatch(1)
cameraControl.setOnNewCaptureRequestListener { countDownLatch.countDown() }
- imageCapture.takePicture(CameraXExecutors.directExecutor(), FakeImageCaptureCallback())
+ imageCapture.takePicture(CameraXExecutors.directExecutor(), FakeOnImageCapturedCallback())
assertThat(countDownLatch.await(3, TimeUnit.SECONDS)).isTrue()
}
- @Ignore("TODO: b/318314454")
+ // Duplicate to ImageCaptureTest on androidTest/fakecamera/ImageCaptureTest, any change here may
+ // need to be reflected there too
+ @Ignore("b/318314454")
@Test
- fun canTakeImage(): Unit = runTest {
- val callback = FakeImageCaptureCallback()
+ fun canCreateBitmapFromTakenImage_whenImageCapturedCallbackIsUsed(): Unit = runTest {
+ val callback = FakeOnImageCapturedCallback()
imageCapture.takePicture(CameraXExecutors.directExecutor(), callback)
- shadowOf(Looper.getMainLooper()).idle()
- callback.awaitCaptures()
+ callback.awaitCapturesAndAssert(capturedImagesCount = 1)
+ callback.results.first().image.toBitmap()
+ }
+
+ // Duplicate to ImageCaptureTest on androidTest/fakecamera/ImageCaptureTest, any change here may
+ // need to be reflected there too
+ @Ignore("b/318314454")
+ @Test
+ fun canFindImage_whenFileStorageAndImageSavedCallbackIsUsed(): Unit = runTest {
+ val saveLocation = temporaryFolder.newFile()
+ val previousLength = saveLocation.length()
+ val callback = FakeOnImageSavedCallback()
+
+ imageCapture.takePicture(
+ ImageCapture.OutputFileOptions.Builder(saveLocation).build(),
+ CameraXExecutors.directExecutor(),
+ callback
+ )
+
+ callback.awaitCapturesAndAssert(capturedImagesCount = 1)
+ assertThat(saveLocation.length()).isGreaterThan(previousLength)
+ }
+
+ // Duplicate to ImageCaptureTest on androidTest/fakecamera/ImageCaptureTest, any change here may
+ // need to be reflected there too
+ @Ignore("b/318314454")
+ @Test
+ fun canFindFakeImageUri_whenMediaStoreAndImageSavedCallbackIsUsed(): Unit = runBlocking {
+ val callback = FakeOnImageSavedCallback()
+ imageCapture.takePicture(
+ createMediaStoreOutputOptions(),
+ CameraXExecutors.directExecutor(),
+ callback
+ )
+ callback.awaitCapturesAndAssert(capturedImagesCount = 1)
+ assertThat(callback.results.first().savedUri).isNotNull()
}
private fun bindImageCapture(): ImageCapture {
- cameraProvider = getFakeConfigCameraProvider(context)
-
val imageCapture = ImageCapture.Builder().build()
cameraProvider.bindToLifecycle(
@@ -127,4 +162,40 @@
return imageCapture
}
+
+ private fun createMediaStoreOutputOptions(): ImageCapture.OutputFileOptions {
+ // Create time stamped name and MediaStore entry.
+ val name =
+ FILENAME_PREFIX +
+ SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-SSS", Locale.US)
+ .format(System.currentTimeMillis())
+ val contentValues =
+ ContentValues().apply {
+ put(MediaStore.MediaColumns.DISPLAY_NAME, name)
+ put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
+ if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
+ put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/CameraX-Image")
+ }
+ }
+
+ // Create output options object which contains file + metadata
+ return ImageCapture.OutputFileOptions.Builder(
+ context.contentResolver,
+ MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
+ contentValues
+ )
+ .build()
+ }
+
+ companion object {
+ private const val FILENAME_PREFIX = "cameraXPhoto"
+
+ @JvmStatic
+ @ParameterizedRobolectricTestRunner.Parameters(name = "LensFacing = {0}")
+ fun data() =
+ listOf(
+ arrayOf(CameraSelector.LENS_FACING_BACK),
+ arrayOf(CameraSelector.LENS_FACING_FRONT),
+ )
+ }
}
diff --git a/collection/collection/build.gradle b/collection/collection/build.gradle
index 7833819..ff66423 100644
--- a/collection/collection/build.gradle
+++ b/collection/collection/build.gradle
@@ -53,7 +53,7 @@
commonMain {
dependencies {
api(libs.kotlinStdlib)
- api(project(":annotation:annotation"))
+ api("androidx.annotation:annotation:1.9.0-alpha02")
}
}
diff --git a/compose/animation/animation/src/androidInstrumentedTest/kotlin/androidx/compose/animation/AnimateBoundsTest.kt b/compose/animation/animation/src/androidInstrumentedTest/kotlin/androidx/compose/animation/AnimateBoundsTest.kt
index 6159e8d..c69c971b 100644
--- a/compose/animation/animation/src/androidInstrumentedTest/kotlin/androidx/compose/animation/AnimateBoundsTest.kt
+++ b/compose/animation/animation/src/androidInstrumentedTest/kotlin/androidx/compose/animation/AnimateBoundsTest.kt
@@ -51,7 +51,6 @@
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
-import androidx.compose.ui.unit.lerp
import androidx.compose.ui.unit.round
import androidx.compose.ui.util.fastRoundToInt
import androidx.compose.ui.util.lerp
@@ -137,8 +136,9 @@
@Test
fun animateSize() =
with(rule.density) {
+ val frameTime = 16 // milliseconds
val frames = 14 // Even number to reliable test at half duration
- val durationMillis = frames * 16
+ val durationMillis = frames * frameTime
val rootSizePx = 400
val boxSizeSmallPx = rootSizePx * 0.25f
val boxSizeLargePx = rootSizePx * 0.5f
@@ -179,14 +179,14 @@
rule.mainClock.autoAdvance = false
isExpanded = true
rule.waitForIdle()
- rule.mainClock.advanceTimeByFrame()
- rule.mainClock.advanceTimeByFrame()
- // 1-frame latency. TODO: Can we fix this?
- rule.mainClock.advanceTimeByFrame()
+ // Wait until first animated frame, for test stability
+ do {
+ rule.mainClock.advanceTimeByFrame()
+ } while (expectedSmallSize.round() == boxSize)
- // Advance to approx. the middle of the animation
- rule.mainClock.advanceTimeBy(durationMillis / 2L)
+ // Advance to approx. the middle of the animation (minus the first animated frame)
+ rule.mainClock.advanceTimeBy(durationMillis / 2L - frameTime)
val expectedMidIntSize = (expectedLargeSize + expectedSmallSize).times(0.5f).round()
assertEquals(expectedMidIntSize, boxSize)
@@ -501,6 +501,7 @@
rule.mainClock.autoAdvance = false
runBlocking { scrollState.scrollBy(itemSizePx) }
+ rule.waitForIdle()
// Let animations play for the first frame
rule.mainClock.advanceTimeByFrame()
@@ -519,17 +520,13 @@
rule.waitForIdle()
rule.mainClock.autoAdvance = false
- // Not sure why, but we need to run this scroll within a runOnIdle to complete the test
- // consistently across devices.
- rule.runOnIdle {
- runBlocking {
- // Scroll back into starting position
- scrollState.scrollBy(-itemSizePx)
- }
+ runBlocking {
+ // Scroll back into starting position
+ scrollState.scrollBy(-itemSizePx)
}
+ rule.waitForIdle()
rule.mainClock.advanceTimeByFrame()
- rule.mainClock.advanceTimeByFrame()
// Position should correspond to the exaggerated keyframe offset.
// Note that the keyframe is actually defined around the item's center
diff --git a/compose/material/material-navigation/build.gradle b/compose/material/material-navigation/build.gradle
index f577da2..4cf82b4 100644
--- a/compose/material/material-navigation/build.gradle
+++ b/compose/material/material-navigation/build.gradle
@@ -13,6 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
+import androidx.build.KotlinTarget
import androidx.build.LibraryType
plugins {
@@ -47,6 +49,7 @@
description = "Compose Material integration with Navigation"
legacyDisableKotlinStrictApiMode = true
samples(projectOrArtifact(":compose:material:material-navigation-samples"))
+ kotlinTarget = KotlinTarget.KOTLIN_1_9
}
android {
diff --git a/compose/material/material-navigation/samples/build.gradle b/compose/material/material-navigation/samples/build.gradle
index 20b3d14d..0e1cdc4 100644
--- a/compose/material/material-navigation/samples/build.gradle
+++ b/compose/material/material-navigation/samples/build.gradle
@@ -21,6 +21,8 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
+
+import androidx.build.KotlinTarget
import androidx.build.LibraryType
plugins {
@@ -45,6 +47,7 @@
mavenVersion = LibraryVersions.COMPOSE
inceptionYear = "2024"
description = "Samples for Compose Material integration with Navigation"
+ kotlinTarget = KotlinTarget.KOTLIN_1_9
}
android {
diff --git a/compose/material3/adaptive/adaptive-layout/api/current.txt b/compose/material3/adaptive/adaptive-layout/api/current.txt
index 6a2ac8f..a64ea1b 100644
--- a/compose/material3/adaptive/adaptive-layout/api/current.txt
+++ b/compose/material3/adaptive/adaptive-layout/api/current.txt
@@ -41,8 +41,8 @@
}
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);
- 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);
+ 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 androidx.compose.material3.adaptive.layout.ThreePaneMotion paneMotions, 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 androidx.compose.material3.adaptive.layout.ThreePaneMotion paneMotions, 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 {
@@ -209,8 +209,8 @@
}
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);
- 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);
+ 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 androidx.compose.material3.adaptive.layout.ThreePaneMotion paneMotions, 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 androidx.compose.material3.adaptive.layout.ThreePaneMotion paneMotions, 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);
}
@SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public final class SupportingPaneScaffoldRole {
@@ -223,6 +223,41 @@
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 {
+ ctor public ThreePaneMotion(androidx.compose.material3.adaptive.layout.PaneMotion primaryPaneMotion, androidx.compose.material3.adaptive.layout.PaneMotion secondaryPaneMotion, androidx.compose.material3.adaptive.layout.PaneMotion tertiaryPaneMotion, optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntSize> sizeAnimationSpec, optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> positionAnimationSpec, optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> delayedPositionAnimationSpec);
+ method public androidx.compose.material3.adaptive.layout.ThreePaneMotion copy(optional androidx.compose.material3.adaptive.layout.PaneMotion primaryPaneMotion, optional androidx.compose.material3.adaptive.layout.PaneMotion secondaryPaneMotion, optional androidx.compose.material3.adaptive.layout.PaneMotion tertiaryPaneMotion, optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntSize> sizeAnimationSpec, optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> positionAnimationSpec, optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> delayedPositionAnimationSpec);
+ method public operator androidx.compose.material3.adaptive.layout.PaneMotion get(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole role);
+ method public androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> getDelayedPositionAnimationSpec();
+ method public androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> getPositionAnimationSpec();
+ 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.IntOffset> delayedPositionAnimationSpec;
+ property public final androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> positionAnimationSpec;
+ property public final androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntSize> sizeAnimationSpec;
+ 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 ThreePaneMotionDefaults {
+ method public androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> getPanePositionAnimationSpec();
+ method public androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> getPanePositionAnimationSpecDelayed();
+ method public androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntSize> getPaneSizeAnimationSpec();
+ property public final androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> PanePositionAnimationSpec;
+ property public final androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> PanePositionAnimationSpecDelayed;
+ property public final androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntSize> PaneSizeAnimationSpec;
+ field public static final androidx.compose.material3.adaptive.layout.ThreePaneMotionDefaults INSTANCE;
+ }
+
+ public final class ThreePaneMotionKt {
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.layout.ThreePaneMotion calculateListDetailPaneScaffoldMotion(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldState);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.layout.ThreePaneMotion calculateListDetailPaneScaffoldMotion(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue targetScaffoldValue);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.layout.ThreePaneMotion calculateSupportingPaneScaffoldMotion(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldState);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.layout.ThreePaneMotion calculateSupportingPaneScaffoldMotion(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue targetScaffoldValue);
+ }
+
@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);
diff --git a/compose/material3/adaptive/adaptive-layout/api/restricted_current.txt b/compose/material3/adaptive/adaptive-layout/api/restricted_current.txt
index 6a2ac8f..a64ea1b 100644
--- a/compose/material3/adaptive/adaptive-layout/api/restricted_current.txt
+++ b/compose/material3/adaptive/adaptive-layout/api/restricted_current.txt
@@ -41,8 +41,8 @@
}
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);
- 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);
+ 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 androidx.compose.material3.adaptive.layout.ThreePaneMotion paneMotions, 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 androidx.compose.material3.adaptive.layout.ThreePaneMotion paneMotions, 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 {
@@ -209,8 +209,8 @@
}
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);
- 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);
+ 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 androidx.compose.material3.adaptive.layout.ThreePaneMotion paneMotions, 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 androidx.compose.material3.adaptive.layout.ThreePaneMotion paneMotions, 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);
}
@SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public final class SupportingPaneScaffoldRole {
@@ -223,6 +223,41 @@
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 {
+ ctor public ThreePaneMotion(androidx.compose.material3.adaptive.layout.PaneMotion primaryPaneMotion, androidx.compose.material3.adaptive.layout.PaneMotion secondaryPaneMotion, androidx.compose.material3.adaptive.layout.PaneMotion tertiaryPaneMotion, optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntSize> sizeAnimationSpec, optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> positionAnimationSpec, optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> delayedPositionAnimationSpec);
+ method public androidx.compose.material3.adaptive.layout.ThreePaneMotion copy(optional androidx.compose.material3.adaptive.layout.PaneMotion primaryPaneMotion, optional androidx.compose.material3.adaptive.layout.PaneMotion secondaryPaneMotion, optional androidx.compose.material3.adaptive.layout.PaneMotion tertiaryPaneMotion, optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntSize> sizeAnimationSpec, optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> positionAnimationSpec, optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> delayedPositionAnimationSpec);
+ method public operator androidx.compose.material3.adaptive.layout.PaneMotion get(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole role);
+ method public androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> getDelayedPositionAnimationSpec();
+ method public androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> getPositionAnimationSpec();
+ 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.IntOffset> delayedPositionAnimationSpec;
+ property public final androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> positionAnimationSpec;
+ property public final androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntSize> sizeAnimationSpec;
+ 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 ThreePaneMotionDefaults {
+ method public androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> getPanePositionAnimationSpec();
+ method public androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> getPanePositionAnimationSpecDelayed();
+ method public androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntSize> getPaneSizeAnimationSpec();
+ property public final androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> PanePositionAnimationSpec;
+ property public final androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> PanePositionAnimationSpecDelayed;
+ property public final androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntSize> PaneSizeAnimationSpec;
+ field public static final androidx.compose.material3.adaptive.layout.ThreePaneMotionDefaults INSTANCE;
+ }
+
+ public final class ThreePaneMotionKt {
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.layout.ThreePaneMotion calculateListDetailPaneScaffoldMotion(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldState);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.layout.ThreePaneMotion calculateListDetailPaneScaffoldMotion(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue targetScaffoldValue);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.layout.ThreePaneMotion calculateSupportingPaneScaffoldMotion(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldState);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.layout.ThreePaneMotion calculateSupportingPaneScaffoldMotion(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue targetScaffoldValue);
+ }
+
@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);
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidUnitTest/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffoldDirectiveTest.kt b/compose/material3/adaptive/adaptive-layout/src/androidUnitTest/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffoldDirectiveTest.kt
index ea19285..2718a66 100644
--- a/compose/material3/adaptive/adaptive-layout/src/androidUnitTest/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffoldDirectiveTest.kt
+++ b/compose/material3/adaptive/adaptive-layout/src/androidUnitTest/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffoldDirectiveTest.kt
@@ -32,6 +32,7 @@
@RunWith(JUnit4::class)
class PaneScaffoldDirectiveTest {
@Test
+ @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
fun test_calculateStandardPaneScaffoldDirective_compactWidth() {
val scaffoldDirective =
calculatePaneScaffoldDirective(
@@ -46,6 +47,7 @@
}
@Test
+ @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
fun test_calculateStandardPaneScaffoldDirective_mediumWidth() {
val scaffoldDirective =
calculatePaneScaffoldDirective(
@@ -60,6 +62,7 @@
}
@Test
+ @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
fun test_calculateStandardPaneScaffoldDirective_expandedWidth() {
val scaffoldDirective =
calculatePaneScaffoldDirective(
@@ -74,6 +77,7 @@
}
@Test
+ @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
fun test_calculateStandardPaneScaffoldDirective_tabletop() {
val scaffoldDirective =
calculatePaneScaffoldDirective(
@@ -88,6 +92,7 @@
}
@Test
+ @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
fun test_calculateDensePaneScaffoldDirective_compactWidth() {
val scaffoldDirective =
calculatePaneScaffoldDirectiveWithTwoPanesOnMediumWidth(
@@ -102,6 +107,7 @@
}
@Test
+ @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
fun test_calculateDensePaneScaffoldDirective_mediumWidth() {
val scaffoldDirective =
calculatePaneScaffoldDirectiveWithTwoPanesOnMediumWidth(
@@ -116,6 +122,7 @@
}
@Test
+ @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
fun test_calculateDensePaneScaffoldDirective_expandedWidth() {
val scaffoldDirective =
calculatePaneScaffoldDirectiveWithTwoPanesOnMediumWidth(
@@ -130,6 +137,7 @@
}
@Test
+ @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
fun test_calculateDensePaneScaffoldDirective_tabletop() {
val scaffoldDirective =
calculatePaneScaffoldDirectiveWithTwoPanesOnMediumWidth(
@@ -144,6 +152,7 @@
}
@Test
+ @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
fun test_calculateStandardPaneScaffoldDirective_alwaysAvoidHinge() {
val scaffoldDirective =
calculatePaneScaffoldDirective(
@@ -158,6 +167,7 @@
}
@Test
+ @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
fun test_calculateStandardPaneScaffoldDirective_avoidOccludingHinge() {
val scaffoldDirective =
calculatePaneScaffoldDirective(
@@ -172,6 +182,7 @@
}
@Test
+ @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
fun test_calculateStandardPaneScaffoldDirective_avoidSeparatingHinge() {
val scaffoldDirective =
calculatePaneScaffoldDirective(
@@ -186,6 +197,7 @@
}
@Test
+ @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
fun test_calculateStandardPaneScaffoldDirective_neverAvoidHinge() {
val scaffoldDirective =
calculatePaneScaffoldDirective(
@@ -200,6 +212,7 @@
}
@Test
+ @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
fun test_calculateDensePaneScaffoldDirective_alwaysAvoidHinge() {
val scaffoldDirective =
calculatePaneScaffoldDirectiveWithTwoPanesOnMediumWidth(
@@ -214,6 +227,7 @@
}
@Test
+ @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
fun test_calculateDensePaneScaffoldDirective_avoidOccludingHinge() {
val scaffoldDirective =
calculatePaneScaffoldDirectiveWithTwoPanesOnMediumWidth(
@@ -228,6 +242,7 @@
}
@Test
+ @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
fun test_calculateDensePaneScaffoldDirective_avoidSeparatingHinge() {
val scaffoldDirective =
calculatePaneScaffoldDirectiveWithTwoPanesOnMediumWidth(
@@ -242,6 +257,7 @@
}
@Test
+ @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
fun test_calculateDensePaneScaffoldDirective_neverAvoidHinge() {
val scaffoldDirective =
calculatePaneScaffoldDirectiveWithTwoPanesOnMediumWidth(
diff --git a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ListDetailPaneScaffold.kt b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ListDetailPaneScaffold.kt
index 5bb4f4c..815399d 100644
--- a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ListDetailPaneScaffold.kt
+++ b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ListDetailPaneScaffold.kt
@@ -47,12 +47,12 @@
* @param extraPane the extra pane of the scaffold, which is supposed to hold any supplementary info
* besides the list and the detail panes, for example, a task list or a mini-calendar view of a
* mail app. See [ListDetailPaneScaffoldRole.Extra].
- * @param paneExpansionDragHandle the pane expansion drag handle to let users be able to drag to
- * change pane expansion state. Note that by default this argument will be `null`, and there won't
- * be a drag handle rendered and users won't be able to drag to change the pane split. You can
- * provide a [PaneExpansionDragHandle] here as our sample suggests. On the other hand, even if
- * there's no drag handle, you can still modify [paneExpansionState] directly to apply pane
- * expansion.
+ * @param paneMotions The specified motion of the panes. By default the value will be calculated by
+ * [calculateListDetailPaneScaffoldMotion] according to the target [ThreePaneScaffoldValue].
+ * @param paneExpansionDragHandle provide a custom pane expansion drag handle to allow users to
+ * resize panes and change the pane expansion state by dragging. This is `null` by default, which
+ * renders no drag handle. Even there's no drag handle, you can still change pane size directly
+ * via modifying [paneExpansionState].
* @param paneExpansionState the state object of pane expansion.
*/
@ExperimentalMaterial3AdaptiveApi
@@ -64,6 +64,7 @@
detailPane: @Composable ThreePaneScaffoldPaneScope.() -> Unit,
modifier: Modifier = Modifier,
extraPane: (@Composable ThreePaneScaffoldPaneScope.() -> Unit)? = null,
+ paneMotions: ThreePaneMotion = calculateListDetailPaneScaffoldMotion(value),
paneExpansionDragHandle: (@Composable ThreePaneScaffoldScope.(PaneExpansionState) -> Unit)? =
null,
paneExpansionState: PaneExpansionState = rememberPaneExpansionState(value),
@@ -73,6 +74,7 @@
scaffoldDirective = directive,
scaffoldValue = value,
paneOrder = ListDetailPaneScaffoldDefaults.PaneOrder,
+ paneMotions = paneMotions,
secondaryPane = listPane,
tertiaryPane = extraPane,
paneExpansionDragHandle = paneExpansionDragHandle,
@@ -102,6 +104,13 @@
* @param extraPane the extra pane of the scaffold, which is supposed to hold any supplementary info
* besides the list and the detail panes, for example, a task list or a mini-calendar view of a
* mail app. See [ListDetailPaneScaffoldRole.Extra].
+ * @param paneMotions The specified motion of the panes. By default the value will be calculated by
+ * [calculateListDetailPaneScaffoldMotion] according to the target [ThreePaneScaffoldValue].
+ * @param paneExpansionDragHandle provide a custom pane expansion drag handle to allow users to
+ * resize panes and change the pane expansion state by dragging. This is `null` by default, which
+ * renders no drag handle. Even there's no drag handle, you can still change pane size directly
+ * via modifying [paneExpansionState].
+ * @param paneExpansionState the state object of pane expansion.
*/
@ExperimentalMaterial3AdaptiveApi
@Composable
@@ -112,14 +121,21 @@
detailPane: @Composable ThreePaneScaffoldPaneScope.() -> Unit,
modifier: Modifier = Modifier,
extraPane: (@Composable ThreePaneScaffoldPaneScope.() -> Unit)? = null,
+ paneMotions: ThreePaneMotion = scaffoldState.calculateListDetailPaneScaffoldMotion(),
+ paneExpansionDragHandle: (@Composable ThreePaneScaffoldScope.(PaneExpansionState) -> Unit)? =
+ null,
+ paneExpansionState: PaneExpansionState = rememberPaneExpansionState(scaffoldState.targetState),
) {
ThreePaneScaffold(
modifier = modifier.fillMaxSize(),
scaffoldDirective = directive,
scaffoldState = scaffoldState,
paneOrder = ListDetailPaneScaffoldDefaults.PaneOrder,
+ paneMotions = paneMotions,
secondaryPane = listPane,
tertiaryPane = extraPane,
+ paneExpansionDragHandle = paneExpansionDragHandle,
+ paneExpansionState = paneExpansionState,
primaryPane = detailPane
)
}
diff --git a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffoldDirective.kt b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffoldDirective.kt
index d0af5ed..a2a06c7 100644
--- a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffoldDirective.kt
+++ b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffoldDirective.kt
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+@file:Suppress("DEPRECATED") // Deprecated import WindowWidthSizeClass.
+
package androidx.compose.material3.adaptive.layout
import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
@@ -27,7 +29,6 @@
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
-import androidx.window.core.layout.WindowWidthSizeClass
/**
* Calculates the recommended [PaneScaffoldDirective] from a given [WindowAdaptiveInfo]. Use this
@@ -44,6 +45,7 @@
* @return an [PaneScaffoldDirective] to be used to decide adaptive layout states.
*/
@ExperimentalMaterial3AdaptiveApi
+@Suppress("DEPRECATION") // WindowWidthSizeClass is deprecated
fun calculatePaneScaffoldDirective(
windowAdaptiveInfo: WindowAdaptiveInfo,
verticalHingePolicy: HingePolicy = HingePolicy.AvoidSeparating
@@ -51,11 +53,11 @@
val maxHorizontalPartitions: Int
val horizontalPartitionSpacerSize: Dp
when (windowAdaptiveInfo.windowSizeClass.windowWidthSizeClass) {
- WindowWidthSizeClass.COMPACT -> {
+ androidx.window.core.layout.WindowWidthSizeClass.COMPACT -> {
maxHorizontalPartitions = 1
horizontalPartitionSpacerSize = 0.dp
}
- WindowWidthSizeClass.MEDIUM -> {
+ androidx.window.core.layout.WindowWidthSizeClass.MEDIUM -> {
maxHorizontalPartitions = 1
horizontalPartitionSpacerSize = 0.dp
}
@@ -108,12 +110,14 @@
* @return an [PaneScaffoldDirective] to be used to decide adaptive layout states.
*/
@ExperimentalMaterial3AdaptiveApi
+@Suppress("DEPRECATION") // WindowWidthSizeClass is deprecated
fun calculatePaneScaffoldDirectiveWithTwoPanesOnMediumWidth(
windowAdaptiveInfo: WindowAdaptiveInfo,
verticalHingePolicy: HingePolicy = HingePolicy.AvoidSeparating
): PaneScaffoldDirective {
val isMediumWidth =
- windowAdaptiveInfo.windowSizeClass.windowWidthSizeClass == WindowWidthSizeClass.MEDIUM
+ windowAdaptiveInfo.windowSizeClass.windowWidthSizeClass ==
+ androidx.window.core.layout.WindowWidthSizeClass.MEDIUM
return with(calculatePaneScaffoldDirective(windowAdaptiveInfo, verticalHingePolicy)) {
copy(
maxHorizontalPartitions = if (isMediumWidth) 2 else maxHorizontalPartitions,
diff --git a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/SupportingPaneScaffold.kt b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/SupportingPaneScaffold.kt
index 7b8abec..f828c05 100644
--- a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/SupportingPaneScaffold.kt
+++ b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/SupportingPaneScaffold.kt
@@ -41,12 +41,12 @@
* @param extraPane the extra pane of the scaffold, which is supposed to hold any additional content
* besides the main and the supporting panes, for example, a styling panel in a doc app. See
* [SupportingPaneScaffoldRole.Extra].
- * @param paneExpansionDragHandle the pane expansion drag handle to let users be able to drag to
- * change pane expansion state. Note that by default this argument will be `null`, and there won't
- * be a drag handle rendered and users won't be able to drag to change the pane split. You can
- * provide a [PaneExpansionDragHandle] here as our sample suggests. On the other hand, even if
- * there's no drag handle, you can still modify [paneExpansionState] directly to apply pane
- * expansion.
+ * @param paneMotions The specified motion of the panes. By default the value will be calculated by
+ * [calculateSupportingPaneScaffoldMotion] according to the target [ThreePaneScaffoldValue].
+ * @param paneExpansionDragHandle provide a custom pane expansion drag handle to allow users to
+ * resize panes and change the pane expansion state by dragging. This is `null` by default, which
+ * renders no drag handle. Even there's no drag handle, you can still change pane size directly
+ * via modifying [paneExpansionState].
* @param paneExpansionState the state object of pane expansion.
*/
@ExperimentalMaterial3AdaptiveApi
@@ -58,6 +58,7 @@
supportingPane: @Composable ThreePaneScaffoldPaneScope.() -> Unit,
modifier: Modifier = Modifier,
extraPane: (@Composable ThreePaneScaffoldPaneScope.() -> Unit)? = null,
+ paneMotions: ThreePaneMotion = calculateSupportingPaneScaffoldMotion(value),
paneExpansionDragHandle: (@Composable ThreePaneScaffoldScope.(PaneExpansionState) -> Unit)? =
null,
paneExpansionState: PaneExpansionState = rememberPaneExpansionState(value),
@@ -69,6 +70,7 @@
paneOrder = SupportingPaneScaffoldDefaults.PaneOrder,
secondaryPane = supportingPane,
tertiaryPane = extraPane,
+ paneMotions = paneMotions,
paneExpansionDragHandle = paneExpansionDragHandle,
paneExpansionState = paneExpansionState,
primaryPane = mainPane
@@ -95,6 +97,13 @@
* @param extraPane the extra pane of the scaffold, which is supposed to hold any additional content
* besides the main and the supporting panes, for example, a styling panel in a doc app. See
* [SupportingPaneScaffoldRole.Extra].
+ * @param paneMotions The specified motion of the panes. By default the value will be calculated by
+ * [calculateSupportingPaneScaffoldMotion] according to the target [ThreePaneScaffoldValue].
+ * @param paneExpansionDragHandle provide a custom pane expansion drag handle to allow users to
+ * resize panes and change the pane expansion state by dragging. This is `null` by default, which
+ * renders no drag handle. Even there's no drag handle, you can still change pane size directly
+ * via modifying [paneExpansionState].
+ * @param paneExpansionState the state object of pane expansion.
*/
@ExperimentalMaterial3AdaptiveApi
@Composable
@@ -105,6 +114,10 @@
supportingPane: @Composable ThreePaneScaffoldPaneScope.() -> Unit,
modifier: Modifier = Modifier,
extraPane: (@Composable ThreePaneScaffoldPaneScope.() -> Unit)? = null,
+ paneMotions: ThreePaneMotion = scaffoldState.calculateSupportingPaneScaffoldMotion(),
+ paneExpansionDragHandle: (@Composable ThreePaneScaffoldScope.(PaneExpansionState) -> Unit)? =
+ null,
+ paneExpansionState: PaneExpansionState = rememberPaneExpansionState(scaffoldState.targetState),
) {
ThreePaneScaffold(
modifier = modifier.fillMaxSize(),
@@ -113,6 +126,9 @@
paneOrder = SupportingPaneScaffoldDefaults.PaneOrder,
secondaryPane = supportingPane,
tertiaryPane = extraPane,
+ paneMotions = paneMotions,
+ paneExpansionDragHandle = paneExpansionDragHandle,
+ paneExpansionState = paneExpansionState,
primaryPane = mainPane
)
}
diff --git a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneMotion.kt b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneMotion.kt
index c3a54ac..23a9c59 100644
--- a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneMotion.kt
+++ b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneMotion.kt
@@ -19,7 +19,6 @@
import androidx.compose.animation.core.AnimationVector
import androidx.compose.animation.core.FiniteAnimationSpec
import androidx.compose.animation.core.Spring
-import androidx.compose.animation.core.SpringSpec
import androidx.compose.animation.core.TwoWayConverter
import androidx.compose.animation.core.VectorizedFiniteAnimationSpec
import androidx.compose.animation.core.VisibilityThreshold
@@ -33,6 +32,69 @@
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.util.fastForEachIndexed
+/**
+ * Calculates the default [ThreePaneMotion] of [ListDetailPaneScaffold] according to the given
+ * [ThreePaneScaffoldState]'s current and target values.
+ */
+@ExperimentalMaterial3AdaptiveApi
+@Composable
+fun ThreePaneScaffoldState.calculateListDetailPaneScaffoldMotion(): ThreePaneMotion =
+ calculateThreePaneMotion(ListDetailPaneScaffoldDefaults.PaneOrder)
+
+/**
+ * Calculates the default [ThreePaneMotion] of [ListDetailPaneScaffold] according to the target and
+ * the previously remembered [ThreePaneScaffoldValue].
+ */
+@ExperimentalMaterial3AdaptiveApi
+@Composable
+fun calculateListDetailPaneScaffoldMotion(
+ targetScaffoldValue: ThreePaneScaffoldValue
+): ThreePaneMotion =
+ calculateThreePaneMotion(targetScaffoldValue, ListDetailPaneScaffoldDefaults.PaneOrder)
+
+/**
+ * Calculates the default [ThreePaneMotion] of [SupportingPaneScaffold] according to the given
+ * [ThreePaneScaffoldState]'s current and target values.
+ */
+@ExperimentalMaterial3AdaptiveApi
+@Composable
+fun ThreePaneScaffoldState.calculateSupportingPaneScaffoldMotion(): ThreePaneMotion =
+ calculateThreePaneMotion(SupportingPaneScaffoldDefaults.PaneOrder)
+
+/**
+ * Calculates the default [ThreePaneMotion] of [SupportingPaneScaffold] according to the target and
+ * the previously remembered [ThreePaneScaffoldValue].
+ */
+@ExperimentalMaterial3AdaptiveApi
+@Composable
+fun calculateSupportingPaneScaffoldMotion(
+ targetScaffoldValue: ThreePaneScaffoldValue
+): ThreePaneMotion =
+ calculateThreePaneMotion(targetScaffoldValue, SupportingPaneScaffoldDefaults.PaneOrder)
+
+@ExperimentalMaterial3AdaptiveApi
+@Composable
+internal fun ThreePaneScaffoldState.calculateThreePaneMotion(
+ paneOrder: ThreePaneScaffoldHorizontalOrder
+): ThreePaneMotion {
+ class ThreePaneMotionHolder(var value: ThreePaneMotion)
+
+ val resultHolder = remember { ThreePaneMotionHolder(ThreePaneMotion.NoMotion) }
+ if (currentState != targetState) {
+ // Only update motions when the state changes to prevent unnecessary recomposition at the
+ // end of state transitions.
+ val ltrPaneOrder = paneOrder.toLtrOrder(LocalLayoutDirection.current)
+ val paneMotions = calculatePaneMotion(currentState, targetState, ltrPaneOrder)
+ resultHolder.value =
+ ThreePaneMotion(
+ paneMotions[ltrPaneOrder.indexOf(ThreePaneScaffoldRole.Primary)],
+ paneMotions[ltrPaneOrder.indexOf(ThreePaneScaffoldRole.Secondary)],
+ paneMotions[ltrPaneOrder.indexOf(ThreePaneScaffoldRole.Tertiary)]
+ )
+ }
+ return resultHolder.value
+}
+
@ExperimentalMaterial3AdaptiveApi
@Composable
internal fun calculateThreePaneMotion(
@@ -59,12 +121,28 @@
return threePaneMotion
}
+/**
+ * The class that provides motion settings for three pane scaffolds like [ListDetailPaneScaffold]
+ * and [SupportingPaneScaffold].
+ *
+ * @param primaryPaneMotion the specified [PaneMotion] of the primary pane, i.e.,
+ * [ListDetailPaneScaffoldRole.Detail] or [SupportingPaneScaffoldRole.Main].
+ * @param secondaryPaneMotion the specified [PaneMotion] of the secondary pane, i.e.,
+ * [ListDetailPaneScaffoldRole.List] or [SupportingPaneScaffoldRole.Supporting].
+ * @param tertiaryPaneMotion the specified [PaneMotion] of the tertiary pane, i.e.,
+ * [ListDetailPaneScaffoldRole.Extra] or [SupportingPaneScaffoldRole.Extra].
+ * @param sizeAnimationSpec the specified [FiniteAnimationSpec] when animating pane size changes.
+ * @param positionAnimationSpec the specified [FiniteAnimationSpec] when animating pane position
+ * changes.
+ * @param delayedPositionAnimationSpec the specified [FiniteAnimationSpec] when animating pane
+ * position changes with a delay to emphasize entering panes.
+ */
@ExperimentalMaterial3AdaptiveApi
@Immutable
-internal class ThreePaneMotion(
- val primaryPaneMotion: PaneMotion,
- val secondaryPaneMotion: PaneMotion,
- val tertiaryPaneMotion: PaneMotion,
+class ThreePaneMotion(
+ internal val primaryPaneMotion: PaneMotion,
+ internal val secondaryPaneMotion: PaneMotion,
+ internal val tertiaryPaneMotion: PaneMotion,
val sizeAnimationSpec: FiniteAnimationSpec<IntSize> =
ThreePaneMotionDefaults.PaneSizeAnimationSpec,
val positionAnimationSpec: FiniteAnimationSpec<IntOffset> =
@@ -72,6 +150,22 @@
val delayedPositionAnimationSpec: FiniteAnimationSpec<IntOffset> =
ThreePaneMotionDefaults.PanePositionAnimationSpecDelayed
) {
+ /**
+ * Makes a copy of [ThreePaneMotion] with override values.
+ *
+ * @param primaryPaneMotion the specified [PaneMotion] of the primary pane, i.e.,
+ * [ListDetailPaneScaffoldRole.Detail] or [SupportingPaneScaffoldRole.Main].
+ * @param secondaryPaneMotion the specified [PaneMotion] of the secondary pane, i.e.,
+ * [ListDetailPaneScaffoldRole.List] or [SupportingPaneScaffoldRole.Supporting].
+ * @param tertiaryPaneMotion the specified [PaneMotion] of the tertiary pane, i.e.,
+ * [ListDetailPaneScaffoldRole.Extra] or [SupportingPaneScaffoldRole.Extra].
+ * @param sizeAnimationSpec the specified [FiniteAnimationSpec] when animating pane size
+ * changes.
+ * @param positionAnimationSpec the specified [FiniteAnimationSpec] when animating pane position
+ * changes.
+ * @param delayedPositionAnimationSpec the specified [FiniteAnimationSpec] when animating pane
+ * position changes with a delay to emphasize entering panes.
+ */
fun copy(
primaryPaneMotion: PaneMotion = this.primaryPaneMotion,
secondaryPaneMotion: PaneMotion = this.secondaryPaneMotion,
@@ -90,6 +184,12 @@
delayedPositionAnimationSpec
)
+ /**
+ * Gets the specified [PaneMotion] of a given pane role.
+ *
+ * @param role the specified role of the pane, see [ListDetailPaneScaffoldRole] and
+ * [SupportingPaneScaffoldRole].
+ */
operator fun get(role: ThreePaneScaffoldRole): PaneMotion =
when (role) {
ThreePaneScaffoldRole.Primary -> primaryPaneMotion
@@ -131,6 +231,16 @@
internal fun toPaneMotionList(ltrOrder: ThreePaneScaffoldHorizontalOrder): List<PaneMotion> =
listOf(this[ltrOrder.firstPane], this[ltrOrder.secondPane], this[ltrOrder.thirdPane])
+
+ companion object {
+ /** A default [ThreePaneMotion] instance that specifies no motions. */
+ val NoMotion =
+ ThreePaneMotion(
+ DefaultPaneMotion.NoMotion,
+ DefaultPaneMotion.NoMotion,
+ DefaultPaneMotion.NoMotion
+ )
+ }
}
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@@ -250,18 +360,23 @@
}
}
+/** The default settings of three pane motions. */
@ExperimentalMaterial3AdaptiveApi
-internal object ThreePaneMotionDefaults {
- // TODO(conradchen): open this to public when we support motion customization
- val PanePositionAnimationSpec: SpringSpec<IntOffset> =
+object ThreePaneMotionDefaults {
+ /** The default [FiniteAnimationSpec] of pane position animations. */
+ val PanePositionAnimationSpec: FiniteAnimationSpec<IntOffset> =
spring(
dampingRatio = 0.8f,
stiffness = 600f,
visibilityThreshold = IntOffset.VisibilityThreshold
)
- // TODO(conradchen): open this to public when we support motion customization
- val PanePositionAnimationSpecDelayed: DelayedSpringSpec<IntOffset> =
+ /**
+ * The default [FiniteAnimationSpec] of pane position animations with a delay. It's by default
+ * used in the case when an enter pane will intersect with exit panes, we delay the entering
+ * animation to emphasize the entering transition.
+ */
+ val PanePositionAnimationSpecDelayed: FiniteAnimationSpec<IntOffset> =
DelayedSpringSpec(
dampingRatio = 0.8f,
stiffness = 600f,
@@ -269,8 +384,8 @@
visibilityThreshold = IntOffset.VisibilityThreshold
)
- // TODO(conradchen): open this to public when we support motion customization
- val PaneSizeAnimationSpec: SpringSpec<IntSize> =
+ /** The default [FiniteAnimationSpec] of pane size animations. */
+ val PaneSizeAnimationSpec: FiniteAnimationSpec<IntSize> =
spring(
dampingRatio = 0.8f,
stiffness = 600f,
diff --git a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffold.kt b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffold.kt
index 62f8235..bebc466 100644
--- a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffold.kt
+++ b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffold.kt
@@ -65,6 +65,7 @@
* panes.
* @param scaffoldValue The current adapted value of the scaffold.
* @param paneOrder The horizontal order of the panes from start to end in the scaffold.
+ * @param paneMotions The specified motion of the panes.
* @param secondaryPane The content of the secondary pane that has a priority lower then the primary
* pane but higher than the tertiary pane.
* @param tertiaryPane The content of the tertiary pane that has the lowest priority.
@@ -110,7 +111,7 @@
paneOrder: ThreePaneScaffoldHorizontalOrder,
secondaryPane: @Composable ThreePaneScaffoldPaneScope.() -> Unit,
tertiaryPane: (@Composable ThreePaneScaffoldPaneScope.() -> Unit)? = null,
- paneMotions: ThreePaneMotion = calculateThreePaneMotion(scaffoldState.targetState, paneOrder),
+ paneMotions: ThreePaneMotion = scaffoldState.calculateThreePaneMotion(paneOrder),
paneExpansionState: PaneExpansionState = rememberPaneExpansionState(),
paneExpansionDragHandle: (@Composable ThreePaneScaffoldScope.(PaneExpansionState) -> Unit)? =
null,
diff --git a/compose/material3/adaptive/adaptive-navigation/api/current.txt b/compose/material3/adaptive/adaptive-navigation/api/current.txt
index ac6ed05..4612aff 100644
--- a/compose/material3/adaptive/adaptive-navigation/api/current.txt
+++ b/compose/material3/adaptive/adaptive-navigation/api/current.txt
@@ -2,8 +2,8 @@
package androidx.compose.material3.adaptive.navigation {
public final class AndroidThreePaneScaffold_androidKt {
- method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void NavigableListDetailPaneScaffold(androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator<java.lang.Object> 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 void NavigableSupportingPaneScaffold(androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator<java.lang.Object> 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);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void NavigableListDetailPaneScaffold(androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator<java.lang.Object> 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 androidx.compose.material3.adaptive.layout.ThreePaneMotion paneMotions, 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 NavigableSupportingPaneScaffold(androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator<java.lang.Object> 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 androidx.compose.material3.adaptive.layout.ThreePaneMotion paneMotions, 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);
}
@SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @kotlin.jvm.JvmInline public final value class BackNavigationBehavior {
diff --git a/compose/material3/adaptive/adaptive-navigation/api/restricted_current.txt b/compose/material3/adaptive/adaptive-navigation/api/restricted_current.txt
index ac6ed05..4612aff 100644
--- a/compose/material3/adaptive/adaptive-navigation/api/restricted_current.txt
+++ b/compose/material3/adaptive/adaptive-navigation/api/restricted_current.txt
@@ -2,8 +2,8 @@
package androidx.compose.material3.adaptive.navigation {
public final class AndroidThreePaneScaffold_androidKt {
- method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void NavigableListDetailPaneScaffold(androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator<java.lang.Object> 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 void NavigableSupportingPaneScaffold(androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator<java.lang.Object> 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);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void NavigableListDetailPaneScaffold(androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator<java.lang.Object> 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 androidx.compose.material3.adaptive.layout.ThreePaneMotion paneMotions, 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 NavigableSupportingPaneScaffold(androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator<java.lang.Object> 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 androidx.compose.material3.adaptive.layout.ThreePaneMotion paneMotions, 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);
}
@SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @kotlin.jvm.JvmInline public final value class BackNavigationBehavior {
diff --git a/compose/material3/adaptive/adaptive-navigation/src/androidMain/kotlin/androidx/compose/material3/adaptive/navigation/AndroidThreePaneScaffold.android.kt b/compose/material3/adaptive/adaptive-navigation/src/androidMain/kotlin/androidx/compose/material3/adaptive/navigation/AndroidThreePaneScaffold.android.kt
index 48ba07a..859c8b8 100644
--- a/compose/material3/adaptive/adaptive-navigation/src/androidMain/kotlin/androidx/compose/material3/adaptive/navigation/AndroidThreePaneScaffold.android.kt
+++ b/compose/material3/adaptive/adaptive-navigation/src/androidMain/kotlin/androidx/compose/material3/adaptive/navigation/AndroidThreePaneScaffold.android.kt
@@ -22,8 +22,11 @@
import androidx.compose.material3.adaptive.layout.PaneExpansionDragHandle
import androidx.compose.material3.adaptive.layout.PaneExpansionState
import androidx.compose.material3.adaptive.layout.SupportingPaneScaffold as BaseSupportingPaneScaffold
+import androidx.compose.material3.adaptive.layout.ThreePaneMotion
import androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope
import androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope
+import androidx.compose.material3.adaptive.layout.calculateListDetailPaneScaffoldMotion
+import androidx.compose.material3.adaptive.layout.calculateSupportingPaneScaffoldMotion
import androidx.compose.material3.adaptive.layout.rememberPaneExpansionState
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@@ -45,6 +48,8 @@
* mail app. See [ListDetailPaneScaffoldRole.Extra].
* @param defaultBackBehavior the default back navigation behavior when the system back event
* happens. See [BackNavigationBehavior] for the use cases of each behavior.
+ * @param paneMotions The specified motion of the panes. By default the value will be calculated by
+ * [calculateListDetailPaneScaffoldMotion] according to the target [ThreePaneScaffoldValue].
* @param paneExpansionDragHandle the pane expansion drag handle to let users be able to drag to
* change pane expansion state. Note that by default this argument will be `null`, and there won't
* be a drag handle rendered and users won't be able to drag to change the pane split. You can
@@ -62,6 +67,7 @@
modifier: Modifier = Modifier,
extraPane: (@Composable ThreePaneScaffoldPaneScope.() -> Unit)? = null,
defaultBackBehavior: BackNavigationBehavior = BackNavigationBehavior.PopUntilContentChange,
+ paneMotions: ThreePaneMotion = calculateListDetailPaneScaffoldMotion(navigator.scaffoldValue),
paneExpansionDragHandle: (@Composable ThreePaneScaffoldScope.(PaneExpansionState) -> Unit)? =
null,
paneExpansionState: PaneExpansionState = rememberPaneExpansionState(navigator.scaffoldValue),
@@ -77,6 +83,7 @@
detailPane = detailPane,
listPane = listPane,
extraPane = extraPane,
+ paneMotions = paneMotions,
paneExpansionDragHandle = paneExpansionDragHandle,
paneExpansionState = paneExpansionState,
)
@@ -98,6 +105,8 @@
* [SupportingPaneScaffoldRole.Extra].
* @param defaultBackBehavior the default back navigation behavior when the system back event
* happens. See [BackNavigationBehavior] for the use cases of each behavior.
+ * @param paneMotions The specified motion of the panes. By default the value will be calculated by
+ * [calculateSupportingPaneScaffoldMotion] according to the target [ThreePaneScaffoldValue].
* @param paneExpansionDragHandle the pane expansion drag handle to let users be able to drag to
* change pane expansion state. Note that by default this argument will be `null`, and there won't
* be a drag handle rendered and users won't be able to drag to change the pane split. You can
@@ -115,6 +124,7 @@
modifier: Modifier = Modifier,
extraPane: (@Composable ThreePaneScaffoldPaneScope.() -> Unit)? = null,
defaultBackBehavior: BackNavigationBehavior = BackNavigationBehavior.PopUntilContentChange,
+ paneMotions: ThreePaneMotion = calculateSupportingPaneScaffoldMotion(navigator.scaffoldValue),
paneExpansionDragHandle: (@Composable ThreePaneScaffoldScope.(PaneExpansionState) -> Unit)? =
null,
paneExpansionState: PaneExpansionState = rememberPaneExpansionState(navigator.scaffoldValue),
@@ -130,6 +140,7 @@
mainPane = mainPane,
supportingPane = supportingPane,
extraPane = extraPane,
+ paneMotions = paneMotions,
paneExpansionDragHandle = paneExpansionDragHandle,
paneExpansionState = paneExpansionState,
)
diff --git a/compose/material3/adaptive/adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/AndroidWindowAdaptiveInfo.android.kt b/compose/material3/adaptive/adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/AndroidWindowAdaptiveInfo.android.kt
index 9781752..f47f656 100644
--- a/compose/material3/adaptive/adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/AndroidWindowAdaptiveInfo.android.kt
+++ b/compose/material3/adaptive/adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/AndroidWindowAdaptiveInfo.android.kt
@@ -33,6 +33,7 @@
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
+@Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
actual fun currentWindowAdaptiveInfo(): WindowAdaptiveInfo {
val windowSize = with(LocalDensity.current) { currentWindowSize().toSize().toDpSize() }
return WindowAdaptiveInfo(
diff --git a/compose/material3/material3-adaptive-navigation-suite/samples/src/main/java/androidx/compose/material3/adaptive/navigationsuite/samples/NavigationSuiteScaffoldSamples.kt b/compose/material3/material3-adaptive-navigation-suite/samples/src/main/java/androidx/compose/material3/adaptive/navigationsuite/samples/NavigationSuiteScaffoldSamples.kt
index df28658..7d112c9 100644
--- a/compose/material3/material3-adaptive-navigation-suite/samples/src/main/java/androidx/compose/material3/adaptive/navigationsuite/samples/NavigationSuiteScaffoldSamples.kt
+++ b/compose/material3/material3-adaptive-navigation-suite/samples/src/main/java/androidx/compose/material3/adaptive/navigationsuite/samples/NavigationSuiteScaffoldSamples.kt
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+@file:Suppress("DEPRECATION") // Suppress for WindowWidthSizeClass
+
package androidx.compose.material3.adaptive.navigationsuite.samples
import androidx.annotation.Sampled
@@ -76,6 +78,7 @@
@Preview
@Sampled
@Composable
+@Suppress("DEPRECATION") // WindowWidthSizeClass is deprecated
fun NavigationSuiteScaffoldCustomConfigSample() {
var selectedItem by remember { mutableIntStateOf(0) }
val navItems = listOf("Songs", "Artists", "Playlists")
diff --git a/compose/material3/material3-adaptive-navigation-suite/src/androidUnitTest/kotlin/androidx/compose/material3/adaptive/navigationsuite/NavigationSuiteScaffoldTest.kt b/compose/material3/material3-adaptive-navigation-suite/src/androidUnitTest/kotlin/androidx/compose/material3/adaptive/navigationsuite/NavigationSuiteScaffoldTest.kt
index 1eb9fa1..a68b7ddc 100644
--- a/compose/material3/material3-adaptive-navigation-suite/src/androidUnitTest/kotlin/androidx/compose/material3/adaptive/navigationsuite/NavigationSuiteScaffoldTest.kt
+++ b/compose/material3/material3-adaptive-navigation-suite/src/androidUnitTest/kotlin/androidx/compose/material3/adaptive/navigationsuite/NavigationSuiteScaffoldTest.kt
@@ -30,6 +30,7 @@
class NavigationSuiteScaffoldTest {
@Test
+ @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
fun navigationLayoutTypeTest_compactWidth_compactHeight() {
val mockAdaptiveInfo =
createMockAdaptiveInfo(windowSizeClass = WindowSizeClass.compute(400f, 400f))
@@ -39,6 +40,7 @@
}
@Test
+ @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
fun navigationLayoutTypeTest_compactWidth_mediumHeight() {
val mockAdaptiveInfo =
createMockAdaptiveInfo(windowSizeClass = WindowSizeClass.compute(400f, 800f))
@@ -48,6 +50,7 @@
}
@Test
+ @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
fun navigationLayoutTypeTest_compactWidth_expandedHeight() {
val mockAdaptiveInfo =
createMockAdaptiveInfo(windowSizeClass = WindowSizeClass.compute(400f, 1000f))
@@ -57,6 +60,7 @@
}
@Test
+ @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
fun navigationLayoutTypeTest_mediumWidth_compactHeight() {
val mockAdaptiveInfo =
createMockAdaptiveInfo(windowSizeClass = WindowSizeClass.compute(800f, 400f))
@@ -66,6 +70,7 @@
}
@Test
+ @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
fun navigationLayoutTypeTest_mediumWidth_mediumHeight() {
val mockAdaptiveInfo =
createMockAdaptiveInfo(windowSizeClass = WindowSizeClass.compute(800f, 800f))
@@ -75,6 +80,7 @@
}
@Test
+ @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
fun navigationLayoutTypeTest_mediumWidth_expandedHeight() {
val mockAdaptiveInfo =
createMockAdaptiveInfo(windowSizeClass = WindowSizeClass.compute(800f, 1000f))
@@ -84,6 +90,7 @@
}
@Test
+ @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
fun navigationLayoutTypeTest_expandedWidth_compactHeight() {
val mockAdaptiveInfo =
createMockAdaptiveInfo(windowSizeClass = WindowSizeClass.compute(1000f, 400f))
@@ -93,6 +100,7 @@
}
@Test
+ @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
fun navigationLayoutTypeTest_expandedWidth_mediumHeight() {
val mockAdaptiveInfo =
createMockAdaptiveInfo(windowSizeClass = WindowSizeClass.compute(1000f, 800f))
@@ -102,6 +110,7 @@
}
@Test
+ @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
fun navigationLayoutTypeTest_expandedWidth_expandedHeight() {
val mockAdaptiveInfo =
createMockAdaptiveInfo(windowSizeClass = WindowSizeClass.compute(1000f, 1000f))
@@ -111,6 +120,7 @@
}
@Test
+ @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
fun navigationLayoutTypeTest_tableTop() {
val mockAdaptiveInfo =
createMockAdaptiveInfo(
@@ -123,6 +133,7 @@
}
@Test
+ @Suppress("DEPRECATION") // WindowSizeClass#compute is deprecated
fun navigationLayoutTypeTest_tableTop_expandedWidth() {
val mockAdaptiveInfo =
createMockAdaptiveInfo(
diff --git a/compose/material3/material3-adaptive-navigation-suite/src/commonMain/kotlin/androidx/compose/material3/adaptive/navigationsuite/NavigationSuiteScaffold.kt b/compose/material3/material3-adaptive-navigation-suite/src/commonMain/kotlin/androidx/compose/material3/adaptive/navigationsuite/NavigationSuiteScaffold.kt
index 5e96cd5..40758e0 100644
--- a/compose/material3/material3-adaptive-navigation-suite/src/commonMain/kotlin/androidx/compose/material3/adaptive/navigationsuite/NavigationSuiteScaffold.kt
+++ b/compose/material3/material3-adaptive-navigation-suite/src/commonMain/kotlin/androidx/compose/material3/adaptive/navigationsuite/NavigationSuiteScaffold.kt
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+@file:Suppress("DEPRECATION") // Suppress for imports of WindowWidthSizeClass
+
package androidx.compose.material3.adaptive.navigationsuite
import androidx.compose.foundation.interaction.Interaction
@@ -407,6 +409,7 @@
* @param adaptiveInfo the provided [WindowAdaptiveInfo]
* @see NavigationSuiteScaffold
*/
+ @Suppress("DEPRECATION") // WindowWidthSizeClass deprecated
fun calculateFromAdaptiveInfo(adaptiveInfo: WindowAdaptiveInfo): NavigationSuiteType {
return with(adaptiveInfo) {
if (
diff --git a/compose/ui/ui-geometry/api/current.txt b/compose/ui/ui-geometry/api/current.txt
index 9253299..7cfe3bd 100644
--- a/compose/ui/ui-geometry/api/current.txt
+++ b/compose/ui/ui-geometry/api/current.txt
@@ -94,12 +94,14 @@
}
@androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class Offset {
+ ctor public Offset(long packedValue);
method @androidx.compose.runtime.Stable public inline operator float component1();
method @androidx.compose.runtime.Stable public inline operator float component2();
method public long copy(optional float x, optional float y);
method @androidx.compose.runtime.Stable public operator long div(float operand);
method @androidx.compose.runtime.Stable public float getDistance();
method @androidx.compose.runtime.Stable public float getDistanceSquared();
+ method public long getPackedValue();
method public inline float getX();
method public inline float getY();
method @androidx.compose.runtime.Stable public inline boolean isValid();
@@ -108,6 +110,7 @@
method @androidx.compose.runtime.Stable public operator long rem(float operand);
method @androidx.compose.runtime.Stable public operator long times(float operand);
method @androidx.compose.runtime.Stable public inline operator long unaryMinus();
+ property public final long packedValue;
property @androidx.compose.runtime.Stable public final inline float x;
property @androidx.compose.runtime.Stable public final inline float y;
field public static final androidx.compose.ui.geometry.Offset.Companion Companion;
@@ -123,7 +126,7 @@
}
public final class OffsetKt {
- method @androidx.compose.runtime.Stable public static long Offset(float x, float y);
+ method @androidx.compose.runtime.Stable public static inline long Offset(float x, float y);
method public static inline boolean isFinite(long);
method public static inline boolean isSpecified(long);
method public static inline boolean isUnspecified(long);
@@ -267,6 +270,7 @@
}
@androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class Size {
+ ctor public Size(long packedValue);
method @androidx.compose.runtime.Stable public inline operator float component1();
method @androidx.compose.runtime.Stable public inline operator float component2();
method public long copy(optional float width, optional float height);
@@ -274,12 +278,14 @@
method public inline float getHeight();
method public float getMaxDimension();
method public float getMinDimension();
+ method public long getPackedValue();
method public inline float getWidth();
method @androidx.compose.runtime.Stable public boolean isEmpty();
method @androidx.compose.runtime.Stable public operator long times(float operand);
property @androidx.compose.runtime.Stable public final inline float height;
property @androidx.compose.runtime.Stable public final float maxDimension;
property @androidx.compose.runtime.Stable public final float minDimension;
+ property public final long packedValue;
property @androidx.compose.runtime.Stable public final inline float width;
field public static final androidx.compose.ui.geometry.Size.Companion Companion;
}
diff --git a/compose/ui/ui-geometry/api/restricted_current.txt b/compose/ui/ui-geometry/api/restricted_current.txt
index 4714d67..940e4ba 100644
--- a/compose/ui/ui-geometry/api/restricted_current.txt
+++ b/compose/ui/ui-geometry/api/restricted_current.txt
@@ -105,13 +105,14 @@
}
@androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class Offset {
- ctor @kotlin.PublishedApi internal Offset(@kotlin.PublishedApi long packedValue);
+ ctor public Offset(long packedValue);
method @androidx.compose.runtime.Stable public inline operator float component1();
method @androidx.compose.runtime.Stable public inline operator float component2();
method public long copy(optional float x, optional float y);
method @androidx.compose.runtime.Stable public operator long div(float operand);
method @androidx.compose.runtime.Stable public float getDistance();
method @androidx.compose.runtime.Stable public float getDistanceSquared();
+ method public long getPackedValue();
method public inline float getX();
method public inline float getY();
method @androidx.compose.runtime.Stable public inline boolean isValid();
@@ -120,6 +121,7 @@
method @androidx.compose.runtime.Stable public operator long rem(float operand);
method @androidx.compose.runtime.Stable public operator long times(float operand);
method @androidx.compose.runtime.Stable public inline operator long unaryMinus();
+ property public final long packedValue;
property @androidx.compose.runtime.Stable public final inline float x;
property @androidx.compose.runtime.Stable public final inline float y;
field public static final androidx.compose.ui.geometry.Offset.Companion Companion;
@@ -135,7 +137,7 @@
}
public final class OffsetKt {
- method @androidx.compose.runtime.Stable public static long Offset(float x, float y);
+ method @androidx.compose.runtime.Stable public static inline long Offset(float x, float y);
method public static inline boolean isFinite(long);
method public static inline boolean isSpecified(long);
method public static inline boolean isUnspecified(long);
@@ -279,7 +281,7 @@
}
@androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class Size {
- ctor @kotlin.PublishedApi internal Size(@kotlin.PublishedApi long packedValue);
+ ctor public Size(long packedValue);
method @androidx.compose.runtime.Stable public inline operator float component1();
method @androidx.compose.runtime.Stable public inline operator float component2();
method public long copy(optional float width, optional float height);
@@ -287,12 +289,14 @@
method public inline float getHeight();
method public float getMaxDimension();
method public float getMinDimension();
+ method public long getPackedValue();
method public inline float getWidth();
method @androidx.compose.runtime.Stable public boolean isEmpty();
method @androidx.compose.runtime.Stable public operator long times(float operand);
property @androidx.compose.runtime.Stable public final inline float height;
property @androidx.compose.runtime.Stable public final float maxDimension;
property @androidx.compose.runtime.Stable public final float minDimension;
+ property public final long packedValue;
property @androidx.compose.runtime.Stable public final inline float width;
field public static final androidx.compose.ui.geometry.Size.Companion Companion;
}
diff --git a/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/Offset.kt b/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/Offset.kt
index 92ed64e..100212d 100644
--- a/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/Offset.kt
+++ b/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/Offset.kt
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+@file:Suppress("NOTHING_TO_INLINE", "KotlinRedundantDiagnosticSuppress")
+
package androidx.compose.ui.geometry
import androidx.compose.runtime.Immutable
@@ -24,8 +26,8 @@
import androidx.compose.ui.util.unpackFloat2
import kotlin.math.sqrt
-/** Constructs an Offset from the given relative x and y offsets */
-@Stable fun Offset(x: Float, y: Float) = Offset(packFloats(x, y))
+/** Constructs an Offset from the given relative [x] and [y] offsets */
+@Stable inline fun Offset(x: Float, y: Float) = Offset(packFloats(x, y))
/**
* An immutable 2D floating-point offset.
@@ -44,15 +46,20 @@
* See also:
* * [Size], which represents a vector describing the size of a rectangle.
*
- * Creates an offset. The first argument sets [x], the horizontal component, and the second sets
- * [y], the vertical component.
+ * To create an [Offset], call the top-level function that accepts an x/y pair of coordinates:
+ * ```
+ * val offset = Offset(x, y)
+ * ```
+ *
+ * The primary constructor of [Offset] is intended to be used with the [packedValue] property to
+ * allow storing offsets in arrays or collections of primitives without boxing.
+ *
+ * @param packedValue [Long] value encoding the [x] and [y] components of the [Offset]. Encoded
+ * values can be obtained by using the [packedValue] property of existing [Offset] instances.
*/
-@Suppress("NOTHING_TO_INLINE")
@Immutable
@kotlin.jvm.JvmInline
-value class Offset
-@PublishedApi
-internal constructor(@PublishedApi internal val packedValue: Long) {
+value class Offset(val packedValue: Long) {
@Stable
inline val x: Float
get() = unpackFloat1(packedValue)
diff --git a/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/Size.kt b/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/Size.kt
index d47e8e4..e288fde 100644
--- a/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/Size.kt
+++ b/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/Size.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-@file:Suppress("NOTHING_TO_INLINE")
+@file:Suppress("NOTHING_TO_INLINE", "KotlinRedundantDiagnosticSuppress")
package androidx.compose.ui.geometry
@@ -36,10 +36,22 @@
* Holds a 2D floating-point size.
*
* You can think of this as an [Offset] from the origin.
+ *
+ * To create a [Size], call the top-level function that accepts a width/height pair of dimensions:
+ * ```
+ * val size = Size(width, height)
+ * ```
+ *
+ * The primary constructor of [Size] is intended to be used with the [packedValue] property to allow
+ * storing sizes in arrays or collections of primitives without boxing.
+ *
+ * @param packedValue [Long] value encoding the [width] and [height] components of the [Size].
+ * Encoded values can be obtained by using the [packedValue] property of existing [Size]
+ * instances.
*/
@Immutable
@kotlin.jvm.JvmInline
-value class Size @PublishedApi internal constructor(@PublishedApi internal val packedValue: Long) {
+value class Size(val packedValue: Long) {
@Stable
inline val width: Float
get() = unpackFloat1(packedValue)
@@ -57,7 +69,6 @@
Size(packFloats(width, height))
companion object {
-
/** An empty size, one with a zero width and a zero height. */
@Stable val Zero = Size(0x0L)
diff --git a/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/AccessibilityChecksTest.kt b/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/AccessibilityChecksTest.kt
index 30b464c..e1b9966 100644
--- a/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/AccessibilityChecksTest.kt
+++ b/compose/ui/ui-test/src/androidInstrumentedTest/kotlin/androidx/compose/ui/test/AccessibilityChecksTest.kt
@@ -111,7 +111,7 @@
@Composable
private fun BoxWithMissingContentDescription() {
Box(
- Modifier.size(48.dp).semantics {
+ Modifier.size(20.dp).semantics {
// The SemanticsModifier will make this node importantForAccessibility
// Having no content description is now a violation
this.contentDescription = ""
diff --git a/compose/ui/ui-unit/api/current.txt b/compose/ui/ui-unit/api/current.txt
index 08d8d65..c372803 100644
--- a/compose/ui/ui-unit/api/current.txt
+++ b/compose/ui/ui-unit/api/current.txt
@@ -94,7 +94,7 @@
}
public final class DpKt {
- method @androidx.compose.runtime.Stable public static long DpOffset(float x, float y);
+ method @androidx.compose.runtime.Stable public static inline long DpOffset(float x, float y);
method @androidx.compose.runtime.Stable public static long DpSize(float width, float height);
method @androidx.compose.runtime.Stable public static inline float coerceAtLeast(float, float minimumValue);
method @androidx.compose.runtime.Stable public static inline float coerceAtMost(float, float maximumValue);
@@ -129,11 +129,14 @@
}
@androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class DpOffset {
+ ctor public DpOffset(long packedValue);
method public long copy(optional float x, optional float y);
+ method public long getPackedValue();
method public float getX();
method public float getY();
method @androidx.compose.runtime.Stable public operator long minus(long other);
method @androidx.compose.runtime.Stable public operator long plus(long other);
+ property public final long packedValue;
property @androidx.compose.runtime.Stable public final float x;
property @androidx.compose.runtime.Stable public final float y;
field public static final androidx.compose.ui.unit.DpOffset.Companion Companion;
@@ -203,10 +206,12 @@
}
@androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class IntOffset {
+ ctor public IntOffset(long packedValue);
method @androidx.compose.runtime.Stable public inline operator int component1();
method @androidx.compose.runtime.Stable public inline operator int component2();
method public long copy(optional int x, optional int y);
method @androidx.compose.runtime.Stable public operator long div(float operand);
+ method public long getPackedValue();
method public int getX();
method public int getY();
method @androidx.compose.runtime.Stable public operator long minus(long other);
@@ -214,6 +219,7 @@
method @androidx.compose.runtime.Stable public operator long rem(int operand);
method @androidx.compose.runtime.Stable public operator long times(float operand);
method @androidx.compose.runtime.Stable public operator long unaryMinus();
+ property public final long packedValue;
property @androidx.compose.runtime.Stable public final int x;
property @androidx.compose.runtime.Stable public final int y;
field public static final androidx.compose.ui.unit.IntOffset.Companion Companion;
@@ -225,7 +231,7 @@
}
public final class IntOffsetKt {
- method @androidx.compose.runtime.Stable public static long IntOffset(int x, int y);
+ method @androidx.compose.runtime.Stable public static inline long IntOffset(int x, int y);
method @androidx.compose.runtime.Stable public static long lerp(long start, long stop, float fraction);
method @androidx.compose.runtime.Stable public static operator long minus(long, long offset);
method @androidx.compose.runtime.Stable public static operator long minus(long, long offset);
@@ -309,9 +315,11 @@
method @androidx.compose.runtime.Stable public inline operator int component2();
method @androidx.compose.runtime.Stable public operator long div(int other);
method public inline int getHeight();
+ method public long getPackedValue();
method public inline int getWidth();
method @androidx.compose.runtime.Stable public operator long times(int other);
property @androidx.compose.runtime.Stable public final inline int height;
+ property public final long packedValue;
property @androidx.compose.runtime.Stable public final inline int width;
field public static final androidx.compose.ui.unit.IntSize.Companion Companion;
}
diff --git a/compose/ui/ui-unit/api/restricted_current.txt b/compose/ui/ui-unit/api/restricted_current.txt
index 8732633..6f13e9f 100644
--- a/compose/ui/ui-unit/api/restricted_current.txt
+++ b/compose/ui/ui-unit/api/restricted_current.txt
@@ -94,7 +94,7 @@
}
public final class DpKt {
- method @androidx.compose.runtime.Stable public static long DpOffset(float x, float y);
+ method @androidx.compose.runtime.Stable public static inline long DpOffset(float x, float y);
method @androidx.compose.runtime.Stable public static long DpSize(float width, float height);
method @androidx.compose.runtime.Stable public static inline float coerceAtLeast(float, float minimumValue);
method @androidx.compose.runtime.Stable public static inline float coerceAtMost(float, float maximumValue);
@@ -129,11 +129,14 @@
}
@androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class DpOffset {
+ ctor public DpOffset(long packedValue);
method public long copy(optional float x, optional float y);
+ method public long getPackedValue();
method public float getX();
method public float getY();
method @androidx.compose.runtime.Stable public operator long minus(long other);
method @androidx.compose.runtime.Stable public operator long plus(long other);
+ property public final long packedValue;
property @androidx.compose.runtime.Stable public final float x;
property @androidx.compose.runtime.Stable public final float y;
field public static final androidx.compose.ui.unit.DpOffset.Companion Companion;
@@ -203,10 +206,12 @@
}
@androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class IntOffset {
+ ctor public IntOffset(long packedValue);
method @androidx.compose.runtime.Stable public inline operator int component1();
method @androidx.compose.runtime.Stable public inline operator int component2();
method public long copy(optional int x, optional int y);
method @androidx.compose.runtime.Stable public operator long div(float operand);
+ method public long getPackedValue();
method public int getX();
method public int getY();
method @androidx.compose.runtime.Stable public operator long minus(long other);
@@ -214,6 +219,7 @@
method @androidx.compose.runtime.Stable public operator long rem(int operand);
method @androidx.compose.runtime.Stable public operator long times(float operand);
method @androidx.compose.runtime.Stable public operator long unaryMinus();
+ property public final long packedValue;
property @androidx.compose.runtime.Stable public final int x;
property @androidx.compose.runtime.Stable public final int y;
field public static final androidx.compose.ui.unit.IntOffset.Companion Companion;
@@ -225,7 +231,7 @@
}
public final class IntOffsetKt {
- method @androidx.compose.runtime.Stable public static long IntOffset(int x, int y);
+ method @androidx.compose.runtime.Stable public static inline long IntOffset(int x, int y);
method @androidx.compose.runtime.Stable public static long lerp(long start, long stop, float fraction);
method @androidx.compose.runtime.Stable public static operator long minus(long, long offset);
method @androidx.compose.runtime.Stable public static operator long minus(long, long offset);
@@ -305,14 +311,16 @@
}
@androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public final value class IntSize {
- ctor @kotlin.PublishedApi internal IntSize(@kotlin.PublishedApi long packedValue);
+ ctor @kotlin.PublishedApi internal IntSize(long packedValue);
method @androidx.compose.runtime.Stable public inline operator int component1();
method @androidx.compose.runtime.Stable public inline operator int component2();
method @androidx.compose.runtime.Stable public operator long div(int other);
method public inline int getHeight();
+ method public long getPackedValue();
method public inline int getWidth();
method @androidx.compose.runtime.Stable public operator long times(int other);
property @androidx.compose.runtime.Stable public final inline int height;
+ property public final long packedValue;
property @androidx.compose.runtime.Stable public final inline int width;
field public static final androidx.compose.ui.unit.IntSize.Companion Companion;
}
diff --git a/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Dp.kt b/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Dp.kt
index 4cc9af8..45a4b6d 100644
--- a/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Dp.kt
+++ b/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Dp.kt
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-@file:Suppress("NOTHING_TO_INLINE")
+@file:Suppress("NOTHING_TO_INLINE", "KotlinRedundantDiagnosticSuppress")
package androidx.compose.ui.unit
@@ -181,12 +181,25 @@
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
/** Constructs a [DpOffset] from [x] and [y] position [Dp] values. */
-@Stable fun DpOffset(x: Dp, y: Dp): DpOffset = DpOffset(packFloats(x.value, y.value))
+@Stable inline fun DpOffset(x: Dp, y: Dp): DpOffset = DpOffset(packFloats(x.value, y.value))
-/** A two-dimensional offset using [Dp] for units */
+/**
+ * A two-dimensional offset using [Dp] for units.
+ *
+ * To create a [DpOffset], call the top-level function that accepts an x/y pair of coordinates:
+ * ```
+ * val offset = DpOffset(x, y)
+ * ```
+ *
+ * The primary constructor of [DpOffset] is intended to be used with the [packedValue] property to
+ * allow storing offsets in arrays or collections of primitives without boxing.
+ *
+ * @param packedValue [Long] value encoding the [x] and [y] components of the [DpOffset]. Encoded
+ * values can be obtained by using the [packedValue] property of existing [DpOffset] instances.
+ */
@Immutable
@JvmInline
-value class DpOffset internal constructor(@PublishedApi internal val packedValue: Long) {
+value class DpOffset(val packedValue: Long) {
/** The horizontal aspect of the offset in [Dp] */
@Stable
val x: Dp
diff --git a/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/IntOffset.kt b/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/IntOffset.kt
index 9bafe44..ca4caa3 100644
--- a/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/IntOffset.kt
+++ b/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/IntOffset.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-@file:Suppress("NOTHING_TO_INLINE")
+@file:Suppress("NOTHING_TO_INLINE", "KotlinRedundantDiagnosticSuppress")
package androidx.compose.ui.unit
@@ -29,12 +29,25 @@
import kotlin.jvm.JvmInline
/** Constructs a [IntOffset] from [x] and [y] position [Int] values. */
-@Stable fun IntOffset(x: Int, y: Int): IntOffset = IntOffset(packInts(x, y))
+@Stable inline fun IntOffset(x: Int, y: Int): IntOffset = IntOffset(packInts(x, y))
-/** A two-dimensional position using [Int] pixels for units */
+/**
+ * A two-dimensional position using [Int] pixels for units.
+ *
+ * To create an [IntOffset], call the top-level function that accepts an x/y pair of coordinates:
+ * ```
+ * val offset = IntOffset(x, y)
+ * ```
+ *
+ * The primary constructor of [IntOffset] is intended to be used with the [packedValue] property to
+ * allow storing offsets in arrays or collections of primitives without boxing.
+ *
+ * @param packedValue [Long] value encoding the [x] and [y] components of the [IntOffset]. Encoded
+ * values can be obtained by using the [packedValue] property of existing [IntOffset] instances.
+ */
@Immutable
@JvmInline
-value class IntOffset internal constructor(@PublishedApi internal val packedValue: Long) {
+value class IntOffset(val packedValue: Long) {
/** The horizontal aspect of the position in [Int] pixels. */
@Stable
val x: Int
diff --git a/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/IntSize.kt b/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/IntSize.kt
index 48c1d7d..021b42d 100644
--- a/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/IntSize.kt
+++ b/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/IntSize.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-@file:Suppress("NOTHING_TO_INLINE")
+@file:Suppress("NOTHING_TO_INLINE", "KotlinRedundantDiagnosticSuppress")
package androidx.compose.ui.unit
@@ -29,12 +29,25 @@
/** Constructs an [IntSize] from width and height [Int] values. */
@Stable inline fun IntSize(width: Int, height: Int): IntSize = IntSize(packInts(width, height))
-/** A two-dimensional size class used for measuring in [Int] pixels. */
+/**
+ * A two-dimensional size class used for measuring in [Int] pixels.
+ *
+ * To create an [IntSize], call the top-level function that accepts a width/height pair of
+ * dimensions:
+ * ```
+ * val size = IntSize(width, height)
+ * ```
+ *
+ * The primary constructor of [IntSize] is intended to be used with the [packedValue] property to
+ * allow storing sizes in arrays or collections of primitives without boxing.
+ *
+ * @param packedValue [Long] value encoding the [width] and [height] components of the [IntSize].
+ * Encoded values can be obtained by using the [packedValue] property of existing [IntSize]
+ * instances.
+ */
@Immutable
@kotlin.jvm.JvmInline
-value class IntSize
-@PublishedApi
-internal constructor(@PublishedApi internal val packedValue: Long) {
+value class IntSize @PublishedApi internal constructor(val packedValue: Long) {
/** The horizontal aspect of the size in [Int] pixels. */
@Stable
inline val width: Int
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/window/DialogTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/window/DialogTest.kt
index 5906b9f..5abbb34 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/window/DialogTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/window/DialogTest.kt
@@ -15,11 +15,8 @@
*/
package androidx.compose.ui.window
-import android.util.DisplayMetrics
+import android.content.res.Configuration
import android.view.KeyEvent
-import android.view.MotionEvent.ACTION_DOWN
-import android.view.MotionEvent.ACTION_UP
-import android.view.View
import androidx.activity.OnBackPressedDispatcher
import androidx.activity.compose.BackHandler
import androidx.activity.compose.LocalOnBackPressedDispatcherOwner
@@ -38,11 +35,8 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.geometry.lerp
-import androidx.compose.ui.gesture.MotionEvent
-import androidx.compose.ui.gesture.PointerProperties
-import androidx.compose.ui.input.pointer.PointerCoords
import androidx.compose.ui.layout.onSizeChanged
-import androidx.compose.ui.platform.LocalView
+import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.SemanticsNodeInteraction
import androidx.compose.ui.test.assertIsDisplayed
@@ -265,9 +259,9 @@
fun canFillScreenWidth_dependingOnProperty() {
var box1Width = 0
var box2Width = 0
- lateinit var displayMetrics: DisplayMetrics
+ lateinit var configuration: Configuration
rule.setContent {
- displayMetrics = LocalView.current.context.resources.displayMetrics
+ configuration = LocalConfiguration.current
Dialog(
onDismissRequest = {},
properties = DialogProperties(usePlatformDefaultWidth = false)
@@ -278,7 +272,7 @@
Box(Modifier.fillMaxSize().onSizeChanged { box2Width = it.width })
}
}
- val expectedWidth = with(rule.density) { displayMetrics.widthPixels }
+ val expectedWidth = with(rule.density) { configuration.screenWidthDp.dp.roundToPx() }
assertThat(box1Width).isEqualTo(expectedWidth)
assertThat(box2Width).isLessThan(box1Width)
}
@@ -319,75 +313,6 @@
}
}
- @Test
- fun dismissWhenClickingOutsideContent() {
- var dismissed = false
- var clicked = false
- lateinit var composeView: View
- val clickBoxTag = "clickBox"
- rule.setContent {
- Dialog(
- onDismissRequest = { dismissed = true },
- properties =
- DialogProperties(
- usePlatformDefaultWidth = false,
- decorFitsSystemWindows = false
- )
- ) {
- composeView = LocalView.current
- Box(Modifier.size(10.dp).testTag(clickBoxTag).clickable { clicked = true })
- }
- }
-
- // click inside the compose view
- rule.onNodeWithTag(clickBoxTag).performClick()
-
- rule.waitForIdle()
-
- assertThat(dismissed).isFalse()
- assertThat(clicked).isTrue()
-
- clicked = false
-
- // click outside the compose view
- rule.waitForIdle()
- var root = composeView
- while (root.parent is View) {
- root = root.parent as View
- }
-
- rule.runOnIdle {
- val x = root.width / 4f
- val y = root.height / 4f
- val down =
- MotionEvent(
- eventTime = 0,
- action = ACTION_DOWN,
- numPointers = 1,
- actionIndex = 0,
- pointerProperties = arrayOf(PointerProperties(0)),
- pointerCoords = arrayOf(PointerCoords(x, y)),
- root
- )
- root.dispatchTouchEvent(down)
- val up =
- MotionEvent(
- eventTime = 10,
- action = ACTION_UP,
- numPointers = 1,
- actionIndex = 0,
- pointerProperties = arrayOf(PointerProperties(0)),
- pointerCoords = arrayOf(PointerCoords(x, y)),
- root
- )
- root.dispatchTouchEvent(up)
- }
- rule.waitForIdle()
-
- assertThat(dismissed).isTrue()
- assertThat(clicked).isFalse()
- }
-
private fun setupDialogTest(
closeDialogOnDismiss: Boolean = true,
dialogProperties: DialogProperties = DialogProperties(),
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/window/DialogWithInsetsTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/window/DialogWithInsetsTest.kt
index cde57e5..e85b14c 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/window/DialogWithInsetsTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/window/DialogWithInsetsTest.kt
@@ -15,16 +15,12 @@
*/
package androidx.compose.ui.window
-import android.content.res.Configuration
-import android.os.Build
import android.view.View
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.ime
import androidx.compose.foundation.layout.imePadding
-import androidx.compose.foundation.layout.safeDrawing
-import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.material.TextField
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.Alignment
@@ -33,18 +29,10 @@
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.layout.layout
-import androidx.compose.ui.layout.onPlaced
import androidx.compose.ui.layout.onSizeChanged
-import androidx.compose.ui.layout.positionInRoot
import androidx.compose.ui.platform.LocalDensity
-import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.platform.LocalView
-import androidx.compose.ui.platform.SoftwareKeyboardController
-import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.junit4.createAndroidComposeRule
-import androidx.compose.ui.test.onNodeWithTag
-import androidx.compose.ui.test.requestFocus
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.LayoutDirection
import androidx.core.graphics.Insets
@@ -53,10 +41,8 @@
import androidx.core.view.WindowInsetsControllerCompat
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
-import com.google.common.truth.Truth.assertThat
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
-import kotlin.math.roundToInt
import org.junit.Assert.assertNotEquals
import org.junit.Rule
import org.junit.Test
@@ -143,90 +129,6 @@
assertNotEquals(Insets.NONE, imeInsets)
}
- @Test
- fun dialogCanTakeEntireScreen() {
- var size = IntSize.Zero
- var displayWidth = 0
- var displayHeight = 0
- var insetsLeft = 0
- var insetsTop = 0
- var insetsRight = 0
- var insetsBottom = 0
- var textTop = 0
- var controller: SoftwareKeyboardController? = null
- rule.setContent {
- val displayMetrics = LocalView.current.resources.displayMetrics
- controller = LocalSoftwareKeyboardController.current
- displayWidth = displayMetrics.widthPixels
- displayHeight = displayMetrics.heightPixels
- Box(Modifier.fillMaxSize()) {
- Dialog(
- {},
- properties =
- DialogProperties(
- decorFitsSystemWindows = false,
- usePlatformDefaultWidth = false
- )
- ) {
- val insets = WindowInsets.safeDrawing
-
- Box(
- Modifier.fillMaxSize()
- .layout { m, c ->
- val p = m.measure(c)
- size = IntSize(p.width, p.height)
- insetsTop = insets.getTop(this)
- insetsLeft = insets.getLeft(this, layoutDirection)
- insetsBottom = insets.getBottom(this)
- insetsRight = insets.getRight(this, layoutDirection)
- layout(p.width, p.height) { p.place(0, 0) }
- }
- .safeDrawingPadding()
- ) {
- TextField(
- value = "Hello",
- onValueChange = {},
- Modifier.align(Alignment.BottomStart).testTag("textField").onPlaced {
- layoutCoordinates ->
- textTop = layoutCoordinates.positionInRoot().y.roundToInt()
- }
- )
- }
- }
- }
- }
- rule.waitForIdle()
-
- if (
- Build.VERSION.SDK_INT >= 35 &&
- rule.activity.applicationContext.applicationInfo.targetSdkVersion >= 35
- ) {
- // On SDK >= 35, the metrics is the size of the entire screen
- assertThat(size.width).isEqualTo(displayWidth)
- assertThat(size.height).isEqualTo(displayHeight)
- } else {
- // On SDK < 35, the metrics is the size of the screen with some insets removed
- assertThat(size.width).isAtLeast(displayWidth)
- assertThat(size.height).isAtLeast(displayHeight)
- }
- // There is going to be some insets
- assertThat(maxOf(insetsLeft, insetsTop, insetsRight, insetsBottom)).isNotEqualTo(0)
-
- val hardKeyboardHidden =
- rule.runOnUiThread { rule.activity.resources.configuration.hardKeyboardHidden }
- if (hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO) {
- return // can't launch the IME when the hardware keyboard is up.
- }
- val bottomInsetsBeforeIme = insetsBottom
- val textTopBeforeIme = textTop
- rule.onNodeWithTag("textField").requestFocus()
- rule.waitUntil {
- controller?.show()
- insetsBottom != bottomInsetsBeforeIme
- }
- rule.runOnIdle { assertThat(textTop).isLessThan(textTopBeforeIme) }
- }
-
private fun findDialogWindowProviderInParent(view: View): DialogWindowProvider? {
if (view is DialogWindowProvider) {
return view
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 6fa2b45..72202ff 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
@@ -268,7 +268,7 @@
// flaky, so we use this callback to test accessibility events.
@VisibleForTesting
internal var onSendAccessibilityEvent: (AccessibilityEvent) -> Boolean = {
- trace("sendAccessibilityEvent") { view.parent.requestSendAccessibilityEvent(view, it) }
+ view.parent.requestSendAccessibilityEvent(view, it)
}
private val accessibilityManager: AccessibilityManager =
@@ -367,12 +367,9 @@
get() {
if (currentSemanticsNodesInvalidated) { // first instance of retrieving all nodes
currentSemanticsNodesInvalidated = false
- field =
- trace("generateCurrentSemanticsNodes") {
- view.semanticsOwner.getAllUncoveredSemanticsNodesToIntObjectMap()
- }
+ field = view.semanticsOwner.getAllUncoveredSemanticsNodesToIntObjectMap()
if (isEnabled) {
- trace("setTraversalValues") { setTraversalValues() }
+ setTraversalValues()
}
}
return field
@@ -497,43 +494,32 @@
}
private fun createNodeInfo(virtualViewId: Int): AccessibilityNodeInfoCompat? {
- trace("checkIfDestroyed") {
- if (
- view.viewTreeOwners?.lifecycleOwner?.lifecycle?.currentState ==
- Lifecycle.State.DESTROYED
- ) {
- return null
- }
+ if (
+ view.viewTreeOwners?.lifecycleOwner?.lifecycle?.currentState ==
+ Lifecycle.State.DESTROYED
+ ) {
+ return null
}
- val info: AccessibilityNodeInfoCompat =
- trace("createAccessibilityNodeInfoObject") { AccessibilityNodeInfoCompat.obtain() }
- val semanticsNodeWithAdjustedBounds =
- trace("calculateNodeWithAdjustedBounds") { currentSemanticsNodes[virtualViewId] }
- ?: return null
+ val info: AccessibilityNodeInfoCompat = AccessibilityNodeInfoCompat.obtain()
+ val semanticsNodeWithAdjustedBounds = currentSemanticsNodes[virtualViewId] ?: return null
val semanticsNode: SemanticsNode = semanticsNodeWithAdjustedBounds.semanticsNode
- trace("setParentForAccessibility") {
- if (virtualViewId == AccessibilityNodeProviderCompat.HOST_VIEW_ID) {
- info.setParent(view.getParentForAccessibility() as? View)
- } else {
- var parentId =
- checkPreconditionNotNull(semanticsNode.parent?.id) {
- "semanticsNode $virtualViewId has null parent"
- }
- if (parentId == view.semanticsOwner.unmergedRootSemanticsNode.id) {
- parentId = AccessibilityNodeProviderCompat.HOST_VIEW_ID
+ if (virtualViewId == AccessibilityNodeProviderCompat.HOST_VIEW_ID) {
+ info.setParent(view.getParentForAccessibility() as? View)
+ } else {
+ var parentId =
+ checkPreconditionNotNull(semanticsNode.parent?.id) {
+ "semanticsNode $virtualViewId has null parent"
}
- info.setParent(view, parentId)
+ if (parentId == view.semanticsOwner.unmergedRootSemanticsNode.id) {
+ parentId = AccessibilityNodeProviderCompat.HOST_VIEW_ID
}
+ info.setParent(view, parentId)
}
info.setSource(view, virtualViewId)
- trace("setBoundsInScreen") {
- info.setBoundsInScreen(boundsInScreen(semanticsNodeWithAdjustedBounds))
- }
+ info.setBoundsInScreen(boundsInScreen(semanticsNodeWithAdjustedBounds))
- trace("populateAccessibilityNodeInfoProperties") {
- populateAccessibilityNodeInfoProperties(virtualViewId, info, semanticsNode)
- }
+ populateAccessibilityNodeInfoProperties(virtualViewId, info, semanticsNode)
return info
}
@@ -1570,14 +1556,13 @@
@Suppress("DEPRECATION")
@VisibleForTesting
private fun createEvent(virtualViewId: Int, eventType: Int): AccessibilityEvent {
- val event: AccessibilityEvent =
- trace("obtainAccessibilityEvent") { AccessibilityEvent.obtain(eventType) }
+ val event: AccessibilityEvent = AccessibilityEvent.obtain(eventType)
event.isEnabled = true
event.className = ClassName
// Don't allow the client to override these properties.
- trace("event.packageName") { event.packageName = view.context.packageName }
- trace("event.setSource") { event.setSource(view, virtualViewId) }
+ event.packageName = view.context.packageName
+ event.setSource(view, virtualViewId)
if (isEnabled) {
// populate additional information from the node
@@ -2261,15 +2246,11 @@
if (isEnabled) {
for (i in subtreeChangedLayoutNodes.indices) {
val layoutNode = subtreeChangedLayoutNodes.valueAt(i)
- trace("sendSubtreeChangeAccessibilityEvents") {
- sendSubtreeChangeAccessibilityEvents(
- layoutNode,
- subtreeChangedSemanticsNodesIds
- )
- }
- trace("sendTypeViewScrolledAccessibilityEvent") {
- sendTypeViewScrolledAccessibilityEvent(layoutNode)
- }
+ sendSubtreeChangeAccessibilityEvents(
+ layoutNode,
+ subtreeChangedSemanticsNodesIds
+ )
+ sendTypeViewScrolledAccessibilityEvent(layoutNode)
}
subtreeChangedSemanticsNodesIds.clear()
// When the bounds of layout nodes change, we will not always get semantics
@@ -2369,22 +2350,19 @@
}
// When we finally send the event, make sure it is an accessibility-focusable node.
- val id =
- trace("GetSemanticsNode") {
- var semanticsNode =
- if (layoutNode.nodes.has(Nodes.Semantics)) layoutNode
- else layoutNode.findClosestParentNode { it.nodes.has(Nodes.Semantics) }
+ var semanticsNode =
+ if (layoutNode.nodes.has(Nodes.Semantics)) layoutNode
+ else layoutNode.findClosestParentNode { it.nodes.has(Nodes.Semantics) }
- val config = semanticsNode?.collapsedSemantics ?: return
- if (!config.isMergingSemanticsOfDescendants) {
- semanticsNode
- .findClosestParentNode {
- it.collapsedSemantics?.isMergingSemanticsOfDescendants == true
- }
- ?.let { semanticsNode = it }
+ val config = semanticsNode?.collapsedSemantics ?: return
+ if (!config.isMergingSemanticsOfDescendants) {
+ semanticsNode
+ .findClosestParentNode {
+ it.collapsedSemantics?.isMergingSemanticsOfDescendants == true
}
- semanticsNode?.semanticsId ?: return
- }
+ ?.let { semanticsNode = it }
+ }
+ val id = semanticsNode?.semanticsId ?: return
if (!subtreeChangedSemanticsNodesIds.add(id)) {
return
@@ -3175,11 +3153,9 @@
private inner class ComposeAccessibilityNodeProvider : AccessibilityNodeProviderCompat() {
override fun createAccessibilityNodeInfo(virtualViewId: Int): AccessibilityNodeInfoCompat? {
- return trace("createAccessibilityNodeInfo") {
- createNodeInfo(virtualViewId).also {
- if (sendingFocusAffectingEvent && virtualViewId == focusedVirtualViewId) {
- currentlyFocusedANI = it
- }
+ return createNodeInfo(virtualViewId).also {
+ if (sendingFocusAffectingEvent && virtualViewId == focusedVirtualViewId) {
+ currentlyFocusedANI = it
}
}
}
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidDialog.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidDialog.android.kt
index 475f5dba..83c30cd 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidDialog.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/window/AndroidDialog.android.kt
@@ -20,16 +20,13 @@
import android.graphics.Outline
import android.os.Build
import android.view.ContextThemeWrapper
-import android.view.Gravity
import android.view.KeyEvent
import android.view.MotionEvent
import android.view.View
-import android.view.View.OnLayoutChangeListener
import android.view.ViewGroup
import android.view.ViewOutlineProvider
import android.view.Window
import android.view.WindowManager
-import android.widget.FrameLayout
import androidx.activity.ComponentDialog
import androidx.activity.addCallback
import androidx.compose.runtime.Composable
@@ -60,11 +57,8 @@
import androidx.compose.ui.util.fastForEach
import androidx.compose.ui.util.fastMap
import androidx.compose.ui.util.fastMaxBy
-import androidx.core.view.OnApplyWindowInsetsListener
-import androidx.core.view.ViewCompat
+import androidx.compose.ui.util.fastRoundToInt
import androidx.core.view.WindowCompat
-import androidx.core.view.WindowInsetsAnimationCompat
-import androidx.core.view.WindowInsetsCompat
import androidx.lifecycle.findViewTreeLifecycleOwner
import androidx.lifecycle.findViewTreeViewModelStoreOwner
import androidx.lifecycle.setViewTreeLifecycleOwner
@@ -83,19 +77,16 @@
* @property securePolicy Policy for setting [WindowManager.LayoutParams.FLAG_SECURE] on the
* dialog's window.
* @property usePlatformDefaultWidth Whether the width of the dialog's content should be limited to
- * the platform default, which is smaller than the screen width. It is recommended to use
- * [decorFitsSystemWindows] set to `false` when [usePlatformDefaultWidth] is false to support
- * using the entire screen and avoiding UI glitches on some devices when the IME animates in.
+ * the platform default, which is smaller than the screen width.
* @property decorFitsSystemWindows Sets [WindowCompat.setDecorFitsSystemWindows] value. Set to
* `false` to use WindowInsets. If `false`, the
* [soft input mode][WindowManager.LayoutParams.softInputMode] will be changed to
* [WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE] and `android:windowIsFloating` is set to
- * `false` when [decorFitsSystemWindows] is false. When
- * `targetSdk` >= [Build.VERSION_CODES.VANILLA_ICE_CREAM], [decorFitsSystemWindows] can only be
- * `false` and this property doesn't have any effect.
+ * `false` for Android [R][Build.VERSION_CODES.R] and earlier.
*/
@Immutable
-actual class DialogProperties(
+actual class DialogProperties
+constructor(
actual val dismissOnBackPress: Boolean = true,
actual val dismissOnClickOutside: Boolean = true,
val securePolicy: SecureFlagPolicy = SecureFlagPolicy.Inherit,
@@ -227,7 +218,6 @@
private var content: @Composable () -> Unit by mutableStateOf({})
var usePlatformDefaultWidth = false
- var decorFitsSystemWindows = false
override var shouldCreateCompositionOnAttachedToWindow: Boolean = false
private set
@@ -239,16 +229,50 @@
createComposition()
}
+ override fun internalOnMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ if (usePlatformDefaultWidth) {
+ super.internalOnMeasure(widthMeasureSpec, heightMeasureSpec)
+ } else {
+ // usePlatformDefaultWidth false, so don't want to limit the dialog width to the Android
+ // platform default. Therefore, we create a new measure spec for width, which
+ // corresponds to the full screen width. We do the same for height, even if
+ // ViewRootImpl gives it to us from the first measure.
+ val displayWidthMeasureSpec =
+ MeasureSpec.makeMeasureSpec(displayWidth, MeasureSpec.AT_MOST)
+ val displayHeightMeasureSpec =
+ MeasureSpec.makeMeasureSpec(displayHeight, MeasureSpec.AT_MOST)
+ super.internalOnMeasure(displayWidthMeasureSpec, displayHeightMeasureSpec)
+ }
+ }
+
+ override fun internalOnLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
+ super.internalOnLayout(changed, left, top, right, bottom)
+ // Now set the content size as fixed layout params, such that ViewRootImpl knows
+ // the exact window size.
+ if (!usePlatformDefaultWidth) {
+ val child = getChildAt(0) ?: return
+ window.setLayout(child.measuredWidth, child.measuredHeight)
+ }
+ }
+
+ private val displayWidth: Int
+ get() {
+ val density = context.resources.displayMetrics.density
+ return (context.resources.configuration.screenWidthDp * density).fastRoundToInt()
+ }
+
+ private val displayHeight: Int
+ get() {
+ val density = context.resources.displayMetrics.density
+ return (context.resources.configuration.screenHeightDp * density).fastRoundToInt()
+ }
+
@Composable
override fun Content() {
content()
}
}
-private fun adjustedDecorFitsSystemWindows(dialogProperties: DialogProperties, context: Context) =
- dialogProperties.decorFitsSystemWindows &&
- context.applicationInfo.targetSdkVersion < Build.VERSION_CODES.VANILLA_ICE_CREAM
-
private class DialogWrapper(
private var onDismissRequest: () -> Unit,
private var properties: DialogProperties,
@@ -264,16 +288,16 @@
*/
ContextThemeWrapper(
composeView.context,
- if (adjustedDecorFitsSystemWindows(properties, composeView.context)) {
+ if (
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.S || properties.decorFitsSystemWindows
+ ) {
R.style.DialogWindowTheme
} else {
R.style.FloatingDialogWindowTheme
}
)
),
- ViewRootForInspector,
- OnApplyWindowInsetsListener,
- OnLayoutChangeListener {
+ ViewRootForInspector {
private val dialogLayout: DialogLayout
@@ -284,12 +308,15 @@
override val subCompositionView: AbstractComposeView
get() = dialogLayout
+ private val defaultSoftInputMode: Int
+
init {
val window = window ?: error("Dialog has no window")
+ defaultSoftInputMode =
+ window.attributes.softInputMode and WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST
window.requestFeature(Window.FEATURE_NO_TITLE)
window.setBackgroundDrawableResource(android.R.color.transparent)
- val decorFitsSystemWindows = adjustedDecorFitsSystemWindows(properties, context)
- WindowCompat.setDecorFitsSystemWindows(window, decorFitsSystemWindows)
+ WindowCompat.setDecorFitsSystemWindows(window, properties.decorFitsSystemWindows)
dialogLayout =
DialogLayout(context, window).apply {
// Set unique id for AbstractComposeView. This allows state restoration for the
@@ -309,8 +336,10 @@
override fun getOutline(view: View, result: Outline) {
result.setRect(0, 0, view.width, view.height)
// We set alpha to 0 to hide the view's shadow and let the composable to
- // draw its own shadow. This still enables us to get the extra space
- // needed in the surface.
+ // draw
+ // its own shadow. This still enables us to get the extra space needed
+ // in the
+ // surface.
result.alpha = 0f
}
}
@@ -330,38 +359,7 @@
// Turn of all clipping so shadows can be drawn outside the window
(window.decorView as? ViewGroup)?.disableClipping()
- // Center the ComposeView in a FrameLayout
- val frameLayout = FrameLayout(context)
- frameLayout.addView(
- dialogLayout,
- FrameLayout.LayoutParams(
- FrameLayout.LayoutParams.WRAP_CONTENT,
- FrameLayout.LayoutParams.WRAP_CONTENT
- )
- .also { it.gravity = Gravity.CENTER }
- )
- frameLayout.setOnClickListener { onDismissRequest() }
- ViewCompat.setOnApplyWindowInsetsListener(frameLayout, this)
- ViewCompat.setWindowInsetsAnimationCallback(
- frameLayout,
- object : WindowInsetsAnimationCompat.Callback(DISPATCH_MODE_CONTINUE_ON_SUBTREE) {
- override fun onProgress(
- insets: WindowInsetsCompat,
- runningAnimations: MutableList<WindowInsetsAnimationCompat>
- ): WindowInsetsCompat {
- return insets.inset(
- dialogLayout.left,
- dialogLayout.top,
- frameLayout.width - dialogLayout.right,
- frameLayout.height - dialogLayout.bottom
- )
- }
- }
- )
- dialogLayout.addOnLayoutChangeListener(this)
- frameLayout.addOnLayoutChangeListener(this)
-
- setContentView(frameLayout)
+ setContentView(dialogLayout)
dialogLayout.setViewTreeLifecycleOwner(composeView.findViewTreeLifecycleOwner())
dialogLayout.setViewTreeViewModelStoreOwner(composeView.findViewTreeViewModelStoreOwner())
dialogLayout.setViewTreeSavedStateRegistryOwner(
@@ -432,42 +430,21 @@
this.properties = properties
setSecurePolicy(properties.securePolicy)
setLayoutDirection(layoutDirection)
+ if (properties.usePlatformDefaultWidth && !dialogLayout.usePlatformDefaultWidth) {
+ // Undo fixed size in internalOnLayout, which would suppress size changes when
+ // usePlatformDefaultWidth is true.
+ window?.setLayout(
+ WindowManager.LayoutParams.WRAP_CONTENT,
+ WindowManager.LayoutParams.WRAP_CONTENT
+ )
+ }
dialogLayout.usePlatformDefaultWidth = properties.usePlatformDefaultWidth
- val decorFitsSystemWindows = adjustedDecorFitsSystemWindows(properties, context)
- dialogLayout.decorFitsSystemWindows = decorFitsSystemWindows
- val window = window
- if (window != null) {
- val softInput =
- when {
- decorFitsSystemWindows ->
- WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED
- Build.VERSION.SDK_INT < Build.VERSION_CODES.S ->
- @Suppress("DEPRECATION") WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
- else -> WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING
- }
- window.setSoftInputMode(softInput)
- val attrs = window.attributes
- val measurementWidth =
- if (properties.usePlatformDefaultWidth) {
- WindowManager.LayoutParams.WRAP_CONTENT
- } else {
- WindowManager.LayoutParams.MATCH_PARENT
- }
- val measurementHeight =
- if (properties.usePlatformDefaultWidth || decorFitsSystemWindows) {
- WindowManager.LayoutParams.WRAP_CONTENT
- } else {
- WindowManager.LayoutParams.MATCH_PARENT
- }
- if (
- attrs.width != measurementWidth ||
- attrs.height != measurementHeight ||
- attrs.gravity != Gravity.CENTER
- ) {
- attrs.width = measurementWidth
- attrs.height = measurementHeight
- attrs.gravity = Gravity.CENTER
- window.attributes = attrs
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
+ if (properties.decorFitsSystemWindows) {
+ window?.setSoftInputMode(defaultSoftInputMode)
+ } else {
+ @Suppress("DEPRECATION")
+ window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
}
}
}
@@ -489,28 +466,6 @@
// Prevents the dialog from dismissing itself
return
}
-
- override fun onApplyWindowInsets(v: View, insets: WindowInsetsCompat): WindowInsetsCompat {
- val left = dialogLayout.left
- val top = dialogLayout.top
- val right = v.width - dialogLayout.right
- val bottom = v.height - dialogLayout.bottom
- return insets.inset(left, top, right, bottom)
- }
-
- override fun onLayoutChange(
- v: View,
- left: Int,
- top: Int,
- right: Int,
- bottom: Int,
- oldLeft: Int,
- oldTop: Int,
- oldRight: Int,
- oldBottom: Int
- ) {
- v.requestApplyInsets()
- }
}
@Composable
diff --git a/compose/ui/ui/src/androidMain/res/values/styles.xml b/compose/ui/ui/src/androidMain/res/values/styles.xml
index d0e837b..e1211d4 100644
--- a/compose/ui/ui/src/androidMain/res/values/styles.xml
+++ b/compose/ui/ui/src/androidMain/res/values/styles.xml
@@ -19,13 +19,11 @@
<style name="DialogWindowTheme">
<item name="android:windowClipToOutline">false</item>
</style>
- <!-- Style for decorFitsSystemWindows = false -->
+ <!-- Style for decorFitsSystemWindows = false on API 30 and earlier. WindowInsets won't
+ be set on Dialogs without android:windowIsFloating set to false. -->
<style name="FloatingDialogWindowTheme">
<item name="android:windowClipToOutline">false</item>
<item name="android:dialogTheme">@style/FloatingDialogTheme</item>
- <item name="android:statusBarColor">@android:color/transparent</item>
- <item name="android:navigationBarColor">@android:color/transparent</item>
- <item name="android:backgroundDimEnabled">true</item>
</style>
<style name="FloatingDialogTheme">
<item name="android:windowIsFloating">false</item>
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 2403769..92695e5 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
@@ -60,7 +60,6 @@
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.LayoutDirection
-import androidx.compose.ui.util.trace
import androidx.compose.ui.viewinterop.InteropView
import androidx.compose.ui.viewinterop.InteropViewFactoryHolder
@@ -409,27 +408,25 @@
// whether or not deactivated nodes should be considered removed or not.
if (!isAttached || isDeactivated) return null
- trace("collapseSemantics") {
- if (!nodes.has(Nodes.Semantics) || _collapsedSemantics != null) {
- return _collapsedSemantics
- }
-
- var config = SemanticsConfiguration()
- requireOwner().snapshotObserver.observeSemanticsReads(this) {
- nodes.tailToHead(Nodes.Semantics) {
- if (it.shouldClearDescendantSemantics) {
- config = SemanticsConfiguration()
- config.isClearingSemantics = true
- }
- if (it.shouldMergeDescendantSemantics) {
- config.isMergingSemanticsOfDescendants = true
- }
- with(config) { with(it) { applySemantics() } }
- }
- }
- _collapsedSemantics = config
- return config
+ if (!nodes.has(Nodes.Semantics) || _collapsedSemantics != null) {
+ return _collapsedSemantics
}
+
+ var config = SemanticsConfiguration()
+ requireOwner().snapshotObserver.observeSemanticsReads(this) {
+ nodes.tailToHead(Nodes.Semantics) {
+ if (it.shouldClearDescendantSemantics) {
+ config = SemanticsConfiguration()
+ config.isClearingSemantics = true
+ }
+ if (it.shouldMergeDescendantSemantics) {
+ config.isMergingSemanticsOfDescendants = true
+ }
+ with(config) { with(it) { applySemantics() } }
+ }
+ }
+ _collapsedSemantics = config
+ return config
}
/**
diff --git a/core/core-telecom/src/main/res/values-af/strings.xml b/core/core-telecom/src/main/res/values-af/strings.xml
new file mode 100644
index 0000000..e5c55e0
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-af/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Oorstuk"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Kabelkopstuk"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Luidspreker"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-am/strings.xml b/core/core-telecom/src/main/res/values-am/strings.xml
new file mode 100644
index 0000000..66301fb
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-am/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"ማዳመጫ"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"ባለ ገመድ ማዳመጫ"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"ድምፅ ማውጫ"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-ar/strings.xml b/core/core-telecom/src/main/res/values-ar/strings.xml
new file mode 100644
index 0000000..8963293
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-ar/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"سماعة أذن"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"سماعة رأس سلكية"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"مكبِّر صوت"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-as/strings.xml b/core/core-telecom/src/main/res/values-as/strings.xml
new file mode 100644
index 0000000..697b692
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-as/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"ইয়েৰপিচ"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"তাঁৰযুক্ত হেডছেট"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"স্পীকাৰ"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-az/strings.xml b/core/core-telecom/src/main/res/values-az/strings.xml
new file mode 100644
index 0000000..e62a4d0
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-az/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Qulaqlıq"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Simli qulaqlıq"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Dinamik"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-b+sr+Latn/strings.xml b/core/core-telecom/src/main/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..6afc996
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Slušalica"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Žičane slušalice"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Zvučnik"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-be/strings.xml b/core/core-telecom/src/main/res/values-be/strings.xml
new file mode 100644
index 0000000..cb6122ec
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-be/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Дынамік"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Правадная гарнітура"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Гучная сувязь"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-bn/strings.xml b/core/core-telecom/src/main/res/values-bn/strings.xml
new file mode 100644
index 0000000..5acdea3
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-bn/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"ইয়ারপিস"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"ওয়্যার্ড হেডসেট"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"স্পিকার"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-bs/strings.xml b/core/core-telecom/src/main/res/values-bs/strings.xml
new file mode 100644
index 0000000..6afc996
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-bs/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Slušalica"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Žičane slušalice"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Zvučnik"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-ca/strings.xml b/core/core-telecom/src/main/res/values-ca/strings.xml
new file mode 100644
index 0000000..4b8b784
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-ca/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Auricular"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Auriculars amb cable"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Altaveu"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-cs/strings.xml b/core/core-telecom/src/main/res/values-cs/strings.xml
new file mode 100644
index 0000000..9bafe17
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-cs/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Sluchátko"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Kabelová náhlavní souprava"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Reproduktor"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-da/strings.xml b/core/core-telecom/src/main/res/values-da/strings.xml
new file mode 100644
index 0000000..257ec4e
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-da/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Højttaler"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Headset med ledning"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Højttaler"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-de/strings.xml b/core/core-telecom/src/main/res/values-de/strings.xml
new file mode 100644
index 0000000..0ce2c07
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-de/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Kopfhörer"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Kabelgebundenes Headset"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Lautsprecher"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-el/strings.xml b/core/core-telecom/src/main/res/values-el/strings.xml
new file mode 100644
index 0000000..83770ab
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-el/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Ακουστικό τηλεφώνου"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Ενσύρματα ακουστικά"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Ηχείο"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-en-rAU/strings.xml b/core/core-telecom/src/main/res/values-en-rAU/strings.xml
new file mode 100644
index 0000000..ae0ae39
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-en-rAU/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Earpiece"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Wired headset"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Speaker"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-en-rGB/strings.xml b/core/core-telecom/src/main/res/values-en-rGB/strings.xml
new file mode 100644
index 0000000..ae0ae39
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-en-rGB/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Earpiece"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Wired headset"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Speaker"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-en-rIN/strings.xml b/core/core-telecom/src/main/res/values-en-rIN/strings.xml
new file mode 100644
index 0000000..ae0ae39
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-en-rIN/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Earpiece"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Wired headset"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Speaker"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-es-rUS/strings.xml b/core/core-telecom/src/main/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..1b87fa9
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-es-rUS/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Auricular"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Auriculares con cable"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Bocina"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-es/strings.xml b/core/core-telecom/src/main/res/values-es/strings.xml
new file mode 100644
index 0000000..5af28da
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-es/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Auricular"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Auriculares con cable"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Altavoz"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-et/strings.xml b/core/core-telecom/src/main/res/values-et/strings.xml
new file mode 100644
index 0000000..3d2cc0c
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-et/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Kuular"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Juhtmega peakomplekt"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Kõlar"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-eu/strings.xml b/core/core-telecom/src/main/res/values-eu/strings.xml
new file mode 100644
index 0000000..16cc5e9
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-eu/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Aurikularra"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Entzungailu kableduna"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Bozgorailua"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-fa/strings.xml b/core/core-telecom/src/main/res/values-fa/strings.xml
new file mode 100644
index 0000000..e159d7c
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-fa/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"گوشی"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"هدست سیمی"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"بلندگو"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-fr-rCA/strings.xml b/core/core-telecom/src/main/res/values-fr-rCA/strings.xml
new file mode 100644
index 0000000..33d0ca4
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-fr-rCA/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Écouteur"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Écouteurs filaires"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Haut-parleur"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-fr/strings.xml b/core/core-telecom/src/main/res/values-fr/strings.xml
new file mode 100644
index 0000000..efba6ec
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-fr/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Écouteur"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Casque filaire"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Haut-parleur"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-gl/strings.xml b/core/core-telecom/src/main/res/values-gl/strings.xml
new file mode 100644
index 0000000..73f3cf5
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-gl/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Auricular"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Auriculares con cable"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Altofalante"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-gu/strings.xml b/core/core-telecom/src/main/res/values-gu/strings.xml
new file mode 100644
index 0000000..eb7a9ee
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-gu/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"ઇયરપીસ"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"વાયર્ડ હૅડસેટ"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"સ્પીકર"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-hr/strings.xml b/core/core-telecom/src/main/res/values-hr/strings.xml
new file mode 100644
index 0000000..a812b93
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-hr/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Zvučnik"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Žičane slušalice"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Zvučnik"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-hu/strings.xml b/core/core-telecom/src/main/res/values-hu/strings.xml
new file mode 100644
index 0000000..1175ec2
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-hu/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Fülhallgató"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Vezetékes headset"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Hangszóró"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-hy/strings.xml b/core/core-telecom/src/main/res/values-hy/strings.xml
new file mode 100644
index 0000000..0f1a0d2
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-hy/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Լսափող"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Լարով ականջակալ"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Բարձրախոս"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-in/strings.xml b/core/core-telecom/src/main/res/values-in/strings.xml
new file mode 100644
index 0000000..8e053cc
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-in/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Earpiece"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Headset berkabel"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Speaker"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-is/strings.xml b/core/core-telecom/src/main/res/values-is/strings.xml
new file mode 100644
index 0000000..c0befd0
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-is/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Hátalari"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Höfuðtól með snúru"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Hátalari"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-iw/strings.xml b/core/core-telecom/src/main/res/values-iw/strings.xml
new file mode 100644
index 0000000..9ec7ee9
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-iw/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"אוזניה"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"אוזניות חוטיות"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"רמקול"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-kk/strings.xml b/core/core-telecom/src/main/res/values-kk/strings.xml
new file mode 100644
index 0000000..6867ece
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-kk/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Телефон динамигі"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Сымды гарнитура"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Динамик"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-km/strings.xml b/core/core-telecom/src/main/res/values-km/strings.xml
new file mode 100644
index 0000000..0fc75bb
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-km/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"ឧបករណ៍ស្ដាប់សំឡេង"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"កាសមានខ្សែ"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"ឧបករណ៍បំពងសំឡេង"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-kn/strings.xml b/core/core-telecom/src/main/res/values-kn/strings.xml
new file mode 100644
index 0000000..4b006c3
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-kn/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"ಇಯರ್ಪೀಸ್"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"ವೈಯರ್ಡ್ ಹೆಡ್ಸೆಟ್"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"ಸ್ಪೀಕರ್"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-ko/strings.xml b/core/core-telecom/src/main/res/values-ko/strings.xml
new file mode 100644
index 0000000..6dd6a7d
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-ko/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"스피커"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"유선 헤드셋"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"스피커"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-ky/strings.xml b/core/core-telecom/src/main/res/values-ky/strings.xml
new file mode 100644
index 0000000..4de5b55
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-ky/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Кулакчын"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Зымдуу гарнитура"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Динамик"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-lt/strings.xml b/core/core-telecom/src/main/res/values-lt/strings.xml
new file mode 100644
index 0000000..ab5b490
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-lt/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Garsiakalbis prie ausies"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Laidinės ausinės"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Garsiakalbis"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-lv/strings.xml b/core/core-telecom/src/main/res/values-lv/strings.xml
new file mode 100644
index 0000000..dbbe825
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-lv/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Auss skaļrunis"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Vadu austiņas"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Skaļrunis"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-mk/strings.xml b/core/core-telecom/src/main/res/values-mk/strings.xml
new file mode 100644
index 0000000..73a43de
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-mk/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Слушалка"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Жичени слушалки"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Звучник"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-mn/strings.xml b/core/core-telecom/src/main/res/values-mn/strings.xml
new file mode 100644
index 0000000..3f929e9
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-mn/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Чихний спикер"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Утастай чихэвч"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Чанга яригч"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-mr/strings.xml b/core/core-telecom/src/main/res/values-mr/strings.xml
new file mode 100644
index 0000000..343c6c2
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-mr/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"इअरपीस"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"वायर्ड हेडसेट"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"स्पीकर"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-ms/strings.xml b/core/core-telecom/src/main/res/values-ms/strings.xml
new file mode 100644
index 0000000..1ffead0
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-ms/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Alat dengar"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Set kepala berwayar"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Pembesar suara"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-my/strings.xml b/core/core-telecom/src/main/res/values-my/strings.xml
new file mode 100644
index 0000000..a62aea0
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-my/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"တယ်လီဖုန်းနားခွက်"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"ကြိုးတပ် မိုက်ခွက်ပါနားကြပ်"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"စပီကာ"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-nb/strings.xml b/core/core-telecom/src/main/res/values-nb/strings.xml
new file mode 100644
index 0000000..387722e
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-nb/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Ørehøyttaler"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Hodetelefoner med ledning"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Høyttaler"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-nl/strings.xml b/core/core-telecom/src/main/res/values-nl/strings.xml
new file mode 100644
index 0000000..c496439
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-nl/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Oortelefoon"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Bedrade headset"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Speaker"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-or/strings.xml b/core/core-telecom/src/main/res/values-or/strings.xml
new file mode 100644
index 0000000..2a480c7
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-or/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"ଇୟରପିସ"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"ତାରଯୁକ୍ତ ହେଡସେଟ"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"ସ୍ପିକର"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-pa/strings.xml b/core/core-telecom/src/main/res/values-pa/strings.xml
new file mode 100644
index 0000000..ebc4300
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-pa/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"ਈਅਰਪੀਸ"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"ਤਾਰ ਵਾਲਾ ਹੈੱਡਸੈੱਟ"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"ਸਪੀਕਰ"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-pl/strings.xml b/core/core-telecom/src/main/res/values-pl/strings.xml
new file mode 100644
index 0000000..768b794
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-pl/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Słuchawka"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Przewodowy zestaw słuchawkowy"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Głośnik"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-pt-rBR/strings.xml b/core/core-telecom/src/main/res/values-pt-rBR/strings.xml
new file mode 100644
index 0000000..de52295
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-pt-rBR/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Minifone de ouvido"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Fone de ouvido com fio"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Alto-falante"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-pt/strings.xml b/core/core-telecom/src/main/res/values-pt/strings.xml
new file mode 100644
index 0000000..de52295
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-pt/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Minifone de ouvido"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Fone de ouvido com fio"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Alto-falante"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-ro/strings.xml b/core/core-telecom/src/main/res/values-ro/strings.xml
new file mode 100644
index 0000000..a1b63da
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-ro/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Cască"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Set de căști-microfon cu fir"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Difuzor"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-ru/strings.xml b/core/core-telecom/src/main/res/values-ru/strings.xml
new file mode 100644
index 0000000..9f1c287
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-ru/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Наушник"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Проводная гарнитура"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Колонка"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-si/strings.xml b/core/core-telecom/src/main/res/values-si/strings.xml
new file mode 100644
index 0000000..748d5c2
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-si/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"සවන් කඩ"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"රැහැන්ගත කළ හෙඩ්සෙට්"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"ස්පීකරය"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-sk/strings.xml b/core/core-telecom/src/main/res/values-sk/strings.xml
new file mode 100644
index 0000000..b6075f4
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-sk/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Slúchadlo"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Káblové slúchadlá s mikrofónom"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Reproduktor"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-sl/strings.xml b/core/core-telecom/src/main/res/values-sl/strings.xml
new file mode 100644
index 0000000..5ca1886
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-sl/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Slušalka"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Žične slušalke z mikrofonom"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Zvočnik"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-sq/strings.xml b/core/core-telecom/src/main/res/values-sq/strings.xml
new file mode 100644
index 0000000..c932e8d
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-sq/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Receptor"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Kufje me tel"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Altoparlant"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-sr/strings.xml b/core/core-telecom/src/main/res/values-sr/strings.xml
new file mode 100644
index 0000000..199a682
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-sr/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Слушалица"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Жичане слушалице"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Звучник"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-sv/strings.xml b/core/core-telecom/src/main/res/values-sv/strings.xml
new file mode 100644
index 0000000..aec1523
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-sv/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Lur"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Kabelanslutet headset"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Högtalare"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-sw/strings.xml b/core/core-telecom/src/main/res/values-sw/strings.xml
new file mode 100644
index 0000000..af27136d
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-sw/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Spika ya sikioni"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Vifaa vya sauti vyenye waya"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Spika"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-ta/strings.xml b/core/core-telecom/src/main/res/values-ta/strings.xml
new file mode 100644
index 0000000..b3678bb
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-ta/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"ஒலி கேட்கும் பகுதி"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"வயர் ஹெட்செட்"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"ஸ்பீக்கர்"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-te/strings.xml b/core/core-telecom/src/main/res/values-te/strings.xml
new file mode 100644
index 0000000..e387597
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-te/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"ఇయర్పీస్"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"వైర్ ఉన్న హెడ్సెట్"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"స్పీకర్"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-th/strings.xml b/core/core-telecom/src/main/res/values-th/strings.xml
new file mode 100644
index 0000000..103ceb1
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-th/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"หูฟังโทรศัพท์"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"ชุดหูฟังแบบมีสาย"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"ลำโพง"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-tl/strings.xml b/core/core-telecom/src/main/res/values-tl/strings.xml
new file mode 100644
index 0000000..50ac56c
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-tl/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Earpiece"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Wired na headset"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Speaker"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-tr/strings.xml b/core/core-telecom/src/main/res/values-tr/strings.xml
new file mode 100644
index 0000000..600c6d2
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-tr/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Kulaklık"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Kablolu mikrofonlu kulaklık"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Hoparlör"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-uk/strings.xml b/core/core-telecom/src/main/res/values-uk/strings.xml
new file mode 100644
index 0000000..d8580f9
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-uk/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Динамік"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Дротова гарнітура"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Колонка"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-ur/strings.xml b/core/core-telecom/src/main/res/values-ur/strings.xml
new file mode 100644
index 0000000..d577f0b
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-ur/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"ایئر پیس"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"تار والا ہیڈ سیٹ"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"اسپیکر"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-uz/strings.xml b/core/core-telecom/src/main/res/values-uz/strings.xml
new file mode 100644
index 0000000..679f5dd
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-uz/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Quloq karnaychasi"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Simli garnitura"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Karnay"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-vi/strings.xml b/core/core-telecom/src/main/res/values-vi/strings.xml
new file mode 100644
index 0000000..a4c82f9
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-vi/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Loa tai nghe"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Tai nghe có dây"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Loa ngoài"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-zh-rCN/strings.xml b/core/core-telecom/src/main/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..0d4efb7
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-zh-rCN/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"手机听筒"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"有线头戴式耳机"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"扬声器"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-zh-rHK/strings.xml b/core/core-telecom/src/main/res/values-zh-rHK/strings.xml
new file mode 100644
index 0000000..4e99591
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-zh-rHK/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"聽筒"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"有線耳機"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"喇叭"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-zh-rTW/strings.xml b/core/core-telecom/src/main/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..4c586c7
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-zh-rTW/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"耳機"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"有線耳機"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"喇叭"</string>
+</resources>
diff --git a/core/core-telecom/src/main/res/values-zu/strings.xml b/core/core-telecom/src/main/res/values-zu/strings.xml
new file mode 100644
index 0000000..7aab3c5
--- /dev/null
+++ b/core/core-telecom/src/main/res/values-zu/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="callendpoint_name_earpiece" msgid="4519059065203201851">"Isipikha sendlebe"</string>
+ <string name="callendpoint_name_wiredheadset" msgid="6723516311603411573">"Iheadset enentambo"</string>
+ <string name="callendpoint_name_speaker" msgid="623806810712383295">"Isipikha"</string>
+</resources>
diff --git a/credentials/credentials-play-services-auth/build.gradle b/credentials/credentials-play-services-auth/build.gradle
index 30d02c4..63d01b8 100644
--- a/credentials/credentials-play-services-auth/build.gradle
+++ b/credentials/credentials-play-services-auth/build.gradle
@@ -21,6 +21,8 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
+
+import androidx.build.KotlinTarget
import androidx.build.LibraryType
plugins {
@@ -87,4 +89,5 @@
inceptionYear = "2022"
description = "sign into apps using play-services-auth library"
legacyDisableKotlinStrictApiMode = true
+ kotlinTarget = KotlinTarget.KOTLIN_1_9
}
diff --git a/credentials/credentials/build.gradle b/credentials/credentials/build.gradle
index eb89d1a..ce89b90 100644
--- a/credentials/credentials/build.gradle
+++ b/credentials/credentials/build.gradle
@@ -31,7 +31,7 @@
dependencies {
api("androidx.annotation:annotation:1.8.1")
- api(project(":biometric:biometric-ktx"))
+ api("androidx.biometric:biometric-ktx:1.4.0-alpha02")
api(libs.kotlinStdlib)
implementation(libs.kotlinCoroutinesCore)
implementation("androidx.core:core:1.15.0-alpha01")
diff --git a/datastore/datastore-core/build.gradle b/datastore/datastore-core/build.gradle
index f184764..d509e6a 100644
--- a/datastore/datastore-core/build.gradle
+++ b/datastore/datastore-core/build.gradle
@@ -28,8 +28,6 @@
plugins {
id("AndroidXPlugin")
id("com.android.library")
- id("com.google.protobuf")
- id ("kotlin-parcelize")
}
android {
@@ -42,26 +40,6 @@
namespace "androidx.datastore.core"
}
-protobuf {
- protoc {
- artifact = libs.protobufCompiler.get()
- }
- generateProtoTasks {
- all().each { task ->
- task.builtins {
- java {
- option "lite"
- }
- }
- }
- }
-}
-
-def protoDir = project.layout.projectDirectory.dir("src/androidInstrumentedTest/proto")
-tasks.named("extractAndroidTestProto").configure {
- it.inputFiles.from(project.files(protoDir))
-}
-
androidXMultiplatform {
jvm()
mac()
@@ -137,7 +115,6 @@
androidInstrumentedTest {
dependsOn(commonJvmTest)
dependencies {
- implementation(libs.protobufLite)
implementation(libs.truth)
implementation(project(":internal-testutils-truth"))
implementation(libs.testRunner)
diff --git a/datastore/datastore-core/src/androidInstrumentedTest/AndroidManifest.xml b/datastore/datastore-core/src/androidInstrumentedTest/AndroidManifest.xml
index c25da00..4ec52f2f 100644
--- a/datastore/datastore-core/src/androidInstrumentedTest/AndroidManifest.xml
+++ b/datastore/datastore-core/src/androidInstrumentedTest/AndroidManifest.xml
@@ -15,16 +15,5 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
- <application>
- <service android:name="androidx.datastore.core.twoWayIpc.TwoWayIpcService"
- android:enabled="true"
- android:exported="false"
- android:process=":TwoWayIpcService" />
- <service android:name="androidx.datastore.core.twoWayIpc.TwoWayIpcService2"
- android:enabled="true"
- android:exported="false"
- android:process=":TwoWayIpcService2" />
- </application>
-
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
</manifest>
diff --git a/datastore/datastore-preferences-core/build.gradle b/datastore/datastore-preferences-core/build.gradle
index efa4b1f..9dfab0a 100644
--- a/datastore/datastore-preferences-core/build.gradle
+++ b/datastore/datastore-preferences-core/build.gradle
@@ -21,6 +21,8 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
+
+import androidx.build.KotlinTarget
import androidx.build.LibraryType
import androidx.build.PlatformIdentifier
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
@@ -118,4 +120,5 @@
description = "Android Preferences DataStore without the Android Dependencies"
legacyDisableKotlinStrictApiMode = true
metalavaK2UastEnabled = false
+ kotlinTarget = KotlinTarget.KOTLIN_1_9
}
diff --git a/datastore/datastore-preferences-rxjava2/build.gradle b/datastore/datastore-preferences-rxjava2/build.gradle
index aef9070..30c019c 100644
--- a/datastore/datastore-preferences-rxjava2/build.gradle
+++ b/datastore/datastore-preferences-rxjava2/build.gradle
@@ -21,6 +21,8 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
+
+import androidx.build.KotlinTarget
import androidx.build.LibraryType
plugins {
@@ -68,4 +70,5 @@
inceptionYear = "2020"
description = "Android DataStore Core - contains wrappers for using DataStore using RxJava2"
legacyDisableKotlinStrictApiMode = true
+ kotlinTarget = KotlinTarget.KOTLIN_1_9
}
diff --git a/datastore/datastore-preferences-rxjava3/build.gradle b/datastore/datastore-preferences-rxjava3/build.gradle
index d168af4..b9208ea 100644
--- a/datastore/datastore-preferences-rxjava3/build.gradle
+++ b/datastore/datastore-preferences-rxjava3/build.gradle
@@ -21,6 +21,8 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
+
+import androidx.build.KotlinTarget
import androidx.build.LibraryType
plugins {
@@ -68,4 +70,5 @@
inceptionYear = "2020"
description = "Android DataStore Core - contains wrappers for using DataStore using RxJava2"
legacyDisableKotlinStrictApiMode = true
+ kotlinTarget = KotlinTarget.KOTLIN_1_9
}
diff --git a/datastore/datastore-preferences/build.gradle b/datastore/datastore-preferences/build.gradle
index 1bf3287..9fb7310 100644
--- a/datastore/datastore-preferences/build.gradle
+++ b/datastore/datastore-preferences/build.gradle
@@ -21,6 +21,8 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
+
+import androidx.build.KotlinTarget
import androidx.build.LibraryType
import androidx.build.PlatformIdentifier
@@ -98,4 +100,5 @@
inceptionYear = "2020"
description = "Android Preferences DataStore"
legacyDisableKotlinStrictApiMode = true
+ kotlinTarget = KotlinTarget.KOTLIN_1_9
}
diff --git a/datastore/integration-tests/testapp/build.gradle b/datastore/integration-tests/testapp/build.gradle
new file mode 100644
index 0000000..8266634
--- /dev/null
+++ b/datastore/integration-tests/testapp/build.gradle
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+plugins {
+ id("AndroidXPlugin")
+ id("com.android.library")
+ id("org.jetbrains.kotlin.android")
+ id("com.google.protobuf")
+ id ("kotlin-parcelize")
+}
+
+android {
+ namespace "androidx.datastore.testapp"
+}
+
+protobuf {
+ protoc {
+ artifact = libs.protobufCompiler.get()
+ }
+ generateProtoTasks {
+ all().each { task ->
+ task.builtins {
+ java {
+ option "lite"
+ }
+ }
+ }
+ }
+}
+
+dependencies {
+ implementation(libs.okio)
+ implementation(libs.protobufLite)
+ implementation("androidx.lifecycle:lifecycle-service:2.6.1")
+ implementation(project(":datastore:datastore-core"))
+ implementation(project(":datastore:datastore-core-okio"))
+
+ androidTestImplementation(libs.kotlinCoroutinesTest)
+ androidTestImplementation(libs.kotlinTest)
+ androidTestImplementation(libs.truth)
+ androidTestImplementation(libs.testRunner)
+ androidTestImplementation(libs.testCore)
+ androidTestImplementation(project(":internal-testutils-truth"))
+ androidTestImplementation(project(":internal-testutils-datastore"))
+ androidTestImplementation(project(":kruth:kruth"))
+}
diff --git a/datastore/integration-tests/testapp/src/androidTest/AndroidManifest.xml b/datastore/integration-tests/testapp/src/androidTest/AndroidManifest.xml
new file mode 100644
index 0000000..867fda8
--- /dev/null
+++ b/datastore/integration-tests/testapp/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+ <application>
+ <service android:name="androidx.datastore.testapp.twoWayIpc.TwoWayIpcService"
+ android:enabled="true"
+ android:exported="false"
+ android:process=":TwoWayIpcService" />
+ <service android:name="androidx.datastore.testapp.twoWayIpc.TwoWayIpcService2"
+ android:enabled="true"
+ android:exported="false"
+ android:process=":TwoWayIpcService2" />
+ </application>
+
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+</manifest>
diff --git a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/InterProcessCompletableTest.kt b/datastore/integration-tests/testapp/src/androidTest/java/androidx/datastore/testapp/multiprocess/InterProcessCompletableTest.kt
similarity index 89%
rename from datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/InterProcessCompletableTest.kt
rename to datastore/integration-tests/testapp/src/androidTest/java/androidx/datastore/testapp/multiprocess/InterProcessCompletableTest.kt
index 9623f58..c6233a5 100644
--- a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/InterProcessCompletableTest.kt
+++ b/datastore/integration-tests/testapp/src/androidTest/java/androidx/datastore/testapp/multiprocess/InterProcessCompletableTest.kt
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-package androidx.datastore.core.multiprocess
+package androidx.datastore.testapp.multiprocess
import android.os.Parcelable
-import androidx.datastore.core.twoWayIpc.InterProcessCompletable
-import androidx.datastore.core.twoWayIpc.IpcAction
-import androidx.datastore.core.twoWayIpc.IpcUnit
-import androidx.datastore.core.twoWayIpc.TwoWayIpcSubject
+import androidx.datastore.testapp.twoWayIpc.InterProcessCompletable
+import androidx.datastore.testapp.twoWayIpc.IpcAction
+import androidx.datastore.testapp.twoWayIpc.IpcUnit
+import androidx.datastore.testapp.twoWayIpc.TwoWayIpcSubject
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.async
import kotlinx.coroutines.yield
diff --git a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/MultiProcessDataStoreIpcTest.kt b/datastore/integration-tests/testapp/src/androidTest/java/androidx/datastore/testapp/multiprocess/MultiProcessDataStoreIpcTest.kt
similarity index 96%
rename from datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/MultiProcessDataStoreIpcTest.kt
rename to datastore/integration-tests/testapp/src/androidTest/java/androidx/datastore/testapp/multiprocess/MultiProcessDataStoreIpcTest.kt
index 86887fc..b13ee2d 100644
--- a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/MultiProcessDataStoreIpcTest.kt
+++ b/datastore/integration-tests/testapp/src/androidTest/java/androidx/datastore/testapp/multiprocess/MultiProcessDataStoreIpcTest.kt
@@ -14,21 +14,24 @@
* limitations under the License.
*/
-package androidx.datastore.core.multiprocess
+// Parcelize object is testing internal implementation of datastore-core library
+@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "CANNOT_OVERRIDE_INVISIBLE_MEMBER")
+
+package androidx.datastore.testapp.multiprocess
import androidx.datastore.core.CorruptionException
import androidx.datastore.core.CorruptionHandler
import androidx.datastore.core.IOException
import androidx.datastore.core.SharedCounter
-import androidx.datastore.core.multiprocess.ipcActions.ReadTextAction
-import androidx.datastore.core.multiprocess.ipcActions.SetTextAction
-import androidx.datastore.core.multiprocess.ipcActions.StorageVariant
-import androidx.datastore.core.multiprocess.ipcActions.createMultiProcessTestDatastore
-import androidx.datastore.core.multiprocess.ipcActions.datastore
-import androidx.datastore.core.twoWayIpc.InterProcessCompletable
-import androidx.datastore.core.twoWayIpc.IpcAction
-import androidx.datastore.core.twoWayIpc.IpcUnit
-import androidx.datastore.core.twoWayIpc.TwoWayIpcSubject
+import androidx.datastore.testapp.multiprocess.ipcActions.ReadTextAction
+import androidx.datastore.testapp.multiprocess.ipcActions.SetTextAction
+import androidx.datastore.testapp.multiprocess.ipcActions.StorageVariant
+import androidx.datastore.testapp.multiprocess.ipcActions.createMultiProcessTestDatastore
+import androidx.datastore.testapp.multiprocess.ipcActions.datastore
+import androidx.datastore.testapp.twoWayIpc.InterProcessCompletable
+import androidx.datastore.testapp.twoWayIpc.IpcAction
+import androidx.datastore.testapp.twoWayIpc.IpcUnit
+import androidx.datastore.testapp.twoWayIpc.TwoWayIpcSubject
import androidx.datastore.testing.TestMessageProto.FooProto
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CompletableDeferred
diff --git a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/MultiProcessTestRule.kt b/datastore/integration-tests/testapp/src/androidTest/java/androidx/datastore/testapp/multiprocess/MultiProcessTestRule.kt
similarity index 92%
rename from datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/MultiProcessTestRule.kt
rename to datastore/integration-tests/testapp/src/androidTest/java/androidx/datastore/testapp/multiprocess/MultiProcessTestRule.kt
index c2cbb5b..82df64d 100644
--- a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/MultiProcessTestRule.kt
+++ b/datastore/integration-tests/testapp/src/androidTest/java/androidx/datastore/testapp/multiprocess/MultiProcessTestRule.kt
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package androidx.datastore.core.multiprocess
+package androidx.datastore.testapp.multiprocess
-import androidx.datastore.core.twoWayIpc.TwoWayIpcConnection
-import androidx.datastore.core.twoWayIpc.TwoWayIpcService
-import androidx.datastore.core.twoWayIpc.TwoWayIpcService2
+import androidx.datastore.testapp.twoWayIpc.TwoWayIpcConnection
+import androidx.datastore.testapp.twoWayIpc.TwoWayIpcService
+import androidx.datastore.testapp.twoWayIpc.TwoWayIpcService2
import androidx.test.platform.app.InstrumentationRegistry
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.time.Duration.Companion.seconds
diff --git a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/MultipleDataStoresInMultipleProcessesTest.kt b/datastore/integration-tests/testapp/src/androidTest/java/androidx/datastore/testapp/multiprocess/MultipleDataStoresInMultipleProcessesTest.kt
similarity index 91%
rename from datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/MultipleDataStoresInMultipleProcessesTest.kt
rename to datastore/integration-tests/testapp/src/androidTest/java/androidx/datastore/testapp/multiprocess/MultipleDataStoresInMultipleProcessesTest.kt
index 00442d7..9b07814 100644
--- a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/MultipleDataStoresInMultipleProcessesTest.kt
+++ b/datastore/integration-tests/testapp/src/androidTest/java/androidx/datastore/testapp/multiprocess/MultipleDataStoresInMultipleProcessesTest.kt
@@ -14,19 +14,22 @@
* limitations under the License.
*/
-package androidx.datastore.core.multiprocess
+// Parcelize object is testing internal implementation of datastore-core library
+@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
-import androidx.datastore.core.multiprocess.ipcActions.ReadTextAction
-import androidx.datastore.core.multiprocess.ipcActions.SetTextAction
-import androidx.datastore.core.multiprocess.ipcActions.StorageVariant
-import androidx.datastore.core.multiprocess.ipcActions.createMultiProcessTestDatastore
-import androidx.datastore.core.multiprocess.ipcActions.datastore
-import androidx.datastore.core.twoWayIpc.CompositeServiceSubjectModel
-import androidx.datastore.core.twoWayIpc.InterProcessCompletable
-import androidx.datastore.core.twoWayIpc.IpcAction
-import androidx.datastore.core.twoWayIpc.IpcUnit
-import androidx.datastore.core.twoWayIpc.SubjectReadWriteProperty
-import androidx.datastore.core.twoWayIpc.TwoWayIpcSubject
+package androidx.datastore.testapp.multiprocess
+
+import androidx.datastore.testapp.multiprocess.ipcActions.ReadTextAction
+import androidx.datastore.testapp.multiprocess.ipcActions.SetTextAction
+import androidx.datastore.testapp.multiprocess.ipcActions.StorageVariant
+import androidx.datastore.testapp.multiprocess.ipcActions.createMultiProcessTestDatastore
+import androidx.datastore.testapp.multiprocess.ipcActions.datastore
+import androidx.datastore.testapp.twoWayIpc.CompositeServiceSubjectModel
+import androidx.datastore.testapp.twoWayIpc.InterProcessCompletable
+import androidx.datastore.testapp.twoWayIpc.IpcAction
+import androidx.datastore.testapp.twoWayIpc.IpcUnit
+import androidx.datastore.testapp.twoWayIpc.SubjectReadWriteProperty
+import androidx.datastore.testapp.twoWayIpc.TwoWayIpcSubject
import androidx.datastore.testing.TestMessageProto.FooProto
import androidx.kruth.assertThat
import kotlin.time.Duration.Companion.seconds
diff --git a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/TwoWayIpcTest.kt b/datastore/integration-tests/testapp/src/androidTest/java/androidx/datastore/testapp/multiprocess/TwoWayIpcTest.kt
similarity index 95%
rename from datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/TwoWayIpcTest.kt
rename to datastore/integration-tests/testapp/src/androidTest/java/androidx/datastore/testapp/multiprocess/TwoWayIpcTest.kt
index 3cf76e5..a6d77b6 100644
--- a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/TwoWayIpcTest.kt
+++ b/datastore/integration-tests/testapp/src/androidTest/java/androidx/datastore/testapp/multiprocess/TwoWayIpcTest.kt
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package androidx.datastore.core.multiprocess
+package androidx.datastore.testapp.multiprocess
import android.os.Parcelable
-import androidx.datastore.core.twoWayIpc.CompositeServiceSubjectModel
-import androidx.datastore.core.twoWayIpc.IpcAction
-import androidx.datastore.core.twoWayIpc.TwoWayIpcSubject
+import androidx.datastore.testapp.twoWayIpc.CompositeServiceSubjectModel
+import androidx.datastore.testapp.twoWayIpc.IpcAction
+import androidx.datastore.testapp.twoWayIpc.TwoWayIpcSubject
import com.google.common.truth.Truth.assertThat
import kotlinx.parcelize.Parcelize
import org.junit.Rule
diff --git a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/ProtoOkioSerializer.kt b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/ProtoOkioSerializer.kt
similarity index 95%
rename from datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/ProtoOkioSerializer.kt
rename to datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/ProtoOkioSerializer.kt
index cb319f1..6a291b8 100644
--- a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/ProtoOkioSerializer.kt
+++ b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/ProtoOkioSerializer.kt
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-package androidx.datastore.core
+package androidx.datastore.testapp
+import androidx.datastore.core.CorruptionException
import androidx.datastore.core.okio.OkioSerializer
import com.google.protobuf.ExtensionRegistryLite
import com.google.protobuf.InvalidProtocolBufferException
diff --git a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/ProtoSerializer.kt b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/ProtoSerializer.kt
similarity index 92%
rename from datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/ProtoSerializer.kt
rename to datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/ProtoSerializer.kt
index 587f24f..f9eb2e3 100644
--- a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/ProtoSerializer.kt
+++ b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/ProtoSerializer.kt
@@ -14,8 +14,10 @@
* limitations under the License.
*/
-package androidx.datastore.core
+package androidx.datastore.testapp
+import androidx.datastore.core.CorruptionException
+import androidx.datastore.core.Serializer
import com.google.protobuf.ExtensionRegistryLite
import com.google.protobuf.InvalidProtocolBufferException
import com.google.protobuf.MessageLite
diff --git a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/ipcActions/CreateDatastoreAction.kt b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/multiprocess/ipcActions/CreateDatastoreAction.kt
similarity index 87%
rename from datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/ipcActions/CreateDatastoreAction.kt
rename to datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/multiprocess/ipcActions/CreateDatastoreAction.kt
index 4890be9..cb3ef18 100644
--- a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/ipcActions/CreateDatastoreAction.kt
+++ b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/multiprocess/ipcActions/CreateDatastoreAction.kt
@@ -14,24 +14,28 @@
* limitations under the License.
*/
-package androidx.datastore.core.multiprocess.ipcActions
+// Parcelize object is testing internal implementation of datastore-core library
+@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
+package androidx.datastore.testapp.multiprocess.ipcActions
+
+import android.annotation.SuppressLint
import android.os.Parcelable
import androidx.datastore.core.CorruptionHandler
import androidx.datastore.core.DataStore
import androidx.datastore.core.DataStoreImpl
import androidx.datastore.core.FileStorage
import androidx.datastore.core.MultiProcessCoordinator
-import androidx.datastore.core.ProtoOkioSerializer
-import androidx.datastore.core.ProtoSerializer
import androidx.datastore.core.Serializer
import androidx.datastore.core.handlers.NoOpCorruptionHandler
import androidx.datastore.core.okio.OkioSerializer
import androidx.datastore.core.okio.OkioStorage
-import androidx.datastore.core.twoWayIpc.CompositeServiceSubjectModel
-import androidx.datastore.core.twoWayIpc.IpcAction
-import androidx.datastore.core.twoWayIpc.SubjectReadWriteProperty
-import androidx.datastore.core.twoWayIpc.TwoWayIpcSubject
+import androidx.datastore.testapp.ProtoOkioSerializer
+import androidx.datastore.testapp.ProtoSerializer
+import androidx.datastore.testapp.twoWayIpc.CompositeServiceSubjectModel
+import androidx.datastore.testapp.twoWayIpc.IpcAction
+import androidx.datastore.testapp.twoWayIpc.SubjectReadWriteProperty
+import androidx.datastore.testapp.twoWayIpc.TwoWayIpcSubject
import androidx.datastore.testing.TestMessageProto.FooProto
import com.google.protobuf.ExtensionRegistryLite
import java.io.File
@@ -117,6 +121,7 @@
)
}
+@SuppressLint("BanParcelableUsage")
@Parcelize
private class CreateDatastoreAction(
private val filePath: String,
diff --git a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/ipcActions/ReadTextAction.kt b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/multiprocess/ipcActions/ReadTextAction.kt
similarity index 67%
rename from datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/ipcActions/ReadTextAction.kt
rename to datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/multiprocess/ipcActions/ReadTextAction.kt
index eafa6b5..0162238 100644
--- a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/ipcActions/ReadTextAction.kt
+++ b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/multiprocess/ipcActions/ReadTextAction.kt
@@ -14,17 +14,23 @@
* limitations under the License.
*/
-package androidx.datastore.core.multiprocess.ipcActions
+// Parcelize object is testing internal implementation of datastore-core library
+@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
+package androidx.datastore.testapp.multiprocess.ipcActions
+
+import android.annotation.SuppressLint
import android.os.Parcelable
-import androidx.datastore.core.twoWayIpc.IpcAction
-import androidx.datastore.core.twoWayIpc.TwoWayIpcSubject
+import androidx.datastore.testapp.twoWayIpc.IpcAction
+import androidx.datastore.testapp.twoWayIpc.TwoWayIpcSubject
import kotlinx.coroutines.flow.first
import kotlinx.parcelize.Parcelize
@Parcelize
internal class ReadTextAction : IpcAction<ReadTextAction.TextValue>() {
- @Parcelize data class TextValue(val value: String) : Parcelable
+ @SuppressLint("BanParcelableUsage")
+ @Parcelize
+ data class TextValue(val value: String) : Parcelable
override suspend fun invokeInRemoteProcess(subject: TwoWayIpcSubject): TextValue {
return TextValue(subject.datastore.data.first().text)
diff --git a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/ipcActions/SetTextAction.kt b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/multiprocess/ipcActions/SetTextAction.kt
similarity index 73%
rename from datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/ipcActions/SetTextAction.kt
rename to datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/multiprocess/ipcActions/SetTextAction.kt
index ce98deb..929b23f 100644
--- a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/multiprocess/ipcActions/SetTextAction.kt
+++ b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/multiprocess/ipcActions/SetTextAction.kt
@@ -14,15 +14,20 @@
* limitations under the License.
*/
-package androidx.datastore.core.multiprocess.ipcActions
+// Parcelize object is testing internal implementation of datastore-core library
+@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
+package androidx.datastore.testapp.multiprocess.ipcActions
+
+import android.annotation.SuppressLint
import android.os.Parcelable
-import androidx.datastore.core.twoWayIpc.InterProcessCompletable
-import androidx.datastore.core.twoWayIpc.IpcAction
-import androidx.datastore.core.twoWayIpc.IpcUnit
-import androidx.datastore.core.twoWayIpc.TwoWayIpcSubject
+import androidx.datastore.testapp.twoWayIpc.InterProcessCompletable
+import androidx.datastore.testapp.twoWayIpc.IpcAction
+import androidx.datastore.testapp.twoWayIpc.IpcUnit
+import androidx.datastore.testapp.twoWayIpc.TwoWayIpcSubject
import kotlinx.parcelize.Parcelize
+@SuppressLint("BanParcelableUsage")
@Parcelize
internal class SetTextAction(
private val value: String,
diff --git a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/CompositeServiceSubjectModel.kt b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/CompositeServiceSubjectModel.kt
similarity index 94%
rename from datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/CompositeServiceSubjectModel.kt
rename to datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/CompositeServiceSubjectModel.kt
index 3b8dd64..741bb88 100644
--- a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/CompositeServiceSubjectModel.kt
+++ b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/CompositeServiceSubjectModel.kt
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-package androidx.datastore.core.twoWayIpc
+package androidx.datastore.testapp.twoWayIpc
+//noinspection BanConcurrentHashMap
import java.util.concurrent.ConcurrentHashMap
/**
diff --git a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/InterProcessCompletable.kt b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/InterProcessCompletable.kt
similarity index 96%
rename from datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/InterProcessCompletable.kt
rename to datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/InterProcessCompletable.kt
index 46eaffa..4e5d506 100644
--- a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/InterProcessCompletable.kt
+++ b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/InterProcessCompletable.kt
@@ -14,14 +14,16 @@
* limitations under the License.
*/
-package androidx.datastore.core.twoWayIpc
+package androidx.datastore.testapp.twoWayIpc
+import android.annotation.SuppressLint
import android.os.Parcelable
import java.util.UUID
import kotlinx.coroutines.CompletableDeferred
import kotlinx.parcelize.Parcelize
/** A [Parcelable] [CompletableDeferred] implementation that can be shared across processes. */
+@SuppressLint("BanParcelableUsage")
@Parcelize
internal class InterProcessCompletable<T : Parcelable>(
private val key: String = UUID.randomUUID().toString(),
diff --git a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/IpcAction.kt b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/IpcAction.kt
similarity index 85%
rename from datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/IpcAction.kt
rename to datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/IpcAction.kt
index fa2de84..086510f 100644
--- a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/IpcAction.kt
+++ b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/IpcAction.kt
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-package androidx.datastore.core.twoWayIpc
+package androidx.datastore.testapp.twoWayIpc
+import android.annotation.SuppressLint
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
@@ -25,4 +26,4 @@
}
/** Utility object for [IpcAction]s that do not return a value. */
-@Parcelize object IpcUnit : Parcelable
+@SuppressLint("BanParcelableUsage") @Parcelize object IpcUnit : Parcelable
diff --git a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/IpcLogger.kt b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/IpcLogger.kt
similarity index 96%
rename from datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/IpcLogger.kt
rename to datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/IpcLogger.kt
index 628ff8b..278f16d 100644
--- a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/IpcLogger.kt
+++ b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/IpcLogger.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.datastore.core.twoWayIpc
+package androidx.datastore.testapp.twoWayIpc
import android.app.Application
import android.os.Build
diff --git a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/TwoWayIpcBus.kt b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/TwoWayIpcBus.kt
similarity index 97%
rename from datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/TwoWayIpcBus.kt
rename to datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/TwoWayIpcBus.kt
index 4768068..3e84a15 100644
--- a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/TwoWayIpcBus.kt
+++ b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/TwoWayIpcBus.kt
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package androidx.datastore.core.twoWayIpc
+package androidx.datastore.testapp.twoWayIpc
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.os.Messenger
-import androidx.datastore.core.twoWayIpc.IpcLogger.log
+import androidx.datastore.testapp.twoWayIpc.IpcLogger.log
import java.util.UUID
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.CompletableDeferred
diff --git a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/TwoWayIpcConnection.kt b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/TwoWayIpcConnection.kt
similarity index 98%
rename from datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/TwoWayIpcConnection.kt
rename to datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/TwoWayIpcConnection.kt
index e153d5d..d37575e 100644
--- a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/TwoWayIpcConnection.kt
+++ b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/TwoWayIpcConnection.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.datastore.core.twoWayIpc
+package androidx.datastore.testapp.twoWayIpc
import android.content.ComponentName
import android.content.Context
diff --git a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/TwoWayIpcService.kt b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/TwoWayIpcService.kt
similarity index 95%
rename from datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/TwoWayIpcService.kt
rename to datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/TwoWayIpcService.kt
index 9834950..2fb7ec2 100644
--- a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/TwoWayIpcService.kt
+++ b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/TwoWayIpcService.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.datastore.core.twoWayIpc
+package androidx.datastore.testapp.twoWayIpc
import android.content.Intent
import android.os.Handler
@@ -44,7 +44,7 @@
* It properly scopes those subjects and destroys their scopes when the Service is destroyed,
* allowing tests to properly maintain resources.
*
- * @see androidx.datastore.core.multiprocess.MultiProcessTestRule
+ * @see androidx.datastore.testapp.multiprocess.MultiProcessTestRule
*/
open class TwoWayIpcService : LifecycleService() {
private val subjects = mutableListOf<TwoWayIpcSubject>()
@@ -89,6 +89,7 @@
)
override fun onBind(intent: Intent): IBinder? {
+ super.onBind(intent)
return messenger.binder
}
diff --git a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/TwoWayIpcSubject.kt b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/TwoWayIpcSubject.kt
similarity index 98%
rename from datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/TwoWayIpcSubject.kt
rename to datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/TwoWayIpcSubject.kt
index 8c6cd3f..c6689a3 100644
--- a/datastore/datastore-core/src/androidInstrumentedTest/kotlin/androidx/datastore/core/twoWayIpc/TwoWayIpcSubject.kt
+++ b/datastore/integration-tests/testapp/src/main/java/androidx/datastore/testapp/twoWayIpc/TwoWayIpcSubject.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.datastore.core.twoWayIpc
+package androidx.datastore.testapp.twoWayIpc
import android.os.Bundle
import android.os.Parcelable
diff --git a/datastore/datastore-core/src/androidInstrumentedTest/proto/test.proto b/datastore/integration-tests/testapp/src/main/proto/test.proto
similarity index 100%
rename from datastore/datastore-core/src/androidInstrumentedTest/proto/test.proto
rename to datastore/integration-tests/testapp/src/main/proto/test.proto
diff --git a/docs-public/build.gradle b/docs-public/build.gradle
index f0650b2..d1966329 100644
--- a/docs-public/build.gradle
+++ b/docs-public/build.gradle
@@ -223,31 +223,31 @@
docs("androidx.media2:media2-widget:1.3.0")
docs("androidx.media:media:1.7.0")
// androidx.media3 is not hosted in androidx
- docsWithoutApiSince("androidx.media3:media3-cast:1.4.0")
- docsWithoutApiSince("androidx.media3:media3-common:1.4.0")
- docsWithoutApiSince("androidx.media3:media3-container:1.4.0")
- docsWithoutApiSince("androidx.media3:media3-database:1.4.0")
- docsWithoutApiSince("androidx.media3:media3-datasource:1.4.0")
- docsWithoutApiSince("androidx.media3:media3-datasource-cronet:1.4.0")
- docsWithoutApiSince("androidx.media3:media3-datasource-okhttp:1.4.0")
- docsWithoutApiSince("androidx.media3:media3-datasource-rtmp:1.4.0")
- docsWithoutApiSince("androidx.media3:media3-decoder:1.4.0")
- docsWithoutApiSince("androidx.media3:media3-effect:1.4.0")
- docsWithoutApiSince("androidx.media3:media3-exoplayer:1.4.0")
- docsWithoutApiSince("androidx.media3:media3-exoplayer-dash:1.4.0")
- docsWithoutApiSince("androidx.media3:media3-exoplayer-hls:1.4.0")
- docsWithoutApiSince("androidx.media3:media3-exoplayer-ima:1.4.0")
- docsWithoutApiSince("androidx.media3:media3-exoplayer-rtsp:1.4.0")
- docsWithoutApiSince("androidx.media3:media3-exoplayer-smoothstreaming:1.4.0")
- docsWithoutApiSince("androidx.media3:media3-exoplayer-workmanager:1.4.0")
- docsWithoutApiSince("androidx.media3:media3-extractor:1.4.0")
- docsWithoutApiSince("androidx.media3:media3-muxer:1.4.0")
- docsWithoutApiSince("androidx.media3:media3-session:1.4.0")
- docsWithoutApiSince("androidx.media3:media3-test-utils:1.4.0")
- docsWithoutApiSince("androidx.media3:media3-test-utils-robolectric:1.4.0")
- docsWithoutApiSince("androidx.media3:media3-transformer:1.4.0")
- docsWithoutApiSince("androidx.media3:media3-ui:1.4.0")
- docsWithoutApiSince("androidx.media3:media3-ui-leanback:1.4.0")
+ docsWithoutApiSince("androidx.media3:media3-cast:1.4.1")
+ docsWithoutApiSince("androidx.media3:media3-common:1.4.1")
+ docsWithoutApiSince("androidx.media3:media3-container:1.4.1")
+ docsWithoutApiSince("androidx.media3:media3-database:1.4.1")
+ docsWithoutApiSince("androidx.media3:media3-datasource:1.4.1")
+ docsWithoutApiSince("androidx.media3:media3-datasource-cronet:1.4.1")
+ docsWithoutApiSince("androidx.media3:media3-datasource-okhttp:1.4.1")
+ docsWithoutApiSince("androidx.media3:media3-datasource-rtmp:1.4.1")
+ docsWithoutApiSince("androidx.media3:media3-decoder:1.4.1")
+ docsWithoutApiSince("androidx.media3:media3-effect:1.4.1")
+ docsWithoutApiSince("androidx.media3:media3-exoplayer:1.4.1")
+ docsWithoutApiSince("androidx.media3:media3-exoplayer-dash:1.4.1")
+ docsWithoutApiSince("androidx.media3:media3-exoplayer-hls:1.4.1")
+ docsWithoutApiSince("androidx.media3:media3-exoplayer-ima:1.4.1")
+ docsWithoutApiSince("androidx.media3:media3-exoplayer-rtsp:1.4.1")
+ docsWithoutApiSince("androidx.media3:media3-exoplayer-smoothstreaming:1.4.1")
+ docsWithoutApiSince("androidx.media3:media3-exoplayer-workmanager:1.4.1")
+ docsWithoutApiSince("androidx.media3:media3-extractor:1.4.1")
+ docsWithoutApiSince("androidx.media3:media3-muxer:1.4.1")
+ docsWithoutApiSince("androidx.media3:media3-session:1.4.1")
+ docsWithoutApiSince("androidx.media3:media3-test-utils:1.4.1")
+ docsWithoutApiSince("androidx.media3:media3-test-utils-robolectric:1.4.1")
+ docsWithoutApiSince("androidx.media3:media3-transformer:1.4.1")
+ docsWithoutApiSince("androidx.media3:media3-ui:1.4.1")
+ docsWithoutApiSince("androidx.media3:media3-ui-leanback:1.4.1")
docs("androidx.mediarouter:mediarouter:1.7.0")
docs("androidx.mediarouter:mediarouter-testing:1.7.0")
docs("androidx.metrics:metrics-performance:1.0.0-beta01")
diff --git a/emoji2/emoji2-emojipicker/src/main/res/values-ar/strings.xml b/emoji2/emoji2-emojipicker/src/main/res/values-ar/strings.xml
index 4d42ff9..a35d75c 100644
--- a/emoji2/emoji2-emojipicker/src/main/res/values-ar/strings.xml
+++ b/emoji2/emoji2-emojipicker/src/main/res/values-ar/strings.xml
@@ -18,7 +18,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="emoji_category_recent" msgid="7142376595414250279">"المستخدمة حديثًا"</string>
- <string name="emoji_category_emotions" msgid="1570830970240985537">"الوجوه المبتسمة والرموز التعبيرية"</string>
+ <string name="emoji_category_emotions" msgid="1570830970240985537">"الوجوه المبتسمة ورموز الإيموجي"</string>
<string name="emoji_category_people" msgid="7968173366822927025">"الأشخاص"</string>
<string name="emoji_category_animals_nature" msgid="4640771324837307541">"الحيوانات والطبيعة"</string>
<string name="emoji_category_food_drink" msgid="1189971856721244395">"المأكولات والمشروبات"</string>
@@ -27,11 +27,11 @@
<string name="emoji_category_objects" msgid="6106115586332708067">"عناصر متنوعة"</string>
<string name="emoji_category_symbols" msgid="5626171724310261787">"الرموز"</string>
<string name="emoji_category_flags" msgid="6185639503532784871">"الأعلام"</string>
- <string name="emoji_empty_non_recent_category" msgid="288822832574892625">"لا تتوفر أي رموز تعبيرية."</string>
- <string name="emoji_empty_recent_category" msgid="7863877827879290200">"لم تستخدم أي رموز تعبيرية حتى الآن."</string>
- <string name="emoji_bidirectional_switcher_content_desc" msgid="5084600168354220605">"مفتاح ثنائي الاتجاه للرموز التعبيرية"</string>
+ <string name="emoji_empty_non_recent_category" msgid="288822832574892625">"لا تتوفر أي رموز إيموجي."</string>
+ <string name="emoji_empty_recent_category" msgid="7863877827879290200">"لم تستخدم أي رموز إيموجي حتى الآن."</string>
+ <string name="emoji_bidirectional_switcher_content_desc" msgid="5084600168354220605">"مفتاح ثنائي الاتجاه لرموز الإيموجي"</string>
<string name="emoji_bidirectional_switcher_clicked_desc" msgid="5055290162204827523">"تم تغيير اتجاه الإيموجي"</string>
- <string name="emoji_variant_selector_content_desc" msgid="2898934883418401376">"أداة اختيار الرموز التعبيرية"</string>
+ <string name="emoji_variant_selector_content_desc" msgid="2898934883418401376">"أداة اختيار رموز الإيموجي"</string>
<string name="emoji_variant_content_desc_template" msgid="6381933050671041489">"%1$s و%2$s"</string>
<string name="emoji_skin_tone_shadow_content_desc" msgid="1759906883307507376">"الظل"</string>
<string name="emoji_skin_tone_light_content_desc" msgid="1052239040923092881">"بشرة فاتحة"</string>
diff --git a/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/LowLatencyCanvasViewTest.kt b/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/LowLatencyCanvasViewTest.kt
index dd8de283..299c232 100644
--- a/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/LowLatencyCanvasViewTest.kt
+++ b/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/LowLatencyCanvasViewTest.kt
@@ -115,7 +115,7 @@
val resumeLatch = CountDownLatch(1)
val drawLatch = CountDownLatch(1)
var lowLatencyView: LowLatencyCanvasView? = null
- scenario.moveToState(Lifecycle.State.RESUMED).onActivity {
+ scenario.onActivity {
val view = it.getLowLatencyCanvasView()
view.post { drawLatch.countDown() }
resumeLatch.countDown()
@@ -207,9 +207,7 @@
}
},
scenarioCallback = { scenario ->
- scenario.moveToState(Lifecycle.State.RESUMED).onActivity {
- it.getLowLatencyCanvasView().renderFrontBufferedLayer()
- }
+ scenario.onActivity { it.getLowLatencyCanvasView().renderFrontBufferedLayer() }
assertTrue(renderFrontBufferLatch.await(3000, TimeUnit.MILLISECONDS))
},
validateBitmap = { bitmap, left, top, right, bottom ->
diff --git a/graphics/graphics-core/src/androidTest/java/androidx/graphics/surface/SurfaceControlUtils.kt b/graphics/graphics-core/src/androidTest/java/androidx/graphics/surface/SurfaceControlUtils.kt
index 966c457..b417a45 100644
--- a/graphics/graphics-core/src/androidTest/java/androidx/graphics/surface/SurfaceControlUtils.kt
+++ b/graphics/graphics-core/src/androidTest/java/androidx/graphics/surface/SurfaceControlUtils.kt
@@ -49,36 +49,32 @@
var surfaceView: SurfaceView? = null
val destroyLatch = CountDownLatch(1)
val scenario =
- ActivityScenario.launch(SurfaceControlWrapperTestActivity::class.java)
- .moveToState(Lifecycle.State.CREATED)
- .onActivity {
- it.setDestroyCallback { destroyLatch.countDown() }
- val callback =
- object : SurfaceHolder.Callback {
- override fun surfaceCreated(sh: SurfaceHolder) {
- surfaceView = it.mSurfaceView
- onSurfaceCreated(surfaceView!!, setupLatch)
- }
-
- override fun surfaceChanged(
- holder: SurfaceHolder,
- format: Int,
- width: Int,
- height: Int
- ) {
- // NO-OP
- }
-
- override fun surfaceDestroyed(holder: SurfaceHolder) {
- // NO-OP
- }
+ ActivityScenario.launch(SurfaceControlWrapperTestActivity::class.java).onActivity {
+ it.setDestroyCallback { destroyLatch.countDown() }
+ val callback =
+ object : SurfaceHolder.Callback {
+ override fun surfaceCreated(sh: SurfaceHolder) {
+ surfaceView = it.mSurfaceView
+ onSurfaceCreated(surfaceView!!, setupLatch)
}
- it.addSurface(it.mSurfaceView, callback)
- surfaceView = it.mSurfaceView
- }
+ override fun surfaceChanged(
+ holder: SurfaceHolder,
+ format: Int,
+ width: Int,
+ height: Int
+ ) {
+ // NO-OP
+ }
- scenario.moveToState(Lifecycle.State.RESUMED)
+ override fun surfaceDestroyed(holder: SurfaceHolder) {
+ // NO-OP
+ }
+ }
+
+ it.addSurface(it.mSurfaceView, callback)
+ surfaceView = it.mSurfaceView
+ }
Assert.assertTrue(setupLatch.await(3000, TimeUnit.MILLISECONDS))
val coords = intArrayOf(0, 0)
diff --git a/graphics/graphics-core/src/androidTest/java/androidx/graphics/surface/SurfaceControlWrapperTest.kt b/graphics/graphics-core/src/androidTest/java/androidx/graphics/surface/SurfaceControlWrapperTest.kt
index 4a3e5b9..dec948e 100644
--- a/graphics/graphics-core/src/androidTest/java/androidx/graphics/surface/SurfaceControlWrapperTest.kt
+++ b/graphics/graphics-core/src/androidTest/java/androidx/graphics/surface/SurfaceControlWrapperTest.kt
@@ -137,9 +137,7 @@
fun testSurfaceTransactionOnCompleteCallback() {
val listener = TransactionOnCompleteListener()
- val scenario =
- ActivityScenario.launch(SurfaceControlWrapperTestActivity::class.java)
- .moveToState(Lifecycle.State.CREATED)
+ val scenario = ActivityScenario.launch(SurfaceControlWrapperTestActivity::class.java)
val destroyLatch = CountDownLatch(1)
try {
@@ -150,8 +148,6 @@
.commit()
}
- scenario.moveToState(Lifecycle.State.RESUMED)
-
listener.mLatch.await(3, TimeUnit.SECONDS)
assertEquals(0, listener.mLatch.count)
assertTrue(listener.mCallbackTime > 0)
@@ -167,9 +163,7 @@
fun testSurfaceTransactionOnCommitCallback() {
val listener = TransactionOnCommitListener()
- val scenario =
- ActivityScenario.launch(SurfaceControlWrapperTestActivity::class.java)
- .moveToState(Lifecycle.State.CREATED)
+ val scenario = ActivityScenario.launch(SurfaceControlWrapperTestActivity::class.java)
val destroyLatch = CountDownLatch(1)
try {
@@ -179,7 +173,6 @@
.addTransactionCommittedListener(executor!!, listener)
.commit()
}
- scenario.moveToState(Lifecycle.State.RESUMED)
listener.mLatch.await(3, TimeUnit.SECONDS)
assertEquals(0, listener.mLatch.count)
@@ -197,9 +190,7 @@
val listener = TransactionOnCommitListener()
val listener2 = TransactionOnCommitListener()
- val scenario =
- ActivityScenario.launch(SurfaceControlWrapperTestActivity::class.java)
- .moveToState(Lifecycle.State.CREATED)
+ val scenario = ActivityScenario.launch(SurfaceControlWrapperTestActivity::class.java)
val destroyLatch = CountDownLatch(1)
try {
@@ -211,8 +202,6 @@
.commit()
}
- scenario.moveToState(Lifecycle.State.RESUMED)
-
listener.mLatch.await(3, TimeUnit.SECONDS)
listener2.mLatch.await(3, TimeUnit.SECONDS)
@@ -234,9 +223,7 @@
val listener1 = TransactionOnCommitListener()
val listener2 = TransactionOnCompleteListener()
- val scenario =
- ActivityScenario.launch(SurfaceControlWrapperTestActivity::class.java)
- .moveToState(Lifecycle.State.CREATED)
+ val scenario = ActivityScenario.launch(SurfaceControlWrapperTestActivity::class.java)
val destroyLatch = CountDownLatch(1)
try {
@@ -248,8 +235,6 @@
.commit()
}
- scenario.moveToState(Lifecycle.State.RESUMED)
-
listener1.mLatch.await(3, TimeUnit.SECONDS)
listener2.mLatch.await(3, TimeUnit.SECONDS)
@@ -852,39 +837,37 @@
var scCompat: SurfaceControlWrapper? = null
val listener = TransactionOnCompleteListener()
val scenario =
- ActivityScenario.launch(SurfaceControlWrapperTestActivity::class.java)
- .moveToState(Lifecycle.State.CREATED)
- .onActivity {
- val callback =
- object : SurfaceHolderCallback() {
- override fun surfaceCreated(sh: SurfaceHolder) {
- scCompat =
- SurfaceControlWrapper.Builder()
- .setParent(it.getSurfaceView().holder.surface)
- .setDebugName("SurfaceControlCompatTest")
- .build()
+ ActivityScenario.launch(SurfaceControlWrapperTestActivity::class.java).onActivity {
+ val callback =
+ object : SurfaceHolderCallback() {
+ override fun surfaceCreated(sh: SurfaceHolder) {
+ scCompat =
+ SurfaceControlWrapper.Builder()
+ .setParent(it.getSurfaceView().holder.surface)
+ .setDebugName("SurfaceControlCompatTest")
+ .build()
- // Buffer colorspace is RGBA, so Color.BLUE will be visually Red
- val buffer =
- SurfaceControlUtils.getSolidBuffer(
- SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
- SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
- Color.BLUE
- )
+ // Buffer colorspace is RGBA, so Color.BLUE will be visually Red
+ val buffer =
+ SurfaceControlUtils.getSolidBuffer(
+ SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
+ SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
+ Color.BLUE
+ )
- SurfaceControlWrapper.Transaction()
- .addTransactionCompletedListener(listener)
- .setBuffer(scCompat!!, buffer)
- .setVisibility(scCompat!!, true)
- .setCrop(scCompat!!, Rect(20, 30, 90, 60))
- .commit()
- }
+ SurfaceControlWrapper.Transaction()
+ .addTransactionCompletedListener(listener)
+ .setBuffer(scCompat!!, buffer)
+ .setVisibility(scCompat!!, true)
+ .setCrop(scCompat!!, Rect(20, 30, 90, 60))
+ .commit()
}
+ }
- it.addSurface(it.mSurfaceView, callback)
- }
+ it.addSurface(it.mSurfaceView, callback)
+ }
- scenario.moveToState(Lifecycle.State.RESUMED).onActivity {
+ scenario.onActivity {
assert(listener.mLatch.await(3000, TimeUnit.MILLISECONDS))
SurfaceControlUtils.validateOutput { bitmap ->
val coord = intArrayOf(0, 0)
diff --git a/graphics/graphics-shapes/src/androidInstrumentedTest/kotlin/androidx/graphics/shapes/PolygonTest.kt b/graphics/graphics-shapes/src/androidInstrumentedTest/kotlin/androidx/graphics/shapes/PolygonTest.kt
index 368f7b1..c7f7c75 100644
--- a/graphics/graphics-shapes/src/androidInstrumentedTest/kotlin/androidx/graphics/shapes/PolygonTest.kt
+++ b/graphics/graphics-shapes/src/androidInstrumentedTest/kotlin/androidx/graphics/shapes/PolygonTest.kt
@@ -229,6 +229,29 @@
}
}
+ @Test
+ fun emptyPolygonTest() {
+ val poly = RoundedPolygon(6, radius = 0f, rounding = CornerRounding(0.1f))
+ assert(poly.cubics.size == 1)
+
+ val stillEmpty = poly.transformed(scaleTransform(10f, 20f))
+ assert(stillEmpty.cubics.size == 1)
+ assert(stillEmpty.cubics.first().zeroLength())
+ }
+
+ @Test
+ fun emptySideTest() {
+ val poly1 =
+ RoundedPolygon(
+ floatArrayOf(0f, 0f, 1f, 0f, 1f, 0f, 0f, 1f), // Triangle with one point repeated
+ )
+ val poly2 =
+ RoundedPolygon(
+ floatArrayOf(0f, 0f, 1f, 0f, 0f, 1f), // Triangle
+ )
+ assertCubicListsEqualish(poly1.cubics, poly2.cubics)
+ }
+
private fun nonzeroCubics(original: List<Cubic>): List<Cubic> {
val result = mutableListOf<Cubic>()
for (i in original.indices) {
diff --git a/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/RoundedPolygon.kt b/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/RoundedPolygon.kt
index 051588a..5ada39e 100644
--- a/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/RoundedPolygon.kt
+++ b/graphics/graphics-shapes/src/commonMain/kotlin/androidx/graphics/shapes/RoundedPolygon.kt
@@ -74,7 +74,7 @@
}
}
}
- if (lastCubic != null && firstCubic != null)
+ if (lastCubic != null && firstCubic != null) {
add(
Cubic(
lastCubic.anchor0X,
@@ -87,6 +87,21 @@
firstCubic.anchor0Y
)
)
+ } else {
+ // Empty / 0-sized polygon.
+ add(
+ Cubic(
+ centerX,
+ centerY,
+ centerX,
+ centerY,
+ centerX,
+ centerY,
+ centerX,
+ centerY,
+ )
+ )
+ }
}
init {
@@ -100,12 +115,11 @@
abs(cubic.anchor0Y - prevCubic.anchor1Y) > DistanceEpsilon
) {
debugLog("RoundedPolygon") {
- "Ix: $index | (${cubic.anchor0X},${cubic.anchor0Y}) vs " + "$prevCubic"
+ "Ix: $index | (${cubic.anchor0X},${cubic.anchor0Y}) vs $prevCubic"
}
throw IllegalArgumentException(
- "RoundedPolygon must be contiguous, with the " +
- "anchor points of all curves matching the anchor points of the preceding " +
- "and succeeding cubics"
+ "RoundedPolygon must be contiguous, with the anchor points of all curves " +
+ "matching the anchor points of the preceding and succeeding cubics"
)
}
prevCubic = cubic
@@ -483,27 +497,51 @@
val p2: Point,
val rounding: CornerRounding? = null
) {
- val d1 = (p0 - p1).getDirection()
- val d2 = (p2 - p1).getDirection()
- val cornerRadius = rounding?.radius ?: 0f
- val smoothing = rounding?.smoothing ?: 0f
+ val d1: Point
+ val d2: Point
+ val cornerRadius: Float
+ val smoothing: Float
+ val cosAngle: Float
+ val sinAngle: Float
+ val expectedRoundCut: Float
- // cosine of angle at p1 is dot product of unit vectors to the other two vertices
- val cosAngle = d1.dotProduct(d2)
+ init {
+ val v01 = p0 - p1
+ val v21 = p2 - p1
+ val d01 = v01.getDistance()
+ val d21 = v21.getDistance()
+ if (d01 > 0f && d21 > 0f) {
+ d1 = v01 / d01
+ d2 = v21 / d21
+ cornerRadius = rounding?.radius ?: 0f
+ smoothing = rounding?.smoothing ?: 0f
- // identity: sin^2 + cos^2 = 1
- // sinAngle gives us the intersection
- val sinAngle = sqrt(1 - square(cosAngle))
+ // cosine of angle at p1 is dot product of unit vectors to the other two vertices
+ cosAngle = d1.dotProduct(d2)
- // How much we need to cut, as measured on a side, to get the required radius
- // calculating where the rounding circle hits the edge
- // This uses the identity of tan(A/2) = sinA/(1 + cosA), where tan(A/2) = radius/cut
- val expectedRoundCut =
- if (sinAngle > 1e-3) {
- cornerRadius * (cosAngle + 1) / sinAngle
+ // identity: sin^2 + cos^2 = 1
+ // sinAngle gives us the intersection
+ sinAngle = sqrt(1 - square(cosAngle))
+ // How much we need to cut, as measured on a side, to get the required radius
+ // calculating where the rounding circle hits the edge
+ // This uses the identity of tan(A/2) = sinA/(1 + cosA), where tan(A/2) = radius/cut
+ expectedRoundCut =
+ if (sinAngle > 1e-3) {
+ cornerRadius * (cosAngle + 1) / sinAngle
+ } else {
+ 0f
+ }
} else {
- 0f
+ // One (or both) of the sides is empty, not much we can do.
+ d1 = Point(0f, 0f)
+ d2 = Point(0f, 0f)
+ cornerRadius = 0f
+ smoothing = 0f
+ cosAngle = 0f
+ sinAngle = 0f
+ expectedRoundCut = 0f
}
+ }
// smoothing changes the actual cut. 0 is same as expectedRoundCut, 1 doubles it
val expectedCut: Float
diff --git a/hilt/hilt-compiler/build.gradle b/hilt/hilt-compiler/build.gradle
index 3213852..339bafd 100644
--- a/hilt/hilt-compiler/build.gradle
+++ b/hilt/hilt-compiler/build.gradle
@@ -21,6 +21,8 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
+
+import androidx.build.KotlinTarget
import androidx.build.LibraryType
import androidx.build.SdkHelperKt
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
@@ -68,4 +70,5 @@
mavenVersion = LibraryVersions.HILT
inceptionYear = "2020"
description = "AndroidX Hilt Extension Compiler"
+ kotlinTarget = KotlinTarget.KOTLIN_1_9
}
diff --git a/hilt/hilt-navigation-compose/build.gradle b/hilt/hilt-navigation-compose/build.gradle
index 0d2dbc8..7a73f47 100644
--- a/hilt/hilt-navigation-compose/build.gradle
+++ b/hilt/hilt-navigation-compose/build.gradle
@@ -21,6 +21,8 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
+
+import androidx.build.KotlinTarget
import androidx.build.LibraryType
plugins {
@@ -78,4 +80,5 @@
description = "Navigation Compose Hilt Integration"
legacyDisableKotlinStrictApiMode = true
samples(projectOrArtifact(":hilt:hilt-navigation-compose-samples"))
+ kotlinTarget = KotlinTarget.KOTLIN_1_9
}
diff --git a/hilt/hilt-navigation-compose/samples/build.gradle b/hilt/hilt-navigation-compose/samples/build.gradle
index cb0491f..c59ff49 100644
--- a/hilt/hilt-navigation-compose/samples/build.gradle
+++ b/hilt/hilt-navigation-compose/samples/build.gradle
@@ -21,6 +21,8 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
+
+import androidx.build.KotlinTarget
import androidx.build.LibraryType
import androidx.build.Publish
@@ -44,6 +46,7 @@
mavenVersion = LibraryVersions.HILT_NAVIGATION_COMPOSE
inceptionYear = "2021"
description = "Samples for the Navigation Compose Hilt Integration"
+ kotlinTarget = KotlinTarget.KOTLIN_1_9
}
android {
diff --git a/hilt/hilt-navigation-fragment/build.gradle b/hilt/hilt-navigation-fragment/build.gradle
index 5dc55cc1..c78ad99 100644
--- a/hilt/hilt-navigation-fragment/build.gradle
+++ b/hilt/hilt-navigation-fragment/build.gradle
@@ -21,6 +21,8 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
+
+import androidx.build.KotlinTarget
import androidx.build.LibraryType
plugins {
@@ -69,4 +71,5 @@
inceptionYear = "2021"
description = "Android Navigation Fragment Hilt Extension"
legacyDisableKotlinStrictApiMode = true
+ kotlinTarget = KotlinTarget.KOTLIN_1_9
}
diff --git a/hilt/hilt-navigation/build.gradle b/hilt/hilt-navigation/build.gradle
index 2e4f65b..6ce18e5 100644
--- a/hilt/hilt-navigation/build.gradle
+++ b/hilt/hilt-navigation/build.gradle
@@ -21,6 +21,8 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
+
+import androidx.build.KotlinTarget
import androidx.build.LibraryType
plugins {
@@ -45,6 +47,7 @@
inceptionYear = "2021"
description = "Android Navigation Hilt Extension"
legacyDisableKotlinStrictApiMode = true
+ kotlinTarget = KotlinTarget.KOTLIN_1_9
}
android {
diff --git a/libraryversions.toml b/libraryversions.toml
index b33669b..3980454 100644
--- a/libraryversions.toml
+++ b/libraryversions.toml
@@ -21,7 +21,7 @@
COLLECTION = "1.5.0-alpha01"
COMPOSE = "1.8.0-alpha01"
COMPOSE_MATERIAL3 = "1.4.0-alpha01"
-COMPOSE_MATERIAL3_ADAPTIVE = "1.1.0-alpha01"
+COMPOSE_MATERIAL3_ADAPTIVE = "1.1.0-alpha02"
COMPOSE_MATERIAL3_COMMON = "1.0.0-alpha01"
COMPOSE_RUNTIME_TRACING = "1.0.0-beta01"
CONSTRAINTLAYOUT = "2.2.0-beta01"
@@ -43,7 +43,7 @@
CORE_SPLASHSCREEN = "1.2.0-alpha02"
CORE_TELECOM = "1.0.0-alpha4"
CORE_UWB = "1.0.0-alpha08"
-CREDENTIALS = "1.5.0-alpha04"
+CREDENTIALS = "1.5.0-alpha05"
CREDENTIALS_E2EE_QUARANTINE = "1.0.0-alpha02"
CREDENTIALS_FIDO_QUARANTINE = "1.0.0-alpha02"
CURSORADAPTER = "1.1.0-alpha01"
@@ -111,7 +111,7 @@
PRIVACYSANDBOX_UI = "1.0.0-alpha09"
PROFILEINSTALLER = "1.4.0-rc01"
RECOMMENDATION = "1.1.0-alpha01"
-RECYCLERVIEW = "1.4.0-beta01"
+RECYCLERVIEW = "1.4.0-rc01"
RECYCLERVIEW_SELECTION = "1.2.0-alpha02"
REMOTECALLBACK = "1.0.0-alpha02"
RESOURCEINSPECTION = "1.1.0-alpha01"
diff --git a/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt b/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt
index 317bd03..4799f00 100644
--- a/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt
@@ -83,9 +83,10 @@
RestrictToDetector.RESTRICTED,
ObsoleteCompatDetector.ISSUE,
ReplaceWithDetector.ISSUE,
- // This issue is only enabled when `-Pandroidx.migrateArrayAnnotations=true`.
- ArrayNullnessMigration.ISSUE,
+ // This issue is only enabled when `-Pandroidx.useJSpecifyAnnotations=true`.
+ JSpecifyNullnessMigration.ISSUE,
TypeMirrorToString.ISSUE,
+ BanNullMarked.ISSUE,
)
}
}
diff --git a/lint-checks/src/main/java/androidx/build/lint/ArrayNullnessMigration.kt b/lint-checks/src/main/java/androidx/build/lint/ArrayNullnessMigration.kt
deleted file mode 100644
index 7e741be..0000000
--- a/lint-checks/src/main/java/androidx/build/lint/ArrayNullnessMigration.kt
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.build.lint
-
-import com.android.tools.lint.client.api.UElementHandler
-import com.android.tools.lint.detector.api.Category
-import com.android.tools.lint.detector.api.Detector
-import com.android.tools.lint.detector.api.Implementation
-import com.android.tools.lint.detector.api.Incident
-import com.android.tools.lint.detector.api.Issue
-import com.android.tools.lint.detector.api.JavaContext
-import com.android.tools.lint.detector.api.Location
-import com.android.tools.lint.detector.api.Scope
-import com.android.tools.lint.detector.api.Severity
-import com.android.tools.lint.detector.api.isKotlin
-import com.intellij.psi.PsiArrayType
-import com.intellij.psi.PsiEllipsisType
-import java.util.EnumSet
-import org.jetbrains.uast.UAnnotation
-import org.jetbrains.uast.UElement
-import org.jetbrains.uast.UField
-import org.jetbrains.uast.UMethod
-import org.jetbrains.uast.UParameter
-
-/**
- * Repositions nullness annotations on arrays to facilitate migrating the nullness annotations to
- * TYPE_USE. See the issue description in the companion object for more detail.
- */
-class ArrayNullnessMigration : Detector(), Detector.UastScanner {
- override fun getApplicableUastTypes() = listOf(UAnnotation::class.java)
-
- override fun createUastHandler(context: JavaContext): UElementHandler {
- return AnnotationChecker(context)
- }
-
- private inner class AnnotationChecker(val context: JavaContext) : UElementHandler() {
- override fun visitAnnotation(node: UAnnotation) {
- // Nullness annotations are only relevant for Java source.
- if (isKotlin(node.lang)) return
-
- // Verify this is a nullness annotation.
- val annotationName = node.qualifiedName ?: return
- if (annotationName !in nullnessAnnotations) return
-
- // Find the type of the annotated element, and only continue if it is an array.
- val annotated = node.uastParent ?: return
- val type =
- when (annotated) {
- is UParameter -> annotated.type
- is UMethod -> annotated.returnType
- is UField -> annotated.type
- else -> return
- }
- if (type !is PsiArrayType) return
-
- // Determine the file location for the autofix. This is a bit complicated because it
- // needs to avoid editing the wrong thing, like the doc comment preceding a method, but
- // also is doing some reformatting in the area around the annotation.
- // This is where the annotation itself is located.
- val annotationLocation = context.getLocation(node)
- // This is where the element being annotated is located.
- val annotatedLocation = context.getLocation(annotated as UElement)
- // If the annotation and annotated element aren't on the same line, that probably means
- // the annotation is on its own line, with indentation before it. To also get rid of
- // that indentation, start the range at the start of the annotation's line.
- // If the annotation and annotated element are on the same line, just start at the
- // annotation starting place to avoid including e.g. other parameters.
- val startLocation =
- if (annotatedLocation.start!!.sameLine(annotationLocation.start!!)) {
- annotationLocation.start!!
- } else {
- Location.create(
- context.file,
- context.getContents()!!.toString(),
- annotationLocation.start!!.line
- )
- .start!!
- }
- val fixLocation =
- Location.create(annotatedLocation.file, startLocation, annotatedLocation.end)
-
- // Part 1 of the fix: remove the original annotation
- val annotationString = node.asSourceString()
- val removeOriginalAnnotation =
- fix()
- .replace()
- .range(fixLocation)
- // In addition to the annotation, also remove any extra whitespace and trailing
- // new line. The reformat option unfortunately doesn't do this.
- .pattern("(( )*$annotationString ?\n?)")
- .with("")
- // Only remove one instance of the annotation.
- .repeatedly(false)
- .autoFix()
- .build()
-
- // Vararg types are also arrays, determine which array marker is present here.
- val arraySuffix =
- if (type is PsiEllipsisType) {
- "..."
- } else {
- "[]"
- }
- // Part 2 of the fix: add a new annotation.
- val addNewAnnotation =
- fix()
- .replace()
- .range(fixLocation)
- .text(arraySuffix)
- .with(" $annotationString $arraySuffix")
- // Only add one instance of the annotation. This will replace the first instance
- // of []/..., which is correct. In `String @Nullable [][]` the annotation
- // applies to the outer `String[][]` type, while in `String[] @Nullable []` it
- // applies to the inner `String[]` arrays.
- .repeatedly(false)
- .autoFix()
- .build()
-
- // Combine the two elements of the fix and report.
- val fix =
- fix()
- .name("Move annotation")
- .composite()
- .add(removeOriginalAnnotation)
- .add(addNewAnnotation)
- .autoFix()
- .build()
-
- val incident =
- Incident(context)
- .message("Nullness annotation on array will apply to element")
- .issue(ISSUE)
- .location(context.getLocation(annotated as UElement))
- .scope(annotated)
- .fix(fix)
- context.report(incident)
- }
- }
-
- companion object {
- val nullnessAnnotations =
- listOf(
- "androidx.annotation.NonNull",
- "androidx.annotation.Nullable",
- )
- val ISSUE =
- Issue.create(
- "ArrayMigration",
- "Migrate arrays to type-use nullness annotations",
- """
- When nullness annotations do not target TYPE_USE, the following definition means
- that the type of `arg` is nullable:
- @Nullable String[] arg
- However, if the annotation targets TYPE_USE, it now applies to the component
- type of the array, meaning that `arg`'s type is an array of nullable strings.
- To retain the original meaning, the definition needs to be changed to this:
- String @Nullable [] arg
- This check performs that migration to enable converting nullness annotations to
- target TYPE_USE.
- """,
- Category.CORRECTNESS,
- 5,
- Severity.ERROR,
- Implementation(
- ArrayNullnessMigration::class.java,
- EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)
- )
- )
- }
-}
diff --git a/lint-checks/src/main/java/androidx/build/lint/BanNullMarked.kt b/lint-checks/src/main/java/androidx/build/lint/BanNullMarked.kt
new file mode 100644
index 0000000..9b735cc
--- /dev/null
+++ b/lint-checks/src/main/java/androidx/build/lint/BanNullMarked.kt
@@ -0,0 +1,64 @@
+/*
+ * 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.build.lint
+
+import com.android.tools.lint.client.api.UElementHandler
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Incident
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import org.jetbrains.uast.UAnnotation
+
+class BanNullMarked : Detector(), Detector.UastScanner {
+
+ override fun getApplicableUastTypes() = listOf(UAnnotation::class.java)
+
+ override fun createUastHandler(context: JavaContext): UElementHandler {
+ return AnnotationChecker(context)
+ }
+
+ private inner class AnnotationChecker(val context: JavaContext) : UElementHandler() {
+ override fun visitAnnotation(node: UAnnotation) {
+ if (node.qualifiedName != "org.jspecify.annotations.NullMarked") return
+
+ val incident =
+ Incident(context)
+ .issue(ISSUE)
+ .location(context.getLocation(node))
+ .scope(node)
+ .message("Should not use @NullMarked annotation")
+ context.report(incident)
+ }
+ }
+
+ companion object {
+ val ISSUE =
+ Issue.create(
+ "BanNullMarked",
+ "Should not use @NullMarked annotation",
+ "Usage of the @NullMarked annotation is not allowed because lint and metalava do not support it",
+ Category.CORRECTNESS,
+ 5,
+ Severity.ERROR,
+ Implementation(BanNullMarked::class.java, Scope.JAVA_FILE_SCOPE)
+ )
+ }
+}
diff --git a/lint-checks/src/main/java/androidx/build/lint/JSpecifyNullnessMigration.kt b/lint-checks/src/main/java/androidx/build/lint/JSpecifyNullnessMigration.kt
new file mode 100644
index 0000000..a57fe59
--- /dev/null
+++ b/lint-checks/src/main/java/androidx/build/lint/JSpecifyNullnessMigration.kt
@@ -0,0 +1,221 @@
+/*
+ * 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.build.lint
+
+import com.android.tools.lint.client.api.UElementHandler
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Incident
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.LintFix
+import com.android.tools.lint.detector.api.Location
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.isKotlin
+import com.intellij.psi.PsiArrayType
+import com.intellij.psi.PsiClassType
+import com.intellij.psi.PsiEllipsisType
+import com.intellij.psi.PsiPrimitiveType
+import java.util.EnumSet
+import org.jetbrains.uast.UAnnotation
+import org.jetbrains.uast.UElement
+import org.jetbrains.uast.UField
+import org.jetbrains.uast.ULocalVariable
+import org.jetbrains.uast.UMethod
+import org.jetbrains.uast.UParameter
+
+/**
+ * Repositions nullness annotations to facilitate migrating the nullness annotations to JSpecify
+ * TYPE_USE annotations. See the issue description in the companion object for more detail.
+ */
+class JSpecifyNullnessMigration : Detector(), Detector.UastScanner {
+ override fun getApplicableUastTypes() = listOf(UAnnotation::class.java)
+
+ override fun createUastHandler(context: JavaContext): UElementHandler {
+ return AnnotationChecker(context)
+ }
+
+ private inner class AnnotationChecker(val context: JavaContext) : UElementHandler() {
+ override fun visitAnnotation(node: UAnnotation) {
+ // Nullness annotations are only relevant for Java source.
+ if (isKotlin(node.lang)) return
+
+ // Verify this is a nullness annotation.
+ val annotationName = node.qualifiedName ?: return
+ if (annotationName !in nullnessAnnotations.keys) return
+ val replacementAnnotationName = nullnessAnnotations[annotationName]!!
+
+ val fix = createFix(node, replacementAnnotationName)
+ val incident =
+ Incident(context)
+ .message("Switch nullness annotation to JSpecify")
+ .issue(ISSUE)
+ .location(context.getLocation(node as UElement))
+ .scope(node)
+ .fix(fix)
+ context.report(incident)
+ }
+
+ fun createFix(node: UAnnotation, replacementAnnotationName: String): LintFix? {
+ // Find the type of the annotated element.
+ val annotated = node.uastParent ?: return null
+ val type =
+ when (annotated) {
+ is UParameter -> annotated.type
+ is UMethod -> annotated.returnType
+ is UField -> annotated.type
+ is ULocalVariable -> annotated.type
+ else -> return null
+ } ?: return null
+
+ // Determine the file location for the autofix. This is a bit complicated because it
+ // needs to avoid editing the wrong thing, like the doc comment preceding a method, but
+ // also is doing some reformatting in the area around the annotation.
+ // This is where the annotation itself is located.
+ val annotationLocation = context.getLocation(node)
+ // This is where the element being annotated is located.
+ val annotatedLocation = context.getLocation(annotated as UElement)
+ // If the annotation and annotated element aren't on the same line, that probably means
+ // the annotation is on its own line, with indentation before it. To also get rid of
+ // that indentation, start the range at the start of the annotation's line.
+ // If the annotation and annotated element are on the same line, just start at the
+ // annotation starting place to avoid including e.g. other parameters.
+ val annotatedStart = annotatedLocation.start ?: return null
+ val annotationStart = annotationLocation.start ?: return null
+ val startLocation =
+ if (annotatedStart.sameLine(annotationStart)) {
+ annotationStart
+ } else {
+ Location.create(
+ context.file,
+ context.getContents()!!.toString(),
+ annotationStart.line
+ )
+ .start!!
+ }
+ val fixLocation =
+ Location.create(annotatedLocation.file, startLocation, annotatedLocation.end)
+
+ // Part 1 of the fix: remove the original annotation
+ val annotationString = node.asSourceString()
+ val removeOriginalAnnotation =
+ fix()
+ .replace()
+ .range(fixLocation)
+ // In addition to the annotation, also remove any extra whitespace and trailing
+ // new line. The reformat option unfortunately doesn't do this.
+ .pattern("(( )*$annotationString ?\n?)")
+ .with("")
+ // Only remove one instance of the annotation.
+ .repeatedly(false)
+ .autoFix()
+ .build()
+
+ // The jspecify annotations can't be applied to primitive types (since primitives are
+ // non-null by definition) or local variables, so just remove the annotation in those
+ // cases. For all other cases, also add a new annotation to the correct position.
+ return if (type is PsiPrimitiveType || annotated is ULocalVariable) {
+ removeOriginalAnnotation
+ } else {
+ // Create a regex pattern for where to insert the annotation. The replacement lint
+ // removes the first capture group (section in parentheses) of the supplied regex.
+ // Since this fix is really just to insert an annotation, use an empty capture group
+ // so nothing is removed.
+ val (prefix, textToReplace) =
+ when {
+ // For a vararg type where the component type is an array, the annotation
+ // goes before the array instead of the vararg ("String @NonNull []..."),
+ // so only match the "..." when the component isn't an array.
+ type is PsiEllipsisType && type.componentType !is PsiArrayType ->
+ Pair(" ", "()\\.\\.\\.")
+ type is PsiArrayType -> Pair(" ", "()\\[\\]")
+ // Make sure to match the right usage of the class name: find the name
+ // preceded by a space or dot, and followed by a space, open angle bracket,
+ // or newline character.
+ type is PsiClassType -> Pair("", "[ .]()${type.className}[ <\\n\\r]")
+ else -> Pair("", "()${type.presentableText}")
+ }
+ val replacement = "$prefix@$replacementAnnotationName "
+
+ // Part 2 of the fix: add a new annotation.
+ val addNewAnnotation =
+ fix()
+ .replace()
+ .range(fixLocation)
+ .pattern(textToReplace)
+ .with(replacement)
+ // Only add one instance of the annotation. For nested array types, this
+ // will replace the first instance of []/..., which is correct. In
+ // `String @Nullable [][]` the annotation applies to the outer `String[][]`
+ // type, while in `String[] @Nullable []` it applies to the inner `String[]`
+ // arrays.
+ .repeatedly(false)
+ .shortenNames()
+ .autoFix()
+ .build()
+
+ // Combine the two elements of the fix.
+ return fix()
+ .name("Move annotation")
+ .composite()
+ .add(removeOriginalAnnotation)
+ .add(addNewAnnotation)
+ .autoFix()
+ .build()
+ }
+ }
+ }
+
+ companion object {
+ val nullnessAnnotations =
+ mapOf(
+ "androidx.annotation.NonNull" to "NonNull",
+ "androidx.annotation.Nullable" to "Nullable",
+ )
+ val ISSUE =
+ Issue.create(
+ "JSpecifyNullness",
+ "Migrate nullness annotations to type-use position",
+ """
+ Switches from AndroidX nullness annotations to JSpecify, which are type-use.
+ Type-use annotations have different syntactic positions than non-type-use
+ annotations in some cases.
+
+ For instance, when nullness annotations do not target TYPE_USE, the following
+ definition means that the type of `arg` is nullable:
+ @Nullable String[] arg
+ However, if the annotation targets TYPE_USE, it now applies to the component
+ type of the array, meaning that `arg`'s type is an array of nullable strings.
+ To retain the original meaning, the definition needs to be changed to this:
+ String @Nullable [] arg
+
+ Type-use nullness annotations must go before the simple class name of a
+ qualified type. For instance, `java.lang.@Nullable String` is required instead
+ of `@Nullable java.lang.String`.
+ """,
+ Category.CORRECTNESS,
+ 5,
+ Severity.ERROR,
+ Implementation(
+ JSpecifyNullnessMigration::class.java,
+ EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)
+ )
+ )
+ }
+}
diff --git a/lint-checks/src/test/java/androidx/build/lint/ArrayNullnessMigrationTest.kt b/lint-checks/src/test/java/androidx/build/lint/ArrayNullnessMigrationTest.kt
deleted file mode 100644
index 556cf75..0000000
--- a/lint-checks/src/test/java/androidx/build/lint/ArrayNullnessMigrationTest.kt
+++ /dev/null
@@ -1,359 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.build.lint
-
-import com.android.tools.lint.checks.infrastructure.TestFile
-import com.android.tools.lint.checks.infrastructure.TestMode
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-
-@RunWith(JUnit4::class)
-class ArrayNullnessMigrationTest :
- AbstractLintDetectorTest(
- useDetector = ArrayNullnessMigration(),
- useIssues = listOf(ArrayNullnessMigration.ISSUE),
- stubs = annotationStubs
- ) {
- @Test
- fun `Nullness annotation on parameter`() {
- val input =
- java(
- """
- package test.pkg;
- import androidx.annotation.NonNull;
- public class Foo {
- public void foo(@NonNull String[] arr) {}
- }
- """
- .trimIndent()
- )
-
- val expected =
- """
- src/test/pkg/Foo.java:4: Error: Nullness annotation on array will apply to element [ArrayMigration]
- public void foo(@NonNull String[] arr) {}
- ~~~~~~~~~~~~~~~~~~~~~
- 1 errors, 0 warnings
- """
- .trimIndent()
-
- val expectedFixDiffs =
- """
- Autofix for src/test/pkg/Foo.java line 4: Move annotation:
- @@ -4 +4
- - public void foo(@NonNull String[] arr) {}
- + public void foo(String @NonNull [] arr) {}
- """
- .trimIndent()
-
- runArrayNullnessTest(input, expected, expectedFixDiffs)
- }
-
- @Test
- fun `Nullness annotation on method return`() {
- val input =
- java(
- """
- package test.pkg;
- import androidx.annotation.Nullable;
- public class Foo {
- @Nullable
- public String[] foo() { return null; }
- }
- """
- .trimIndent(),
- )
-
- val expected =
- """
- src/test/pkg/Foo.java:5: Error: Nullness annotation on array will apply to element [ArrayMigration]
- public String[] foo() { return null; }
- ~~~
- 1 errors, 0 warnings
- """
- .trimIndent()
-
- val expectedFixDiffs =
- """
- Autofix for src/test/pkg/Foo.java line 5: Move annotation:
- @@ -4 +4
- - @Nullable
- - public String[] foo() { return null; }
- + public String @Nullable [] foo() { return null; }
- """
- .trimIndent()
-
- runArrayNullnessTest(input, expected, expectedFixDiffs)
- }
-
- @Test
- fun `Nullness annotation on method return and parameter`() {
- val input =
- java(
- """
- package test.pkg;
- import androidx.annotation.Nullable;
- public class Foo {
- @Nullable
- public String[] foo(@Nullable String[] arr) { return null; }
- }
- """
- .trimIndent()
- )
-
- val expected =
- """
- src/test/pkg/Foo.java:5: Error: Nullness annotation on array will apply to element [ArrayMigration]
- public String[] foo(@Nullable String[] arr) { return null; }
- ~~~
- src/test/pkg/Foo.java:5: Error: Nullness annotation on array will apply to element [ArrayMigration]
- public String[] foo(@Nullable String[] arr) { return null; }
- ~~~~~~~~~~~~~~~~~~~~~~
- 2 errors, 0 warnings
- """
- .trimIndent()
-
- val expectedFixDiffs =
- """
- Autofix for src/test/pkg/Foo.java line 5: Move annotation:
- @@ -4 +4
- - @Nullable
- - public String[] foo(@Nullable String[] arr) { return null; }
- + public String @Nullable [] foo(@Nullable String[] arr) { return null; }
- Autofix for src/test/pkg/Foo.java line 5: Move annotation:
- @@ -5 +5
- - public String[] foo(@Nullable String[] arr) { return null; }
- + public String[] foo(String @Nullable [] arr) { return null; }
- """
- .trimIndent()
-
- runArrayNullnessTest(input, expected, expectedFixDiffs)
- }
-
- @Test
- fun `Nullness annotation on field`() {
- val input =
- java(
- """
- package test.pkg;
- import androidx.annotation.Nullable;
- public class Foo {
- @Nullable public String[] foo;
- }
- """
- .trimIndent()
- )
-
- val expected =
- """
- src/test/pkg/Foo.java:4: Error: Nullness annotation on array will apply to element [ArrayMigration]
- @Nullable public String[] foo;
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- 1 errors, 0 warnings
- """
- .trimIndent()
-
- val expectedFixDiffs =
- """
- Autofix for src/test/pkg/Foo.java line 4: Move annotation:
- @@ -4 +4
- - @Nullable public String[] foo;
- + public String @Nullable [] foo;
- """
- .trimIndent()
-
- runArrayNullnessTest(input, expected, expectedFixDiffs)
- }
-
- @Test
- fun `Nullness annotation on 2d array`() {
- val input =
- java(
- """
- package test.pkg;
- import androidx.annotation.Nullable;
- public class Foo {
- @Nullable public String[][] foo;
- }
- """
- .trimIndent()
- )
-
- val expected =
- """
- src/test/pkg/Foo.java:4: Error: Nullness annotation on array will apply to element [ArrayMigration]
- @Nullable public String[][] foo;
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- 1 errors, 0 warnings
- """
- .trimIndent()
-
- val expectedFixDiffs =
- """
- Autofix for src/test/pkg/Foo.java line 4: Move annotation:
- @@ -4 +4
- - @Nullable public String[][] foo;
- + public String @Nullable [][] foo;
- """
- .trimIndent()
-
- runArrayNullnessTest(input, expected, expectedFixDiffs)
- }
-
- @Test
- fun `Nullness annotation on varargs`() {
- val input =
- java(
- """
- package test.pkg;
- import androidx.annotation.NonNull;
- public class Foo {
- public void foo(@NonNull String... arr) {}
- }
- """
- .trimIndent(),
- )
-
- val expected =
- """
- src/test/pkg/Foo.java:4: Error: Nullness annotation on array will apply to element [ArrayMigration]
- public void foo(@NonNull String... arr) {}
- ~~~~~~~~~~~~~~~~~~~~~~
- 1 errors, 0 warnings
- """
- .trimIndent()
-
- val expectedFixDiffs =
- """
- Autofix for src/test/pkg/Foo.java line 4: Move annotation:
- @@ -4 +4
- - public void foo(@NonNull String... arr) {}
- + public void foo(String @NonNull ... arr) {}
- """
- .trimIndent()
-
- runArrayNullnessTest(input, expected, expectedFixDiffs)
- }
-
- @Test
- fun `Nullness annotation on method return with array in comments`() {
- val input =
- java(
- """
- package test.pkg;
- import androidx.annotation.Nullable;
- public class Foo {
- /**
- * @return A String[]
- */
- @Nullable
- public String[] foo() { return null; }
- }
- """
- .trimIndent(),
- )
-
- val expected =
- """
- src/test/pkg/Foo.java:8: Error: Nullness annotation on array will apply to element [ArrayMigration]
- public String[] foo() { return null; }
- ~~~
- 1 errors, 0 warnings
- """
- .trimIndent()
-
- val expectedFixDiffs =
- """
- Autofix for src/test/pkg/Foo.java line 8: Move annotation:
- @@ -7 +7
- - @Nullable
- - public String[] foo() { return null; }
- + public String @Nullable [] foo() { return null; }
- """
- .trimIndent()
-
- runArrayNullnessTest(input, expected, expectedFixDiffs)
- }
-
- @Test
- fun `Nullness annotation on method return with annotation in comments`() {
- val input =
- java(
- """
- package test.pkg;
- import androidx.annotation.Nullable;
- public class Foo {
- /**
- * @return A @Nullable string array
- */
- @Nullable
- public String[] foo() { return null; }
- }
- """
- .trimIndent(),
- )
-
- val expected =
- """
- src/test/pkg/Foo.java:8: Error: Nullness annotation on array will apply to element [ArrayMigration]
- public String[] foo() { return null; }
- ~~~
- 1 errors, 0 warnings
- """
- .trimIndent()
-
- val expectedFixDiffs =
- """
- Autofix for src/test/pkg/Foo.java line 8: Move annotation:
- @@ -7 +7
- - @Nullable
- - public String[] foo() { return null; }
- + public String @Nullable [] foo() { return null; }
- """
- .trimIndent()
-
- runArrayNullnessTest(input, expected, expectedFixDiffs)
- }
-
- private fun runArrayNullnessTest(input: TestFile, expected: String, expectedFixDiffs: String) {
- lint()
- .files(*stubs, input)
- .skipTestModes(TestMode.WHITESPACE)
- .run()
- .expect(expected)
- .expectFixDiffs(expectedFixDiffs)
- }
-
- companion object {
- val annotationStubs =
- arrayOf(
- kotlin(
- """
- package androidx.annotation
- annotation class NonNull
- """
- ),
- kotlin(
- """
- package androidx.annotation
- annotation class Nullable
- """
- )
- )
- }
-}
diff --git a/lint-checks/src/test/java/androidx/build/lint/BanNullMarkedTest.kt b/lint-checks/src/test/java/androidx/build/lint/BanNullMarkedTest.kt
new file mode 100644
index 0000000..c16ea81
--- /dev/null
+++ b/lint-checks/src/test/java/androidx/build/lint/BanNullMarkedTest.kt
@@ -0,0 +1,155 @@
+/*
+ * 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.build.lint
+
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class BanNullMarkedTest :
+ AbstractLintDetectorTest(
+ useDetector = BanNullMarked(),
+ useIssues = listOf(BanNullMarked.ISSUE),
+ stubs = arrayOf(nullMarkedStub)
+ ) {
+ @Test
+ fun `Usage of NullMarked in a package-info file`() {
+ val input =
+ java(
+ """
+ @NullMarked
+ package test.pkg;
+
+ import org.jspecify.annotations.NullMarked;
+ """
+ .trimIndent()
+ )
+
+ val expected =
+ """
+ src/test/pkg/package-info.java:1: Error: Should not use @NullMarked annotation [BanNullMarked]
+ @NullMarked
+ ~~~~~~~~~~~
+ 1 errors, 0 warnings
+ """
+ .trimIndent()
+
+ check(input).expect(expected)
+ }
+
+ @Test
+ fun `Usage of NullMarked on a class`() {
+ val input =
+ java(
+ """
+ package test.pkg;
+
+ import org.jspecify.annotations.NullMarked;
+
+ @NullMarked
+ public class Foo {}
+ """
+ .trimIndent()
+ )
+
+ val expected =
+ """
+ src/test/pkg/Foo.java:5: Error: Should not use @NullMarked annotation [BanNullMarked]
+ @NullMarked
+ ~~~~~~~~~~~
+ 1 errors, 0 warnings
+ """
+ .trimIndent()
+
+ check(input).expect(expected)
+ }
+
+ @Test
+ fun `Usage of NullMarked on a method`() {
+ val input =
+ java(
+ """
+ package test.pkg;
+
+ import org.jspecify.annotations.NullMarked;
+
+ public class Foo {
+ @NullMarked
+ public void foo() {}
+ }
+ """
+ .trimIndent()
+ )
+
+ val expected =
+ """
+ src/test/pkg/Foo.java:6: Error: Should not use @NullMarked annotation [BanNullMarked]
+ @NullMarked
+ ~~~~~~~~~~~
+ 1 errors, 0 warnings
+ """
+ .trimIndent()
+
+ check(input).expect(expected)
+ }
+
+ @Test
+ fun `Usage of NullMarked on a constructor`() {
+ val input =
+ java(
+ """
+ package test.pkg;
+
+ import org.jspecify.annotations.NullMarked;
+
+ public class Foo {
+ @NullMarked
+ public Foo() {}
+ }
+ """
+ .trimIndent()
+ )
+
+ val expected =
+ """
+ src/test/pkg/Foo.java:6: Error: Should not use @NullMarked annotation [BanNullMarked]
+ @NullMarked
+ ~~~~~~~~~~~
+ 1 errors, 0 warnings
+ """
+ .trimIndent()
+
+ check(input).expect(expected)
+ }
+
+ companion object {
+ private val nullMarkedStub =
+ java(
+ """
+ package org.jspecify.annotations;
+
+ import java.lang.annotation.ElementType;
+ import java.lang.annotation.Target;
+
+ @Target({ElementType.MODULE, ElementType.PACKAGE, ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR})
+ public @interface NullMarked {}
+ """
+ .trimIndent()
+ )
+ }
+}
diff --git a/lint-checks/src/test/java/androidx/build/lint/JSpecifyNullnessMigrationTest.kt b/lint-checks/src/test/java/androidx/build/lint/JSpecifyNullnessMigrationTest.kt
new file mode 100644
index 0000000..2b91a0a
--- /dev/null
+++ b/lint-checks/src/test/java/androidx/build/lint/JSpecifyNullnessMigrationTest.kt
@@ -0,0 +1,790 @@
+/*
+ * 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.build.lint
+
+import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.lint.checks.infrastructure.TestMode
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class JSpecifyNullnessMigrationTest :
+ AbstractLintDetectorTest(
+ useDetector = JSpecifyNullnessMigration(),
+ useIssues = listOf(JSpecifyNullnessMigration.ISSUE),
+ stubs = annotationStubs
+ ) {
+ @Test
+ fun `Nullness annotation on array parameter`() {
+ val input =
+ java(
+ """
+ package test.pkg;
+ import androidx.annotation.NonNull;
+ public class Foo {
+ public void foo(@NonNull String[] arr) {}
+ }
+ """
+ .trimIndent()
+ )
+
+ val expected =
+ """
+ src/test/pkg/Foo.java:4: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
+ public void foo(@NonNull String[] arr) {}
+ ~~~~~~~~
+ 1 errors, 0 warnings
+ """
+ .trimIndent()
+
+ val expectedFixDiffs =
+ """
+ Autofix for src/test/pkg/Foo.java line 4: Move annotation:
+ @@ -4 +4
+ - public void foo(@NonNull String[] arr) {}
+ + public void foo(String @NonNull [] arr) {}
+ """
+ .trimIndent()
+
+ runNullnessTest(input, expected, expectedFixDiffs)
+ }
+
+ @Test
+ fun `Nullness annotation on array method return`() {
+ val input =
+ java(
+ """
+ package test.pkg;
+ import androidx.annotation.Nullable;
+ public class Foo {
+ @Nullable
+ public String[] foo() { return null; }
+ }
+ """
+ .trimIndent(),
+ )
+
+ val expected =
+ """
+ src/test/pkg/Foo.java:4: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
+ @Nullable
+ ~~~~~~~~~
+ 1 errors, 0 warnings
+ """
+ .trimIndent()
+
+ val expectedFixDiffs =
+ """
+ Autofix for src/test/pkg/Foo.java line 4: Move annotation:
+ @@ -4 +4
+ - @Nullable
+ - public String[] foo() { return null; }
+ + public String @Nullable [] foo() { return null; }
+ """
+ .trimIndent()
+
+ runNullnessTest(input, expected, expectedFixDiffs)
+ }
+
+ @Test
+ fun `Nullness annotation on array method return and array parameter`() {
+ val input =
+ java(
+ """
+ package test.pkg;
+ import androidx.annotation.Nullable;
+ public class Foo {
+ @Nullable
+ public String[] foo(@Nullable String[] arr) { return null; }
+ }
+ """
+ .trimIndent()
+ )
+
+ val expected =
+ """
+ src/test/pkg/Foo.java:4: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
+ @Nullable
+ ~~~~~~~~~
+ src/test/pkg/Foo.java:5: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
+ public String[] foo(@Nullable String[] arr) { return null; }
+ ~~~~~~~~~
+ 2 errors, 0 warnings
+ """
+ .trimIndent()
+
+ val expectedFixDiffs =
+ """
+ Autofix for src/test/pkg/Foo.java line 4: Move annotation:
+ @@ -4 +4
+ - @Nullable
+ - public String[] foo(@Nullable String[] arr) { return null; }
+ + public String @Nullable [] foo(@Nullable String[] arr) { return null; }
+ Autofix for src/test/pkg/Foo.java line 5: Move annotation:
+ @@ -5 +5
+ - public String[] foo(@Nullable String[] arr) { return null; }
+ + public String[] foo(String @Nullable [] arr) { return null; }
+ """
+ .trimIndent()
+
+ runNullnessTest(input, expected, expectedFixDiffs)
+ }
+
+ @Test
+ fun `Nullness annotation on array field`() {
+ val input =
+ java(
+ """
+ package test.pkg;
+ import androidx.annotation.Nullable;
+ public class Foo {
+ @Nullable public String[] foo;
+ }
+ """
+ .trimIndent()
+ )
+
+ val expected =
+ """
+ src/test/pkg/Foo.java:4: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
+ @Nullable public String[] foo;
+ ~~~~~~~~~
+ 1 errors, 0 warnings
+ """
+ .trimIndent()
+
+ val expectedFixDiffs =
+ """
+ Autofix for src/test/pkg/Foo.java line 4: Move annotation:
+ @@ -4 +4
+ - @Nullable public String[] foo;
+ + public String @Nullable [] foo;
+ """
+ .trimIndent()
+
+ runNullnessTest(input, expected, expectedFixDiffs)
+ }
+
+ @Test
+ fun `Nullness annotation on 2d array`() {
+ val input =
+ java(
+ """
+ package test.pkg;
+ import androidx.annotation.Nullable;
+ public class Foo {
+ @Nullable public String[][] foo;
+ }
+ """
+ .trimIndent()
+ )
+
+ val expected =
+ """
+ src/test/pkg/Foo.java:4: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
+ @Nullable public String[][] foo;
+ ~~~~~~~~~
+ 1 errors, 0 warnings
+ """
+ .trimIndent()
+
+ val expectedFixDiffs =
+ """
+ Autofix for src/test/pkg/Foo.java line 4: Move annotation:
+ @@ -4 +4
+ - @Nullable public String[][] foo;
+ + public String @Nullable [][] foo;
+ """
+ .trimIndent()
+
+ runNullnessTest(input, expected, expectedFixDiffs)
+ }
+
+ @Test
+ fun `Nullness annotation on varargs`() {
+ val input =
+ java(
+ """
+ package test.pkg;
+ import androidx.annotation.NonNull;
+ public class Foo {
+ public void foo(@NonNull String... arr) {}
+ }
+ """
+ .trimIndent(),
+ )
+
+ val expected =
+ """
+ src/test/pkg/Foo.java:4: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
+ public void foo(@NonNull String... arr) {}
+ ~~~~~~~~
+ 1 errors, 0 warnings
+ """
+ .trimIndent()
+
+ val expectedFixDiffs =
+ """
+ Autofix for src/test/pkg/Foo.java line 4: Move annotation:
+ @@ -4 +4
+ - public void foo(@NonNull String... arr) {}
+ + public void foo(String @NonNull ... arr) {}
+ """
+ .trimIndent()
+
+ runNullnessTest(input, expected, expectedFixDiffs)
+ }
+
+ @Test
+ fun `Nullness annotation on array varargs`() {
+ val input =
+ java(
+ """
+ package test.pkg;
+ import androidx.annotation.NonNull;
+ public class Foo {
+ public void foo(@NonNull String[]... args) {}
+ }
+ """
+ .trimIndent()
+ )
+
+ val expected =
+ """
+ src/test/pkg/Foo.java:4: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
+ public void foo(@NonNull String[]... args) {}
+ ~~~~~~~~
+ 1 errors, 0 warnings
+ """
+ .trimIndent()
+
+ val expectedFixDiffs =
+ """
+ Autofix for src/test/pkg/Foo.java line 4: Move annotation:
+ @@ -4 +4
+ - public void foo(@NonNull String[]... args) {}
+ + public void foo(String @NonNull []... args) {}
+ """
+ .trimIndent()
+
+ runNullnessTest(input, expected, expectedFixDiffs)
+ }
+
+ @Test
+ fun `Nullness annotation on method return with array in comments`() {
+ val input =
+ java(
+ """
+ package test.pkg;
+ import androidx.annotation.Nullable;
+ public class Foo {
+ /**
+ * @return A String[]
+ */
+ @Nullable
+ public String[] foo() { return null; }
+ }
+ """
+ .trimIndent(),
+ )
+
+ val expected =
+ """
+ src/test/pkg/Foo.java:7: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
+ @Nullable
+ ~~~~~~~~~
+ 1 errors, 0 warnings
+ """
+ .trimIndent()
+
+ val expectedFixDiffs =
+ """
+ Autofix for src/test/pkg/Foo.java line 7: Move annotation:
+ @@ -7 +7
+ - @Nullable
+ - public String[] foo() { return null; }
+ + public String @Nullable [] foo() { return null; }
+ """
+ .trimIndent()
+
+ runNullnessTest(input, expected, expectedFixDiffs)
+ }
+
+ @Test
+ fun `Nullness annotation on method return with annotation in comments`() {
+ val input =
+ java(
+ """
+ package test.pkg;
+ import androidx.annotation.Nullable;
+ public class Foo {
+ /**
+ * @return A @Nullable string array
+ */
+ @Nullable
+ public String[] foo() { return null; }
+ }
+ """
+ .trimIndent(),
+ )
+
+ val expected =
+ """
+ src/test/pkg/Foo.java:7: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
+ @Nullable
+ ~~~~~~~~~
+ 1 errors, 0 warnings
+ """
+ .trimIndent()
+
+ val expectedFixDiffs =
+ """
+ Autofix for src/test/pkg/Foo.java line 7: Move annotation:
+ @@ -7 +7
+ - @Nullable
+ - public String[] foo() { return null; }
+ + public String @Nullable [] foo() { return null; }
+ """
+ .trimIndent()
+
+ runNullnessTest(input, expected, expectedFixDiffs)
+ }
+
+ @Test
+ fun `Nullness annotation removed from local variable declaration`() {
+ val input =
+ java(
+ """
+ package test.pkg;
+ import androidx.annotation.Nullable;
+ public class Foo {
+ public String foo() {
+ @Nullable String str = null;
+ return str;
+ }
+ }
+ """
+ .trimIndent()
+ )
+ val expected =
+ """
+ src/test/pkg/Foo.java:5: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
+ @Nullable String str = null;
+ ~~~~~~~~~
+ 1 errors, 0 warnings
+ """
+ .trimIndent()
+ val expectedFixDiffs =
+ """
+ Autofix for src/test/pkg/Foo.java line 5: Delete:
+ @@ -5 +5
+ - @Nullable String str = null;
+ + String str = null;
+ """
+ .trimIndent()
+
+ runNullnessTest(input, expected, expectedFixDiffs)
+ }
+
+ @Test
+ fun `Nullness annotation removed from void return type`() {
+ val input =
+ java(
+ """
+ package test.pkg;
+ import androidx.annotation.Nullable;
+ public class Foo {
+ @Nullable
+ public void foo() {}
+ }
+ """
+ .trimIndent()
+ )
+
+ val expected =
+ """
+ src/test/pkg/Foo.java:4: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
+ @Nullable
+ ~~~~~~~~~
+ 1 errors, 0 warnings
+ """
+ .trimIndent()
+
+ val expectedFixDiffs =
+ """
+ Autofix for src/test/pkg/Foo.java line 4: Delete:
+ @@ -4 +4
+ - @Nullable
+ """
+ .trimIndent()
+
+ runNullnessTest(input, expected, expectedFixDiffs)
+ }
+
+ @Test
+ fun `Nullness annotation removed from primitive parameter type`() {
+ val input =
+ java(
+ """
+ package test.pkg;
+ import androidx.annotation.Nullable;
+ public class Foo {
+ public void foo(@Nullable int i) {}
+ }
+ """
+ .trimIndent()
+ )
+
+ val expected =
+ """
+ src/test/pkg/Foo.java:4: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
+ public void foo(@Nullable int i) {}
+ ~~~~~~~~~
+ 1 errors, 0 warnings
+ """
+ .trimIndent()
+
+ val expectedFixDiffs =
+ """
+ Autofix for src/test/pkg/Foo.java line 4: Delete:
+ @@ -4 +4
+ - public void foo(@Nullable int i) {}
+ + public void foo(int i) {}
+ """
+ .trimIndent()
+
+ runNullnessTest(input, expected, expectedFixDiffs)
+ }
+
+ @Test
+ fun `Nullness annotation on class type parameter`() {
+ val input =
+ java(
+ """
+ package test.pkg;
+ import androidx.annotation.NonNull;
+ public class Foo {
+ public void foo(@NonNull Foo.InnerFoo arr) {}
+ public class InnerFoo {}
+ }
+ """
+ .trimIndent()
+ )
+
+ val expected =
+ """
+ src/test/pkg/Foo.java:4: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
+ public void foo(@NonNull Foo.InnerFoo arr) {}
+ ~~~~~~~~
+ 1 errors, 0 warnings
+ """
+ .trimIndent()
+
+ val expectedFixDiffs =
+ """
+ Autofix for src/test/pkg/Foo.java line 4: Move annotation:
+ @@ -4 +4
+ - public void foo(@NonNull Foo.InnerFoo arr) {}
+ + public void foo(Foo.@NonNull InnerFoo arr) {}
+ """
+ .trimIndent()
+
+ runNullnessTest(input, expected, expectedFixDiffs)
+ }
+
+ @Test
+ fun `Nullness annotation on class type return`() {
+ val input =
+ java(
+ """
+ package test.pkg;
+ import androidx.annotation.Nullable;
+ public class Foo {
+ @Nullable
+ public String foo() { return null; }
+ }
+ """
+ .trimIndent()
+ )
+
+ val expected =
+ """
+ src/test/pkg/Foo.java:4: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
+ @Nullable
+ ~~~~~~~~~
+ 1 errors, 0 warnings
+ """
+ .trimIndent()
+
+ val expectedFixDiffs =
+ """
+ Autofix for src/test/pkg/Foo.java line 4: Move annotation:
+ @@ -4 +4
+ - @Nullable
+ - public String foo() { return null; }
+ + public @Nullable String foo() { return null; }
+ """
+ .trimIndent()
+
+ runNullnessTest(input, expected, expectedFixDiffs)
+ }
+
+ @Test
+ fun `Nullness annotation on class type param`() {
+ val input =
+ java(
+ """
+ package test.pkg;
+ import androidx.annotation.Nullable;
+ public class Foo {
+ public String foo(@Nullable String foo) { return null; }
+ }
+ """
+ .trimIndent()
+ )
+
+ val expected =
+ """
+ src/test/pkg/Foo.java:4: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
+ public String foo(@Nullable String foo) { return null; }
+ ~~~~~~~~~
+ 1 errors, 0 warnings
+ """
+ .trimIndent()
+
+ val expectedFixDiffs = ""
+
+ runNullnessTest(input, expected, expectedFixDiffs)
+ }
+
+ @Test
+ fun `Nullness annotation on class type param and return`() {
+ val input =
+ java(
+ """
+ package test.pkg;
+ import androidx.annotation.NonNull;
+ import androidx.annotation.Nullable;
+ public class Foo {
+ @Nullable
+ public String foo(@NonNull String foo) { return null; }
+ }
+ """
+ .trimIndent()
+ )
+
+ val expected =
+ """
+ src/test/pkg/Foo.java:5: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
+ @Nullable
+ ~~~~~~~~~
+ src/test/pkg/Foo.java:6: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
+ public String foo(@NonNull String foo) { return null; }
+ ~~~~~~~~
+ 2 errors, 0 warnings
+ """
+ .trimIndent()
+
+ val expectedFixDiffs =
+ """
+ Autofix for src/test/pkg/Foo.java line 5: Move annotation:
+ @@ -5 +5
+ - @Nullable
+ - public String foo(@NonNull String foo) { return null; }
+ + public @Nullable String foo(@NonNull String foo) { return null; }
+ """
+ .trimIndent()
+
+ runNullnessTest(input, expected, expectedFixDiffs)
+ }
+
+ @Test
+ fun `Nullness annotation on type parameter return`() {
+ val input =
+ java(
+ """
+ package test.pkg;
+ import androidx.annotation.Nullable;
+ public class Foo {
+ @Nullable
+ public <T> T foo() {
+ return null;
+ }
+ }
+ """
+ .trimIndent()
+ )
+
+ val expected =
+ """
+ src/test/pkg/Foo.java:4: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
+ @Nullable
+ ~~~~~~~~~
+ 1 errors, 0 warnings
+ """
+ .trimIndent()
+ val expectedFixDiffs =
+ """
+ Autofix for src/test/pkg/Foo.java line 4: Move annotation:
+ @@ -4 +4
+ - @Nullable
+ - public <T> T foo() {
+ + public <T> @Nullable T foo() {
+ """
+ .trimIndent()
+
+ runNullnessTest(input, expected, expectedFixDiffs)
+ }
+
+ @Test
+ fun `Nullness annotation on inner type where outer type contains name`() {
+ val input =
+ java(
+ """
+ package test.pkg;
+ import androidx.annotation.Nullable;
+ public class RecyclerView {
+ public class Recycler {}
+ @Nullable
+ public RecyclerView.Recycler foo() {
+ return null;
+ }
+ }
+ """
+ .trimIndent()
+ )
+
+ val expected =
+ """
+ src/test/pkg/RecyclerView.java:5: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
+ @Nullable
+ ~~~~~~~~~
+ 1 errors, 0 warnings
+ """
+ .trimIndent()
+ val expectedFixDiffs =
+ """
+ Autofix for src/test/pkg/RecyclerView.java line 5: Move annotation:
+ @@ -5 +5
+ - @Nullable
+ - public RecyclerView.Recycler foo() {
+ + public RecyclerView.@Nullable Recycler foo() {
+ """
+ .trimIndent()
+
+ runNullnessTest(input, expected, expectedFixDiffs)
+ }
+
+ @Test
+ fun `Nullness annotation on parameterized type`() {
+ val input =
+ java(
+ """
+ package test.pkg;
+ import androidx.annotation.Nullable;
+ import java.util.List;
+ public class Foo {
+ @Nullable
+ public List<String> foo() {
+ return null;
+ }
+ }
+ """
+ .trimIndent()
+ )
+
+ val expected =
+ """
+ src/test/pkg/Foo.java:5: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
+ @Nullable
+ ~~~~~~~~~
+ 1 errors, 0 warnings
+ """
+ .trimIndent()
+ val expectedFixDiffs =
+ """
+ Autofix for src/test/pkg/Foo.java line 5: Move annotation:
+ @@ -5 +5
+ - @Nullable
+ - public List<String> foo() {
+ + public @Nullable List<String> foo() {
+ """
+ .trimIndent()
+
+ runNullnessTest(input, expected, expectedFixDiffs)
+ }
+
+ @Test
+ fun `Nullness annotation on parameter with newline after type`() {
+ val input =
+ java(
+ """
+ package test.pkg;
+ import androidx.annotation.NonNull;
+ public class Foo {
+ public void foo(@NonNull String
+ arg) {}
+ }
+ """
+ .trimIndent()
+ )
+
+ val expected =
+ """
+ src/test/pkg/Foo.java:4: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
+ public void foo(@NonNull String
+ ~~~~~~~~
+ 1 errors, 0 warnings
+ """
+ .trimIndent()
+ val expectedFixDiffs = ""
+
+ runNullnessTest(input, expected, expectedFixDiffs)
+ }
+
+ private fun runNullnessTest(input: TestFile, expected: String, expectedFixDiffs: String) {
+ lint()
+ .files(*stubs, input)
+ // Skip WHITESPACE mode because an array suffix with whitespace in the middle "[ ]" will
+ // break the pattern matching, but is extremely unlikely to happen in practice.
+ // Skip FULLY_QUALIFIED mode because the type-use annotation positioning depends on
+ // whether types are fully qualified, so fixes will be different.
+ .skipTestModes(TestMode.WHITESPACE, TestMode.FULLY_QUALIFIED)
+ .run()
+ .expect(expected)
+ .expectFixDiffs(expectedFixDiffs)
+ }
+
+ companion object {
+ val annotationStubs =
+ arrayOf(
+ kotlin(
+ """
+ package androidx.annotation
+ annotation class NonNull
+ """
+ ),
+ kotlin(
+ """
+ package androidx.annotation
+ annotation class Nullable
+ """
+ )
+ )
+ }
+}
diff --git a/lint/lint-gradle/build.gradle b/lint/lint-gradle/build.gradle
index e578727..2ac0206 100644
--- a/lint/lint-gradle/build.gradle
+++ b/lint/lint-gradle/build.gradle
@@ -21,6 +21,8 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
+
+import androidx.build.KotlinTarget
import androidx.build.LibraryType
import androidx.build.Publish
@@ -45,4 +47,5 @@
publish = Publish.SNAPSHOT_AND_RELEASE
inceptionYear = "2024"
description = "Lint checks to verify usage of Gradle APIs."
+ kotlinTarget = KotlinTarget.KOTLIN_1_9
}
diff --git a/navigation/navigation-common/build.gradle b/navigation/navigation-common/build.gradle
index 7921162..bcde5af 100644
--- a/navigation/navigation-common/build.gradle
+++ b/navigation/navigation-common/build.gradle
@@ -21,6 +21,8 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
+
+import androidx.build.KotlinTarget
import androidx.build.LibraryType
plugins {
@@ -78,4 +80,5 @@
inceptionYear = "2017"
description = "Android Navigation-Common"
legacyDisableKotlinStrictApiMode = true
+ kotlinTarget = KotlinTarget.KOTLIN_1_9
}
diff --git a/navigation/navigation-compose/build.gradle b/navigation/navigation-compose/build.gradle
index 9fa7d67..0088065 100644
--- a/navigation/navigation-compose/build.gradle
+++ b/navigation/navigation-compose/build.gradle
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+
+import androidx.build.KotlinTarget
import androidx.build.LibraryType
plugins {
@@ -68,6 +70,7 @@
legacyDisableKotlinStrictApiMode = true
samples(projectOrArtifact(":navigation:navigation-compose:navigation-compose-samples"))
legacyDisableKotlinStrictApiMode = true
+ kotlinTarget = KotlinTarget.KOTLIN_1_9
}
android {
diff --git a/navigation/navigation-compose/samples/build.gradle b/navigation/navigation-compose/samples/build.gradle
index 19e494b..b672a68 100644
--- a/navigation/navigation-compose/samples/build.gradle
+++ b/navigation/navigation-compose/samples/build.gradle
@@ -21,6 +21,8 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
+
+import androidx.build.KotlinTarget
import androidx.build.LibraryType
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
@@ -52,6 +54,7 @@
type = LibraryType.SAMPLES
inceptionYear = "2020"
description = "Samples for Compose integration with Navigation"
+ kotlinTarget = KotlinTarget.KOTLIN_2_0
}
tasks.withType(KotlinCompile).configureEach {
diff --git a/navigation/navigation-dynamic-features-fragment/build.gradle b/navigation/navigation-dynamic-features-fragment/build.gradle
index a1527d203..34a65f65 100644
--- a/navigation/navigation-dynamic-features-fragment/build.gradle
+++ b/navigation/navigation-dynamic-features-fragment/build.gradle
@@ -21,6 +21,8 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
+
+import androidx.build.KotlinTarget
import androidx.build.LibraryType
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
@@ -68,4 +70,5 @@
inceptionYear = "2019"
description = "Android Dynamic Feature Navigation Fragment"
legacyDisableKotlinStrictApiMode = true
+ kotlinTarget = KotlinTarget.KOTLIN_1_9
}
diff --git a/navigation/navigation-fragment-compose/build.gradle b/navigation/navigation-fragment-compose/build.gradle
index 9b1cc7b..415e042 100644
--- a/navigation/navigation-fragment-compose/build.gradle
+++ b/navigation/navigation-fragment-compose/build.gradle
@@ -21,6 +21,8 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
+
+import androidx.build.KotlinTarget
import androidx.build.LibraryType
plugins {
@@ -56,4 +58,5 @@
inceptionYear = "2024"
description = "Add Compose destinations to Navigation with Fragments"
legacyDisableKotlinStrictApiMode = true
+ kotlinTarget = KotlinTarget.KOTLIN_1_9
}
diff --git a/navigation/navigation-fragment/build.gradle b/navigation/navigation-fragment/build.gradle
index b20d87a..6c5a89a 100644
--- a/navigation/navigation-fragment/build.gradle
+++ b/navigation/navigation-fragment/build.gradle
@@ -21,6 +21,8 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
+
+import androidx.build.KotlinTarget
import androidx.build.LibraryType
plugins {
@@ -57,6 +59,7 @@
inceptionYear = "2017"
description = "Android Navigation-Fragment"
legacyDisableKotlinStrictApiMode = true
+ kotlinTarget = KotlinTarget.KOTLIN_1_9
}
android {
diff --git a/navigation/navigation-lint-common/build.gradle b/navigation/navigation-lint-common/build.gradle
index 4ed49c5..192dd51 100644
--- a/navigation/navigation-lint-common/build.gradle
+++ b/navigation/navigation-lint-common/build.gradle
@@ -31,6 +31,8 @@
dependencies {
compileOnly(libs.androidLintMinApi)
compileOnly(libs.kotlinStdlib)
+ compileOnly(libs.androidLintTests)
+ compileOnly(libs.junit)
}
androidx {
diff --git a/navigation/navigation-lint-common/src/main/java/androidx/navigation/lint/common/LintUtil.kt b/navigation/navigation-lint-common/src/main/java/androidx/navigation/lint/common/LintUtil.kt
index 611f180..85d67c3 100644
--- a/navigation/navigation-lint-common/src/main/java/androidx/navigation/lint/common/LintUtil.kt
+++ b/navigation/navigation-lint-common/src/main/java/androidx/navigation/lint/common/LintUtil.kt
@@ -16,6 +16,11 @@
package androidx.navigation.lint.common
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest.kotlin
+import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.lint.checks.infrastructure.TestFiles
+import java.util.Locale
+import org.intellij.lang.annotations.Language
import org.jetbrains.kotlin.analysis.api.analyze
import org.jetbrains.kotlin.analysis.api.symbols.KtClassKind
import org.jetbrains.kotlin.analysis.api.symbols.KtClassOrObjectSymbol
@@ -66,3 +71,60 @@
symbol.classKind == KtClassKind.COMPANION_OBJECT) to symbol.name?.asString()
}
}
+
+/**
+ * Copied from compose.lint.test.stubs
+ *
+ * Utility for creating a [kotlin] and corresponding [bytecode] stub, to try and make it easier to
+ * configure everything correctly.
+ *
+ * @param filename name of the Kotlin source file, with extension - e.g. "Test.kt". These should be
+ * unique across a test.
+ * @param filepath directory structure matching the package name of the Kotlin source file. E.g. if
+ * the source has `package foo.bar`, this should be `foo/bar`. If this does _not_ match, lint will
+ * not be able to match the generated classes with the source file, and so won't print them to
+ * console.
+ * @param source Kotlin source for the bytecode
+ * @param bytecode generated bytecode that will be used in tests. Leave empty to generate the
+ * bytecode for [source].
+ * @return a pair of kotlin test file, to bytecode test file
+ */
+fun kotlinAndBytecodeStub(
+ filename: String,
+ filepath: String,
+ checksum: Long,
+ @Language("kotlin") source: String,
+ vararg bytecode: String
+): KotlinAndBytecodeStub {
+ val filenameWithoutExtension = filename.substringBefore(".").lowercase(Locale.ROOT)
+ val kotlin = kotlin(source).to("$filepath/$filename")
+ val bytecodeStub =
+ TestFiles.bytecode("libs/$filenameWithoutExtension.jar", kotlin, checksum, *bytecode)
+ return KotlinAndBytecodeStub(kotlin, bytecodeStub)
+}
+
+class KotlinAndBytecodeStub(val kotlin: TestFile, val bytecode: TestFile)
+
+/**
+ * Copied from compose.lint.test.stubs
+ *
+ * Utility for creating a [bytecode] stub, to try and make it easier to configure everything
+ * correctly.
+ *
+ * @param filename name of the Kotlin source file, with extension - e.g. "Test.kt". These should be
+ * unique across a test.
+ * @param filepath directory structure matching the package name of the Kotlin source file. E.g. if
+ * the source has `package foo.bar`, this should be `foo/bar`. If this does _not_ match, lint will
+ * not be able to match the generated classes with the source file, and so won't print them to
+ * console.
+ * @param source Kotlin source for the bytecode
+ * @param bytecode generated bytecode that will be used in tests. Leave empty to generate the
+ * bytecode for [source].
+ */
+fun bytecodeStub(
+ filename: String,
+ filepath: String,
+ checksum: Long,
+ @Language("kotlin") source: String,
+ vararg bytecode: String
+): TestFile = kotlinAndBytecodeStub(filename, filepath, checksum, source, *bytecode).bytecode
diff --git a/navigation/navigation-runtime-lint/src/test/java/androidx/navigation/runtime/lint/Stub.kt b/navigation/navigation-runtime-lint/src/test/java/androidx/navigation/runtime/lint/Stub.kt
index 4b568a9..89df0cd 100644
--- a/navigation/navigation-runtime-lint/src/test/java/androidx/navigation/runtime/lint/Stub.kt
+++ b/navigation/navigation-runtime-lint/src/test/java/androidx/navigation/runtime/lint/Stub.kt
@@ -16,8 +16,244 @@
package androidx.navigation.runtime.lint
-val TEST_CLASS =
- """
+import androidx.navigation.lint.common.bytecodeStub
+import androidx.navigation.lint.common.kotlinAndBytecodeStub
+
+internal val NAV_CONTROLLER =
+ bytecodeStub(
+ "NavController.kt",
+ "androidx/navigation",
+ 0x40e8c1a8,
+ """
+package androidx.navigation
+
+import kotlin.reflect.KClass
+
+open class NavController {
+
+ fun navigate(resId: Int) {}
+
+ fun navigate(route: String) {}
+
+ fun <T : Any> navigate(route: T) {}
+}
+
+inline fun NavController.createGraph(
+ startDestination: Any,
+ route: KClass<*>? = null,
+): NavGraph { return NavGraph() }
+""",
+ """
+META-INF/main.kotlin_module:
+H4sIAAAAAAAA/2NgYGBmYGBgBGJOBijgMuQSTsxLKcrPTKnQy0ssy0xPLMnM
+zxPi90ssc87PKynKz8lJLfIuEeIECnjkF5d4l3CJcnEn5+fqpVYk5hbkpAqx
+haSChJUYtBgAOMX57WIAAAA=
+""",
+ """
+androidx/navigation/NavController.class:
+H4sIAAAAAAAA/41SW28SQRT+ZoFl2dIW0NYWW7UXLW21Sxt9kaaJNjFug2gs
+4aVPA2zowDKb7A6kj/wW/4FPGh9M46M/ynhmIb1qdJM99++bc2bOz1/fvgN4
+jj2GFS7bYSDaZ47kQ9HhSgTSqfHhYSBVGPi+F6bBGHJdPuSOz2XHed/sei2V
+RoLB3BdSqAOGRGmzkUUKpo0k0gxJdSoihrXqP9krDNYk5xGu5G42GFKhF7lt
+BuYyzJWql2cfq1DITkXXrFWDsON0PdUMuZCRw6UMVHxA5NQCVRv4fkUzBQPl
+WcgzPOgFyhfS6Q77jpDKCyX3HVdqxki0ojTu0GGtU6/Vm8A/8JD3PSpk2Lja
+xPgCKn9qK4s5zNu4i3sMhdsFN6aZEOlplvbrL29nDkr1epwu3M4x5KuTid55
+ire54hQz+sMEPS3TIqMF6BZ7FD8T2iuT1d5lCM9HS7axYNhG7nxkG5Y26LeS
+pC36Z6z5hfPRnlFmr1M/PpmUPFrOJYpGOblqWeejXGqLUntmziwab8cF6aOZ
+cQFFLdKZKz5VlW19Mu2b7qdO+3RtCXZ6it7+MGjTCsxWhfRqg37TC+u86Xt6
++KDF/QYPhfYnwfWPA6lE33PlUESCQhev9epyERgyx6IjuRqEBLGPg0HY8t4I
+jV+c4Btj9BUQVmDQFusvSd3SUpPcJs/RvZNObX2B9ZkMA09JmnHQxDOS2XEB
+MrBJ5zEVRzT4RVxP498EWjFwfpycALU1jRmSmmKWcpqiQlpXpbcLha9YuE5k
+EvCSKH1BlCaKRcrvaFv3n5s0VkTif1izf2VdorwTV9+/zm6gHMst7JKuUnSZ
+ruTBCRIuHrp45NINr5KJNRfreHwCFuEJNk4wFcGOUIpgRtomYzNCPkIxwnTs
+ln4DameR7LoEAAA=
+""",
+ """
+androidx/navigation/NavControllerKt.class:
+H4sIAAAAAAAA/61TbU8TQRB+9o62RxUoRSqv9YWqgC9XKr6WEA0GvVjQiCEx
+JJqlXcrS6x252zYmJuon/4P/wm8aTQzxoz/KOHseCCLiBz7s7Mzs7DPPzM5+
+//H5K4BpzDCMca8W+LL20vZ4W9a5kr5nL/L2nO+pwHddETxUKTCGzAZvc9vl
+Xt1+tLohquQ1GY5VA8GVuB/wzXUGd7xyKFy58idQudLwlSs9OxBrLtn2wzmX
+h2F54iCwKFuZwT/KdDOTs4dnHKv4Qd3eEGo14NILbe55voqiQnvRV4st16Wo
+wr+iKISvuoLCkjNqXYazFtIM+ZjTRrtpS0+JwOOu7VARdF9WwxSOM/RX10W1
+Ead5zAPeFBTIcGH8LzX+9ixpkHp5Yvk4utGTRhcy9Jqh4oG6J0IlvYiZhSzD
+yL/KT+GE5iw9qWYZzHENmMPJNPoxQIAFWVgr7JkG5jD0FnSNe/1j//FqDNn9
+RTEkAr+lBMPJA0aGoW9XqkJNrPGWqxjeHOlgOvsjD52ckX2NeNEqTe8Q7N1O
+tSAUr3HF6YrRbJv0TZkWnVqAetrQikGHL6XWiqTVphjmt97m0ltv08aAES2t
+Zkj8cg2dI33IKLJJo2iUkhmT9I5St2VkEkNW1rDMAVZMPvj2ztJoJZrGw6oh
+Jpk9zbvSoCI65vwaPU5PRXpisdVcFcFTPer6Lf0qd5d5ILUdOzuXZJ1mrxWQ
+Pvyk5SnZFI7XlqGk47u/vwz9pz9Pd4Z/T1jXkuLVxgLfjBOkl/xWUBXzUhuD
+McbyPnxMwUCHbi/tg0ggSdZVsp7H/txk9tgn9F7M9pE0Z7+g/9lHDH6I4qdJ
+Jqkb3cjiGumTdKMbFoYwDP0+OYxgNMLOUUSeIrV2Cqfp7vUIIYUbMYZF+01a
+fWZsbMtOINOJMzhLuib2iq4laM+PJl6/R4ItHEjQxK1IMitimo3q6aFsvQTd
+E3HaZp3bxTqPsZh1fod1PmZt4HbEu4Qy7XforEBkzq3AdHDewQUH45hwCPKi
+g0u4vAIW4grsFaRCJEIUQ4yGyFLXQ5wKcfonI/JCLIwGAAA=
+"""
+ )
+
+internal val NAV_DESTINATION =
+ bytecodeStub(
+ "NavDestination.kt",
+ "androidx/navigation",
+ 0x89e4229a,
+ """
+package androidx.navigation
+
+open class NavDestination
+""",
+ """
+META-INF/main.kotlin_module:
+H4sIAAAAAAAA/2NgYGBmYGBgBGJOBijgMuQSTsxLKcrPTKnQy0ssy0xPLMnM
+zxPi90ssc87PKynKz8lJLfIuEeIECnjkF5d4l3CJcnEn5+fqpVYk5hbkpAqx
+haSChJUYtBgAOMX57WIAAAA=
+""",
+ """
+androidx/navigation/NavDestination.class:
+H4sIAAAAAAAA/4VRO08CQRD+ZoEDTpSHiuAjUWOhFh4SO42Jj5iQICZqaKwW
+7oLLYy/hFkLJb/EfWJlYGGLpjzLOnTRWNl++x+zMZPbr+/0DwAm2CLtSu0Nf
+uRNHy7HqSKN87TTk+NoLjNKRTIIIua4cS6cvdce5a3W9tkkiRrDOlFbmnBDb
+P2hmkIBlI44kIW6eVUDYq//f/pSQr/d801faufWMdKWR7InBOMZLUgjpEECg
+HvsTFaoKM/eYsD2b2rYoCVvkmM2mqWJpNq2KCl0mPl8skRNhXZXC1/m/c496
+hve88l2PkK0r7TVGg5Y3fJStPjuFut+W/aYcqlDPTfvBHw3b3o0KRfl+pI0a
+eE0VKE4vtPZN1DjADgSfYb5zeBXGEisn0kDi8A2pVyYCZUYrMuNYZ8z8FiAN
+O8o3IlzDZvRhhAXOMk+I1bBYw1INWeSYIl9DActPoAArWOU8gB2gGMD6AQ9d
+W4PtAQAA
+"""
+ )
+
+internal val NAV_GRAPH =
+ bytecodeStub(
+ "NavGraph.kt",
+ "androidx/navigation",
+ 0x54a108f5,
+ """
+package androidx.navigation
+
+open class NavGraph: NavDestination() {
+ fun <T : Any> setStartDestination(startDestRoute: T) {}
+}
+""",
+ """
+META-INF/main.kotlin_module:
+H4sIAAAAAAAA/2NgYGBmYGBgBGJOBijgMuQSTsxLKcrPTKnQy0ssy0xPLMnM
+zxPi90ssc87PKynKz8lJLfIuEeIECnjkF5d4l3CJcnEn5+fqpVYk5hbkpAqx
+haSChJUYtBgAOMX57WIAAAA=
+""",
+ """
+androidx/navigation/NavGraph.class:
+H4sIAAAAAAAA/31SXU8TQRQ9s2232wVpAUGo4AegFFC2Ep8sIfEjaE2tSpu+
+8DRtN2X6MWt2pg2P/S3+A580PpjGR3+U8c62aA3iJnPvnXPvuXNm7/z4+fUb
+gMfYZ1jjshkGonnuST4QLa5FIL0yH7wM+YezJBjDxhUVL3ylhYy2ScQY7EMh
+hT5iiOV2arNIwHYRR5Ihrs+EYrhV+t9RBYZF5euK5qGe6sywlCu1+YB7XS5b
+3tt622/owk6NhB9Wn1zOHOWq1Si9WQrCltf2dT3kQiqPSxnoqKXyyoEu97td
+OnJOXZx3EvS17yBNOjuB7grptQc9T0jth5J3vaLUIbURDZXEPIlqnPmNzqTP
+Ox7ynk+FDNv/EDuFVEyTVsH8nkVcd7GAJYaFyxSG+dJExRtf8ybXnDCrN4jR
+2JgxKWPAwDqEnwuzy1PUfMTwfjTMutaKNV4OrYzljobkjHEsZ3llNDyw8uxZ
+4vtHm5Kv1zOxrJWPbzjOaJhJ7Fp5+8DOJLPWq3GBYxofMGxdNcCpeZFMo6rK
+MHMx2f2OpjfwPGj6DOmSkH6536v7YZXXu765fdDg3RoPhdlPwFRFtKhfP6R4
+66Qvtej5RTkQSlD69+9++mekDG4l6IcN/1gY/uqEUxszpgpxFxa9SvNZJJQe
+Kdlt2nlGNvnE7mc4n6J0jqwdgXHskJ0dFyAFl/w8ZgiJReQCVVvkk3sLmS9Y
+/ptuE8XQl8clE7qJ0rhB+d2o+hr2DGZEzEXAg8jex0Pyx4SuUJvVU8SKyBZx
+s4g1rFOIW0Xcxp1TMHO1jVOkFFyFTQVbYUZhS+FeZNMKs78AJaVXJ/gDAAA=
+"""
+ )
+
+internal val NAV_HOST =
+ bytecodeStub(
+ "NavHost.kt",
+ "androidx/navigation",
+ 0x5ce5aeda,
+ """
+package androidx.navigation
+
+import kotlin.reflect.KClass
+
+interface NavHost
+
+inline fun NavHost.createGraph(
+ startDestination: Any,
+ route: KClass<*>? = null,
+): NavGraph { return NavGraph() }
+""",
+ """
+META-INF/main.kotlin_module:
+H4sIAAAAAAAA/2NgYGBmYGBgBGJOBijgMuQSTsxLKcrPTKnQy0ssy0xPLMnM
+zxPi90ssc87PKynKz8lJLfIuEeIECnjkF5d4l3CJcnEn5+fqpVYk5hbkpAqx
+haSChJUYtBgAOMX57WIAAAA=
+""",
+ """
+androidx/navigation/NavHost.class:
+H4sIAAAAAAAA/32OzUoDMRSFz81of8a/qVqoiK9g2uLOlZviQFVQcDOrtBNL
+OtMEmnToss/lQrr2oaR3qmsTOPecc+FLvn8+vwDcoUu4VjZfOpOvpVWVmalg
+nJXPqnp0PjRBhGSuKiVLZWfyZTLXU24jQmdcuFAaK590ULkK6p4gFlXEWKql
+XQsIVHC/NnXqs8sHhO5204pFT8QiYffR226Gok/1cki4Gf/zH36DkfFfui0C
+hze3Wk71yJSacPW6ssEs9LvxZlLqB2td2AN8g/E4wO8RuNjrOS55Dhh5yLeR
+IUrRTNFK0UbMFkcpjnGSgTxOcZZBeCQenR1YLcIOPwEAAA==
+""",
+ """
+androidx/navigation/NavHostKt.class:
+H4sIAAAAAAAA/61TbU8TQRB+9o62R1EsRSogrW9VAV+uVnwtIRqNerGgEUNi
+SDRLu5Sl1ztzu200JsZP/gf/hd80mhjjR3+UcfY8QUTgix92dmZ25plndme/
+//j0BcAMZhmKPGhGoWy+cAPeky2uZRi4C7x3L1T6vs6AMeTWeY+7Pg9a7oOV
+ddEgr80w0IgE1+JuxJ+vMaxO1ncBqtX/hqjV26H2ZeBGYtUn271/y+dK1aZ2
+gonr1Bjk/yk0Oz23d60T9TBquetCr0RcBsrlQRDqOEq5C6Fe6Po+RZV3i6IQ
+vuILCkvP6jWp5hxkGUoJp/Vex5WBFlHAfdcLdET5sqEy2Mcw0lgTjXZS5iGP
+eEdQIMPpyX/0uOlZNCCt2tTSPgziQBb7kaMXVJpH+rZQWgYxMwd5hond2s/g
+oOEsA6nnGOxJA1jAoSxGMEqAZVleLW+ZAOYxDJVNj1v9xV3fiyG/vR2GVBR2
+tWA4tMOYMAz/UaTcFKu862uGl/9pDL3tkXtOy8S25p91qzMb1IZ+l5oXmje5
+5pRidXo2fURmRL8RoHtsG8WiwxfSaBXSmhcY7nx9U8h+fZO1Rq14GTVH4pdr
+/CTp41aFTVsVq5rO2aT3VQcdK5cad/KWY4+ySvret7eOQavSBO7VDTHJJtd2
+vk30+26FTXqQA3UZiIVuZ0VEj81gm/cLG9xf4pE0duLsX5QtmrRuRPrhR91A
+y47wgp5Uko5vbn4Q+j1/n26M+paw/YuaN9rz/HlSILsYdqOGuCONMZZgLG3D
+xwVY6DMXS/sYUkiTdZGsp4m/MJ0f+IihM/lhkvbcZ4w8+YCx93H8DMk03cMg
+BnCJ9GnKGISDcRyGeZkCJlCMsQvIo0SRRjuCo5R7OUbI4EqC4dB+ldawnRi/
+ZT+Q68cxHCfdEHtFaSnaS8XU63dIsfkdCdq4FkvmxEzzcT8OVcuSdIjJJuvC
+H6xLOJGwLm2wLiWsLVyPeVdRo/0GnZWJzMll2B5OeTjtYRJTHkGe8XAW55bB
+FM7DXUZGIaVQUSgq5OnWFY4oHP0JmQ7w4mgGAAA=
+"""
+ )
+
+internal val TEST_NAV_HOST =
+ bytecodeStub(
+ "TestNavHost.kt",
+ "androidx/navigation",
+ 0x2f602e26,
+ """
+package androidx.navigation
+
+class TestNavHost: NavHost
+""",
+ """
+META-INF/main.kotlin_module:
+H4sIAAAAAAAA/2NgYGBmYGBgBGJOBijgMuQSTsxLKcrPTKnQy0ssy0xPLMnM
+zxPi90ssc87PKynKz8lJLfIuEeIECnjkF5d4l3CJcnEn5+fqpVYk5hbkpAqx
+haSChJUYtBgAOMX57WIAAAA=
+""",
+ """
+androidx/navigation/TestNavHost.class:
+H4sIAAAAAAAA/4VRTUtCQRQ99z01fVmpfWlW0q5a9EzaFUEFkWAGJW5cjb5H
+Teo8cEZp6W/pH7QKWoS07EdF9z2jWgQt5nDOuXe4Z+68f7y8AjhAiVASyhsE
+0ntwlRjJW2FkoNyGr01djC4CbWZAhMy9GAm3J9Ste9W+9zvs2oTiX1e/r8UJ
+iSOppDkm2Ns7zTRmkHQQQ4oQM3dSE7Zq/ww/JGRr3cD0pHIvfSM8YQR7Vn9k
+c34KIRUCCNRl/0GGqszM2ydsTMaOY+Wt6EzG+cm4YpXpNP72mLAyVthU4aY/
+M/zMn/8VZ69rOPtZ4PmEhZpUfn3Yb/uDhmj32MnVgo7oNcVAhvrLdG6C4aDj
+n8tQFK6Hysi+35RacvVEqcBE8zT2YfFqeGXTx4S7Ylxj5UYaiO8+w3liYqHI
+mIjMGNYZ09MGzDIL6xsRFrAZfTJhjmvzLdhVLFSRqSKLHFMsVrGE5RZIYwWr
+XNdIa+Q1kp+Mv2/DIQIAAA==
+"""
+ )
+
+internal val NAVIGATION_STUBS =
+ arrayOf(NAV_CONTROLLER, NAV_DESTINATION, NAV_GRAPH, NAV_HOST, TEST_NAV_HOST)
+
+internal val TEST_CODE =
+ kotlinAndBytecodeStub(
+ "Test.kt",
+ "androidx/test",
+ 0xef517b0b,
+ """
+package androidx.test
+
val classInstanceRef = TestClass()
val classInstanceWithArgRef = TestClassWithArg(15)
@@ -66,4 +302,477 @@
abstract class TestAbstractComp { companion object }
class AbstractChildClassComp(val arg: Boolean): TestAbstractComp() { companion object }
object AbstractChildObjectComp: TestAbstractComp()
+""",
+ """
+META-INF/main.kotlin_module:
+H4sIAAAAAAAA/2NgYGBmYGBgBGJOBijgMuQSTsxLKcrPTKnQy0ssy0xPLMnM
+zxPi90ssc87PKynKz8lJLfIuEeIECnjkF5d4l3CJc/HCtZSkFpcIsYWkgiSU
+GLQYABRWGrdkAAAA
+""",
+ """
+androidx/test/AbstractChildClass.class:
+H4sIAAAAAAAA/4VQTWsTURQ9781XMk3MJH6labW1KrRZmLS4U4ppQAhMu6gl
+i2T1khnaRyYzMO9Fusxvce1GUAQXElz6o8T7JlUQFBfv3HvuO5z78f3Hl68A
+nuMpw65IozyT0XVHx0p3ehOlczHV/SuZRP1EKOWBMbT+VF0Q/FJ6sBjclzKV
++pjB3h8dDBms/YNhBQ48HzZKxEV+ycBGFfjYKIOjQlJ9JRXDXvi/CV6Q/2Ws
+e8aCjEcM9XCW6USmndNYi0hoQRI+f2vRUsxA2QCo4Yzq19KwLmXRIcPJatnw
+eZMXb7X0ebDh85LVXC2PeJedVBtuwFu8a3175/LAPq//ZiVSt+ySE7jG6Yhh
+O/z3UWgeau+Z0rOZpl37WRQz1EKZxmeL+STOL8QkoUojzKYiGYpcGn5T9N9k
+i3wav5aGbJ4vUi3n8VAqSb+9NM200DJLFQ7pkHaxaMPclTJOuQOXcJfYMXFO
+0W9/Rrm99QnVD4XmEaHRADvYI7y3VuEWauZulBk3OjMCemuvjjknRaf9EdX3
+f7WprAU3NhyPC9zBE4qviiEd3B7DGuDOAHcH1PY+pWgOsInWGExhC9tjeAo1
+hQcKvsJDBVchUKj/BOsI9uW0AgAA
+""",
+ """
+androidx/test/AbstractChildClassComp$Companion.class:
+H4sIAAAAAAAA/5VSTW/TQBB9u07jxARIWz4SvgolSC0SdRJxK0IqQUiRUpCg
+yqUHtLG3dBN7jbybqMec+CH8g56QOKCoR34UYtYJcKWX2Zn35s143/rnr+8/
+ADzHE4Y9oeM8U/FZaKWx4cHI2FxEtneqkriXCGN6Wfq55YLQKtM+GEN9LGYi
+TIT+FL4bjWVkfXgM5RdKK/uSwdvZHdawhnKAEnyGkj1VhqE9uNyqfYbOzmCS
+2UTpcDxLQ6WtzLVIwtfyREwT28s0TZhGNssPRT6R+f7uMAB3Kzdb0T/yY1qw
+dNfLTWNY/yM4lFbEwgrCeDrzyDzmQtUFMLAJ4WfKVW3K4g5DazEPAt7gAa9T
+tphXLr54jcW8y9vslV/hF1/LvM5db5e5Ca3/8cbHXYbqX4MY/CNq3ptY8riX
+xZLh+kBp+XaajmR+JEYJIRuDLBLJUOTK1Suw1tda5sVcSS8TfMimeSTfKMc1
+30+1VakcKqOo+UDrzApL6ww6ZG7J3ZhO7h6YPnyLqtBZQOfa02+onBf0Q4rl
+AuziEcXasgFVBECdUXZlJX5GJ1+Ja+eFnU5wawkuBUV2FdeI87BNVVCI7uE+
+mnhcLHyAVvFLkwfUWz+G18d6Hxt9bOIGpbjZp5m3j8EMGmgSbxAY3DEo/wZN
+V6SgDwMAAA==
+""",
+ """
+androidx/test/AbstractChildClassComp.class:
+H4sIAAAAAAAA/41SW08TQRT+ZrfXZZFSEQt4QUEsVdlCfBJCgjWaJoUHJE2E
+p2k7wtDtrNmZEh75LT77QtSQaGKIj/4o45lthRiN4WHPmXP2O9+5/vj55RuA
+p1hhmOeqE0eycxwYoU2w0dIm5m1TO5BhpxZyrWtR710WjOHOn8gdEhfoBOMy
+ZNakkmadIVXeXWwyuOXFpo80sh5SyJHN430GtuvDw0geDnyCmgOpGRYaV6lk
+lXLsC7NhaYh8lyG31g6HSZeuwjBvBVcyUllcZ1guN7qRIYbg8KgXSGVErHgY
+vBBveT+kxhRx9Nsmijd53BXx6qCfGx4mMMmQvyBjqF6pgcv0qz5KmLJDmGaY
+a0TxfnAoTCvmUumAKxUZbgimg63IbPXDkFof/13rpjC8ww0nn9M7cmmZzIq8
+FaABd8l/LK1VpVdnmeHV+UnRc0pO8p2feE5hxHNyqdL5yWx2xamyZyz7fLSY
+KTjTTtX9/j7jFFLb4xdWjkKmU7l0IWPp6GxmG/+/BiqM6sha91LXMMxs95WR
+PVFXR1LLVig2LhukE6hFHcEw1pBKbPV7LRHvcMIwFBtRm4dNHktrD51+XSkR
+JxMVFOy9jvpxW7yU9t/UME/zryxYpkmnaCIOpuzgqbxHZGVI3yJdtBdJ2iU7
+nXgfk7VOaIe0VzlDvjLzGaOnCcOTYSSwgiWSkwMUrmHMboBelo0WhgJ9A67A
+LoZ0uvIJox/+SeMPAEOaHBWVHQaXkKwW/ldMvGFnuPkRM6eJxyVim5DRRTpJ
+Y9WEu0INAzXy3ybGO3tw67hbx2wd93CfnpirYx4P9sA0FvBwDzmNMY2yhqex
+qJHRKGiMa5R+ATUXZWovBAAA
+""",
+ """
+androidx/test/AbstractChildObject.class:
+H4sIAAAAAAAA/4VSXWvUQBQ9M7ubZNPV1vrR3bZ+1PqgPpi2+GYR1kUhECPY
+ZaH0aZIMdrrZDCSzpY/75A/xHxQfCgqy6Js/SrwTV0VETMi995w5c27mJl+/
+ffgE4DHuMWyJIiu1ys4CIysT9JPKlCI1g2OVZ6+SE5kaF4xh/U/ZkMJPqYsG
+g7OvCmWeMjTuPxh10ILjowmXoWmOVcWwHf23zxMGbz/Nax8f3G72wvhg2I8H
+zzu4BL9N5GVrpcs3wYk0SSlUUQWiKLQRRmmqY23iaZ6T1ZVorA2ZBS+lEZkw
+gjg+OW3QuZkNbRvAwMbEnymLdqjKdqnBfOb7vMvrZz7zvrzl3flsj++wZ67H
+P79z+Aq30j2Gzejfc6GGrsWPxoZh4/W0MGoiw+JUVSrJZf/3W9OQBjqTDMuR
+KmQ8nSSyHArSMKxGOhX5SJTK4gXpH+hpmcoXyoLewnj0ly12aV7N+pA9Oz7K
+twg5lFcoc7pbNbpNKLCjoNx6eAHvvF6+sxCDqi2KnR8CtMkK8LD0a/Maqe21
+9BH88AKd91g+rwmOu3W8ie36f6PPQgarR2iEuBriWojruEEl1kJ00TsCq7CO
+DVqv4FfYrOB8Bw71GM6sAgAA
+""",
+ """
+androidx/test/AbstractChildObjectComp.class:
+H4sIAAAAAAAA/41SXWvUQBQ9M7ubzaarrfWju1Zrv6TVB9NW3yzCuigEYgS7
+LEifJpuhnW42I8ls6eM++UP8B8WHgoIs+uaPEu/EpSKCmJB77zlzciY5yfcf
+n74AeIJNhvsiS3KtkjPfyML4nbgwuRiY7rFKk9fxiaRRj97VwRhW/pT2qFzK
+S02FwdlXmTLPGCrbD/pN1OB4qKLOUDXHqmDYCv9rv6cM7v4gLb08cGvgBtFB
+rxN1XzRxBV6DyKsMG6HOj/wTaeJcqKzwRZZpI4zSNEfaROM0Jatr4VAbMvNf
+SSMSYQRxfHRaoQyYLQ1bwMCGxJ8pi3ZoSnZpg+nE83iLl9d04n57z1vTyR7f
+Yc/rLv/6weEL3Er3GFbDf+dDm9Yt92hoGJbfjDOjRjLITlWh4lR2fj85hdXV
+iWSYD1Umo/EolnlPkIZhMdQDkfZFriyekd6BHucD+VJZ0J4Z9/+yxS5lVi1f
+tG0jpL5CyKG+QJ3TWSvRPUK+jYN67eEF3PNyeXUmBh5jjWrzlwANsgJczF3e
+vERqe8x9Bn97geZHzJ+XBMd6We9io/z/6NOQweIhKgGuB7gR4CZu0YilAC20
+D8EK3MYyrRfwCtwp4PwEJ9evBbwCAAA=
+""",
+ """
+androidx/test/InterfaceChildClass.class:
+H4sIAAAAAAAA/4VQz28SQRT+ZhZY2FJZqFYKVq21teXg0sabprElMSFBTWrD
+AU4DO9Ipy26yMzQ98rd49mKiMfFgiEf/KOMbSpqYGD3M9973fnxv3vv569t3
+AM/whGFLxGGaqPAqMFKboB0bmb4XQ9k6V1HYioTWLhiDfyEuRRCJeBS8HVzI
+oXHhMNT/bD4juBFwkWXIvVCxMkcMmb3efpfB2dvvFuGi4CEDj7hIRwysV0QR
+qwVw3KJSc640w3bnvz97TgNG0hxbDVLuMZQ748REKg5eSyNCYQSV8MmlQ9sy
+CwULoIljil8py5rkhQcMJ/NZxeNVvnjzmcf9FY/nnep8dsib7GS1kvN5jTed
+Hx9y3M+clm9YnqprmXzWz1mlQ4bNzj/OQh+i+a6NPR0b2raVhJKh1FGxfDOd
+DGR6JgYRRSqdZCiirkiV5cug9y6ZpkP5SlmycTqNjZrIrtKKssdxnBhhVBJr
+HNApMzQnR69ib0uLcjsXecJHxI6Ic7Je4ytWGvUvKH1a1GwT2i6gjseE69dV
+8FG2pyPPqtGlSXdtqRXYi5LNNj6j9PGvMsXrgqUMx84Ct7BL9iXlblPuTh9O
+G+tt3G2jig1yUWtT/70+mMYm7vfhapQ1HmgUNR5q5DUqGmu/ARpxRv/QAgAA
+""",
+ """
+androidx/test/InterfaceChildClassComp$Companion.class:
+H4sIAAAAAAAA/5VSTW/TQBB9u07jxARIWz4SvqGp1CJRNxW3IiQIQrKUggRV
+Lj2gjb1tN7HXyLuJesyJH8I/6AmJA4p65EchZp0AV3qZnXlv3sz6rX/++v4D
+wHNsMoRCJ0WukrPQSmPDSFtZHItY9k5VmvRSYUwvzz53XBBa5doHY2iOxFSE
+qdAn4fvhSMbWh8dQfaG0si8ZvK3tQQMrqAaowGeo2FNlGLr9S+7aJ81Wf5zb
+VOlwNM1C5RRapOEbeSwmqe3l2thiEtu8OBDFWBb724MA3O1c78T/yE9ZyTLs
+XG4aw+ofwYG0IhFWEMazqUf2MRfqLoCBjQk/U67apSzpMnTmsyDgLR7wJmXz
+We3ii9eaz/b4Lnvt1/jF1ypvcte7x9yEzf8yx8ddhvpfhxj8Q+reGVtyuZcn
+kuF6X2n5bpINZXEohikha/08FulAFMrVS7ARaS2Lcq6ktwk+5pMilm+V49of
+JtqqTA6UUdT8SuvcCkvrDLrkbsV9Mp3cPTHd/CFVofOAzpWn31A7L+lHFKsl
+GOIxxcaiAXUEQJNRdmUpfkYnX4ob56WfTnBrAS4EZXYV14jz8ISqoBTdw320
+sVEufIBO+VeTB9TbPIIXYTXCWoR13KAUNyOaefsIzKCFNvEGgcEdg+pvYvaF
+YBIDAAA=
+""",
+ """
+androidx/test/InterfaceChildClassComp.class:
+H4sIAAAAAAAA/41S308TQRD+9lp67XFIWxRLEUUBKVW5gjwJIcEaTZOCCZIm
+wtO2Xcq11z1zu2145G/x2ReihkQTQ3z0jzLOlgoxGsPDzTczN/PNr/3x88s3
+AGtYY1jgshmFfvPY00JpryK1iA55Q5SP/KBZDrhS5bD7zgZjSLd5n3sBly3v
+db0tGtpGjGH6T4I9EpckNkYYEhu+9PUmQ7ywv1RjiBWWai5spBzE4ZDNoxYD
+23fhYiwFCzcoVB/5imGxeq3u1qlIS+gtw0Ps+wzJjUYwrOpdi2LeCC79UNq4
+xbBSqHZCTRReu9/1fJMjeeC9EIe8F+hyKJWOeg0dRts86oho/WKi2w4mkWNI
+XZIR0/VGuKq/7iKPabOHOwxz1TBqeW2h6xH3paJZZKi5pjDl7YR6pxcENHzm
+d7PbQvMm15x8VrcfoxszI1JGgHbcIf+xb6wSac0VhlfnJ1nHylmD7/zEsdKj
+jpWM585PZu1Vq8SeMfv5WDaRtvJWKfb9fcJKx3czl1aSUvLx5Eg6YehWGWaq
+/3kN1BU1YRvfckfTy9ntSe13RUX2feXXA7F1NR09gXLYFAzjVV+KnV63LqI9
+TjEM2WrY4EGNR76xh063IqWIBusUlOy8CXtRQ7z0zb+pYZ3aX1WwQmuOU1MJ
+wimzd9Kf0HoShHcJs+ZNEsZM40iSXCZrk6ItQqd4htHi9GeMn5JlwRtmgrQS
+ycmLKKSRMQcgzbDRvYh3YsjlmbsQjhQ/YfzDP2nci4AhTRI3kRom5zC4LNyv
+mHzLzjD1ETOnA0+MRjMF2aCJPA23OuB+jKeEZfLfI8bZA8QquF/BgwrmME8q
+Fip4iMUDMIUClg6QVMgoFBVchUfKmFmFCYX8L78zjKNFBAAA
+""",
+ """
+androidx/test/InterfaceChildObject.class:
+H4sIAAAAAAAA/4VSy27TQBQ9M0ljxzU0La+EUh59IGCB24odFVKJQLJkgkSj
+SKiriT1tJ3HGkj2JusyKD+EPKhaVQEIR7PgoxB0TihAS2Jr7OHPvuZ4z/vb9
+42cAT3CfYV3oJM9UchoYWZgg1EbmRyKW7ROVJq/7AxkbB4yhMRATEaRCHwe/
+0ArD6p/dXTIXDA4WGGp7SivzjKHy4GHPhwPXQxV1hqo5UQXDZvT/+U8Z3L04
+LYk8cNvthp2D7n6n/cLHEvw6gQ2GjSjLj4OBNP1cKF0EQuvMCKMyijuZ6YzT
+lKiWo2FmiCx4JY1IhBGE8dGkQoIwa+rWgIENCT9VNtumKNmhAbOp5/EmL9ds
+6n59x5uz6S7fZs8dl395X+MNbkt3GdaifyhDEx0LPB4akvDNWBs1kqGeqEL1
+U7n/+7NJpnaWSIalSGnZGY/6Mu8KqmFYibJYpD2RK5vPQe8gG+exfKls0poT
+9/6ixQ4JVqUz1mi1rILk79JBbb5CntNLV0XZPcoCqwb5hUfn8M7K7fV5MXAL
+G2T9nwVYpAjUeOmi+QZV22fxE/jbc1z+gOWzEuDYLO0dbJX/IsMVIrh6iEqI
+ayGuh9TapBCtEDexeghW0LA12i/gF7hdwP0BVe1yPcgCAAA=
+""",
+ """
+androidx/test/Outer$InnerClass.class:
+H4sIAAAAAAAA/4VU308cVRT+7sz+mB0WmOVXKay0yorL0nYAW62FVgFFBpel
+QkOs+HLZvcLAMoMzs6S+GJ76JzTRFxNjfOKhTRSMTQy2b/5NxnjuznS3LgSS
+mXvOPXPOd757zrnz979//AngJlYZhrhT8Vy78sgMhB+Yy7VAeDnLcYQ3V+W+
+nwRjMLb5Pjer3Nk0lze2RTlIQmVITNuOHdxjiOWt0TUGNT+6lkYcSR0xaAya
+LVFmvE0GZqWhoy0FBWnyD7Zsn+Fq8fzUUwxtmyKwGiiUwGLQy+7unusIJ5gg
+qLK79y3DMDG4GG246Hqb5rYINjxuO77JHccNeGC7pJfcoFSrVqfkARI68exj
+SEvwXEV8zWvVgGEtf1EKyyq2VmrqQl5pdKNHZhygkgXuauDZDh22Jz/6Glho
+pTNcarXN1uxqRXhJDOm4Isve08TOv+rAXQ1vUsP43p5wKgzX86ehT2eLkIng
+MHIS/G2GrCz0eY7vSMe8dJw737EgHcfSyOINqV2nw29xf2vOrQiGTDPScgKx
+Kc83Hg4aTZKJSR0TeJdOJL6p8SrNUm/+jMp/yZA7r+XUb75RFVTVuBtsCY+h
+6zQKkSnuuEHVdswlEfAKDzjZlN19lW4Qk0tKLqAh3yH7I1vuiKtSofH8+eRg
+SFf6FV0xTg50ehRD0xUtQbKNpEqyQ3vxWOs/OZhUxtlse1fCUAaUcfXFTwnF
+iC2mjKTcLbx8rC52Gxrp5KhpSuhEZkbmFOn6pGa0DcT62ThbePlEpcB06PGE
+kd5OeofUVzINeI3oDMS0uJGQXCeZPEH3GaOaxDzdueZMMSQf0McbO3QjYmG3
+Oou2I0q13Q3hPZAFlXV0y7y6xj1b7iPj4ErNCexdYTn7tm+TaabZDIb21YCX
+d5b4XuSda/W+zz2+K4jR/8LSTWaCtvqqW/PKYt6WEJcjiLVT6Wh6FPpJyTN3
+yR8TaRrpdPVpXaTdPH1XSOqFY6QKg7+h/RntFHxGawdki3spvg8pkkXa9YXe
+9K1TDgNpEpVmBwa9IaYpZ4RkvPAr2g8bcIm6sa8Okw4dIpgMkXsVPNwazM4M
+oF8JwcqACWIpOaWeQ3k4eIxLTxtBIdlUg2wqIrsUsekFjBT6cTnKPRIVK5ON
+ffc9NMlgujB4hMEQskSrCiYR6DJH6e+QlNSyz3Hl4TGudr11hBEZeYRRY/QI
+145w42nLMbIRo9d40Go2ajAS1aDO4HfcbC2DFsUz3MJ7EY+vSMp25QpjvyAe
+Oxz7C8oPiKuHYydQliTQNXp/lJZY2JNSvX1qUvsHmSTtmxXLNSqWw218QHmW
+SU9KUu/Xa3C/Hkr3CZ9igcr3eR3QwgrJL8h+hzo1tQ7VwrSFuxbu4UNS8ZGF
+Gcyug/mYw8fr6PTl84kPvb4mfBg+Mj66fHT7uFU33vZh+siS/h/DktczzQcA
+AA==
+""",
+ """
+androidx/test/Outer$InnerObject.class:
+H4sIAAAAAAAA/4VUS08TURT+7p0+ptMChSIUUBCpykNpQV1BTJRoHCzFCMEo
+q9t2hKHtjM7cEpas/AkuXLpwxULigkQTg7Dzf/g3jOdOR4vgI2nP+c655zXf
+uTNfv3/8DOAmbjGMCKfquXZ1Jy8tX+aXm9LycqbjWN5yecuqyDgYQ3pLbIt8
+XTgb+Z9ejSE2bzu2vM2gjU+spRBFzEAEcYaI3LR9htHif2rPMejSXZGe7Www
+9I5PFNt9Wl6KGCu63kZ+y5JlT9iOnxeO40ohbZdwyZWlZr1OUckTZXV0UuFN
+4W8uuFUrGM/Uto6+PaSRrZdNUafZzo0XTz/T3MQzhty/ulErUa5b1C7qyk3L
+Y+g5W4Vaz1fqATMGuKJDN0srq3dKC/dSGISRIOcQQ3ex5koKyy9ZUlSFFJTI
+G9sa7YUpkVACDKxG/h1bWQVC1RmG54e7wwbPcoOnD3cNriuQDLVuKFe6Uz9+
+ZWQPd2d5gd2N6/zobYyn+WImrQ3yQmRWT0cHI1lWYA+OX2uLiXSMvHHCjLBO
+OKGw6jbL1AyZP+wxjgmG+CrZ0zXJMPS46Ui7YZnOtu3bRNKdNnF0IVqL6Cra
+jlVqNsqWt6qIVPy5FVFfE56t7NDZsSJFpbYkXoR27nTtR8ITDYuG+K1JKrgC
+C3Xh+xaZxorb9CrWfVuVGAhLrJ0ZDjO0j0hA9YBaD+lrZMVId5CO0mk0sK6T
+lVcLUd7JA+j7BDimw2AgQ8dAqhWABJVSRZPk4UHyaJis9XS9D47a4VoYfrIz
+vXXoDvu2U3v2/pJKW0Jv2MkkzUn3T069QzSyN/UF/A2i2t7UIfiTyF4weIFk
+BDyuB8X6WglhMYX66M+IHagrTC8MAR3ZX1T0BwlA8hP40wMMfMD5/cChYZak
+4pFjEp3E6o2g3xR9b9RoDBeInuF1aCZGTFw06ekuEcSYiRwur4P5uIKr6zB8
+9Rv3EfORCUCfj3QAkiR/AL5kiZ3FBAAA
+""",
+ """
+androidx/test/Outer.class:
+H4sIAAAAAAAA/3VRwW7TQBB9u05ixzE0TSlNKE2BFmiKhNuKU6mQSgSSpZBK
+bRQJ5bRJVmUTx5bsTdRjTnwIf1BxqAQSiuDGRyHGbiAHileetzPz5s3u7M9f
+X74BeIFnDCsi6Eeh6l+4WsbaPRlrGZlgDMWBmAjXF8G5e9IdyJ42YTDkjlSg
+9CsGY6fWdpBFzkYGJkNGf1Axw2rjBr2XDNZRz08rbfCEbnnNs9Zxs/7GwS3Y
+eQreZthqhNG5O5C6GwkVxK4IglALrULaN0PdHPs+SS03hqEmMfed1KIvtKAY
+H00MuhFLTD4xYGBDil+oxNujXX+foTabOjYvc5sXZ1ObW4b14yMvz6YHfI8d
+ciPz2rT49085XuRJwQFLZGwvCGRU90VM1yukzvU8GKo33HV7QTexybD5X86f
+qT5kMFuUez4kyfXTcaDVSHrBRMWq68vjxQxoyPWwLxmWGiqQzfGoK6OWIA5D
+qRH2hN8WkUr8edBZHEVSsX0WjqOefKuSXGXep/1PF+zTY2TSCVaStyHcJi9H
+WCTktLKp95g8N5kzYXb3CtZlmn4yJwMlPCXrXBOQJynAQuFv8Rqxk6/wFfz9
+FZzPWLpMAwZ20nKOB/Rv0DkeEVYJa2mLLewSHpLMMgmXOjA8rHi442EVd2mL
+NQ9lVDpgMe5hvYNsDDvG/Ri5GBsxqr8BxLf8XAEDAAA=
+""",
+ """
+androidx/test/OuterComp$InnerClassComp$Companion.class:
+H4sIAAAAAAAA/5VTTW8SURQ9d4YyMGKlVC34/YGVGu0AcVdjohgTEmqT2rDp
+wjzgqQ+GN2bmTdMlK3+I/6ArExeGdOmPMt43oI0LE7u599x77rk3cx78+Pnt
+O4CnaBCaQo/iSI2OAyMTE+ylRsadaPqp3tWaUSiSJCttEFpF2gMRymNxJIJQ
+6A/B3mAsh8aDS8g/U1qZ5wS3sdUvYQV5Hzl4hJz5qBJCu3feYzuEVqM3iUyo
+dDA+mgZKs0SLMHgl34s0NJ1IJyZOhyaKd0U8kfHOVt+HY4+u14dn5LtpxhK2
+z7eNsPZbsCuNGAkjuOdMj1w2kGwo2gACTbh/rGzVZDRqEerzme87Vcd3yozm
+s8LpZ7c6n7WdJr30Cs7pl7xTduxsm+yGzf9zx8N1wsY/Zj3cJKz+LSAU/xhK
+8A5YsD0x/CqdaCQJl3pKyzfpdCDjAzEIuVPpRUMR9kWsbL1sls6WSn5L/22U
+xkP5Wlmutp9qo6ayrxLFwy+0jowwfC5Bix8jZx3i7NifBH/oPa4CaxnnlUdf
+UTjJ6Psc81nzMeocS4sBFOEDZWJ0YSl+wtlZiksnmf1WcHXRXAgydBGrzLl4
+wFWF2Ru4hduoZegO583s8F08zP4O7AVryodwu1jrotLFOi4zxJUu7944BCWo
+osZ8Aj/BtQT5X3gloppLAwAA
+""",
+ """
+androidx/test/OuterComp$InnerClassComp.class:
+H4sIAAAAAAAA/41UXVMURxQ9Pfs1Oywwi18IJJq4McuuukA0MYqJijEMATRi
+iGjy0OyOMLDMkJlZyrykfPInWJW8pCoPeeJBKwmkYlWK6Ft+UyqV0zPjomAs
+qqD73ru3zz197u35+98//gRwGl8LHJduw/ecxr1aaAdh7VortP0xb2W1ZLku
+raYMAuXmIATMJbkma03pLtSuzS/Z9TCHlEB21HGd8COBdNkanBVIlQdnC8gg
+ZyANXUB3FNIlf0FAWAUY6MhDQ4H54aITCJQn90bhvEDHgh1abTQWsgSMOn/z
+XNsNhwlZ91a/FaiSyd5Rj016/kJtyQ7nfem4QU26rhfK0PFoT3vhdKvZPK8u
+lDXI+6BAQRUpNey7stUMBe7u+QKWNblTwfN75lnAPuxXDPooaejNhL7jUoT9
+5cEXQOMo73RoZ+xyy2k2bD+HNw0cUW3pfRm//LxLF3S8xabK1VXbbQicLO+G
+310xQSfJYyipAu8IDKgmvC7xXZVYVoljr0+sqMRqAQN4Q1knKcCiDBbHvIYt
+UNw+abmhvaDuOBQPI6ethhEDw3iPN7K/ackm5+1A+RVduC1Qet0YcAbkfNOm
+shkvXLR9gZ7dKOQ1Wm8mr2Fob30tqUW6rJIDAYbLk8teSIza0tpKzeGFfFc2
+a1fiYRsjl9Bv1UPPn5L+MuWJn9oFA6NgzXwbTGBkj4O1TYBaX8Ql9TgvU9bn
+PKbsUDZkKElOW1lL8bsh1JJXC/iklxm/5yiPqmsNPsL1rftHDa1XMzRz677B
+P83UDU3Pcu/gnuLexbD+9IHey9TuEW1InBPdlzt7sqbWpw2lnv6U1cz0RN7M
+KW/82YPUxD5Tp711f0TXtTiJYcFwnrYxopsdfeleMSTGnz1M8WAhzngoaHfS
+7lL2jWIbXmf9vrSeMbOK84hQNzn0P3rlcF2g62XRBHI3mXRqme+//0bLDZ0V
+23LXnMDhkFzaHhzOYTyl3ZOOa0+3VuZt/6YaJDU/Xl02Z6XvKD8Jds6Esr48
+JVcTv7QT+7r05YpNZi8VKWyzs+kaM17Lr9tXHQVxOIGY3UWO70LjJxpcD6vO
+U4Ob9LLcD3DvUZ9q1Wn6mSj6Bb2rzNa4G5VN5Cv9v6HzcYQwy7ULagwqxKzy
+VAVf0jsYZ/O3bjUwtBQq5wsm/2PMmpoj7pnKr+hcb8Nlo2A1ginECQlMkeSe
+Hz6287B45QF+PAmrDgyTpeKUfwJtrn8Thx61D8Vk822y+YTsC7KYefRSrrj2
+8UTA4kD6u++hKwajlf4N9MeQt7imIBQCP11J+XPcFbWBJzgyt4mjPW9v4Lg6
+uYFBc3ADJzZw6tGOawwkjF5sj6BsxTaPWIOIwe84vVMGPTkvcAbvJzy+4q7a
+VapUf0YmvV79C9oPyKTWq1vQphTQCf7/qCLpuCe3ovalcvo/KObobytWaitW
+wll8yDpztHOK1AdR+XPIJVR7o6Ik9gSjc2ITH/+CscdRJIXb0dSp+focNyjy
+KK2L3O9E5WdIGbQFrrCvn9xBysJVC59aGIdFExMWPsMkEwJMYfoOzADdAa4F
+MKI1G6hIMUBPgH0BzkTBswFqAQYi++J/Kz3doBgJAAA=
+""",
+ """
+androidx/test/OuterComp$InnerObject.class:
+H4sIAAAAAAAA/41US08TURT+7p0+ptMC5SEUUHxQtFClBXVhICZIfAwpxQjB
+KKvbdoSh7QzO3BKWrPQfuHDpwhULiQsSTQzKzp/kwnjuMApCMCbtOd8597zm
+O3fm+89PXwDcwm2GYeHUPNeubRWk5cvCQkta3qzb3MiajmN5C5V1qyrjYAzp
+dbEpCg3hrBZ+ezWG2LTt2PIug5YbXU4hipiBCOIMEblm+wwjpf+oP8WgS3dR
+erazytCTGy0d9Tr0UsRwyfVWC+uWrHjCdvyCcBxXCmm7hMuuLLcaDYpKHiur
+o50Krwl/bdatWcGIpnbn4esfNLb1siUaNN+5XOnkc02NPmfI/qsbtRKVhkXt
+oq5cszyGrtNVqPV0tRGwY4ArSnSzvLg0U569n8IAjAQ5Bxk6S3VXUlhh3pKi
+JqSgRN7c1Gg/TImEEmBgdfJv2coqEqpNMLzY3x4yeIYbPL2/bXBdgWSodUO5
+0u36wSsjs789yYvsXlzn397FeJrPdae1AV6MTOrp6EAkw4rs0cEbbS6RjpE3
+TpgR1gknFFbdJpmaoe+MXcYxyhBfIt94XTIMPmk50m5aprNp+zYRNXNEHl2M
+w2V0lGzHKreaFctbUmQqDt2qaCwLz1Z26GxblKJanxcboZ09Wfux8ETTokH+
+apIKrsFsQ/i+Raax6La8qvXAViX6wxLLp4bDBO0kEtDdr1ZE+jpZMdJtpKN0
+Gg2sG2QV1FKUd2wP+i4BjvEwGMjRMZA6DECCSqmiSfLwIPlymKx1dXwIjo7C
+tTD8eGd6+9AZ9j1K7do5I5WhGz1hJ5M0J903ln+PaGQn/xX8LaLaTn4f/Glk
+Jxi8SDICHteDYr2HCWExhXrpz4gdqGtMLw0BHZk/VPQFCUDyM/izPfR/xPnd
+wKFhkqTikWMM7cTqzaBfnr49ajSGC0TP0Ao0ExdNXDLp6a4QxLCJLEZWwHxc
+xbUVGL765XzEfHQHoNdHOgBJkr8Ar2VkytEEAAA=
+""",
+ """
+androidx/test/OuterComp.class:
+H4sIAAAAAAAA/31RW2sTQRT+ZjbJbjaxTeMlibX10lqbCm5bfKpFqEVhIaZg
+Q0DyNEmGOslmV3YnoY958of4D4oPBQUJ+uaPEs9so0Wk7rDnO9fvzDnz4+fn
+rwCe4jFDRYT9OFL9U0/LRHtHYy3jw2j03gZjKA3ERHiBCE+8o+5A9rQNiyG3
+r0KlnzNYm/V2EVnkXGRgM2T0O5Uw1BpXcD5jcPZ7QVrtgpsSx28etw6ahy+L
+uAY3T84FhrVGFJ94A6m7sVBh4okwjLTQKiK9GenmOAiIaqkxjDSRea+lFn2h
+Bfn4aGLRZMyIvBFgYEPynypjbZPW32Goz6ZFl1e5y0uzqcsdy/n+gVdn012+
+zfa4lXlhO/zbxxwvcVOwywzNgh+GNEYgksTMwlBIHRd7Ydi4Yub1v8ts3KX5
+/pv7e9P3GewWxZ8MiX75zTjUaiT9cKIS1Q3kweVOaPGHUV8yLDZUKJvjUVfG
+LUE5DOVG1BNBW8TK2HNn8fJKkord42gc9+QrZWK1eZ/2P12wQ4+TSTdaM29F
+uE5WjrBEyOlkU+shWZ7ZO2F26xzOWRremCcDj+gAxYsE5IkKcFD4U1yhbPMV
+voC/PUfxExbPUoeFTZJlCt+jf4Xu8YBwlbCetljDFuEe0SwRcbkDy8d1Hzd8
+3MQtUlHxUUWtA5bgNpY7yCZwE9xJkEuwkmD1F4DlAzIZAwAA
+""",
+ """
+androidx/test/TestAbstract.class:
+H4sIAAAAAAAA/3VRy04CMRQ9t8AgIwriC/AR3Rh14ahxpzFBExMS1EQNG1eF
+mWgFOsm0EJd8i3/gysSFIS79KOPt6NbNyXnctqft1/f7B4AjrBHqUodJrMLn
+wEbGBncMjY6xiezaPIhQfpIjGfSlfgiuO0+RczME70RpZU8Jme2ddhE5eD6y
+yBOy9lEZwmrr/22PCXOtXmz7SgeXkZWhtJI9MRhluBQ5KDgAgXrsPyun9pmF
+B4SNydj3RVX4osxsMp7aqk7Gh2KfznKfL54oCzd3SG513p2617Pc6jwOI0Kp
+pXR0NRx0ouROdvrsVFpxV/bbMlFO/5n+bTxMutGFcqJ2M9RWDaK2MorThtax
+lVbF2mQ3IfjSf03dGzBWWQWpBnK7b5h6ZSJQY/RScx11xuLvAArw03wlxWWs
+pt9CmOaseI9MEzNNzDZRQpkp5pqoYP4eZLCARc4NfIMlA+8H7YuiztMBAAA=
+""",
+ """
+androidx/test/TestAbstractComp$Companion.class:
+H4sIAAAAAAAA/5VSTW/TQBB9u07jxARIWz4SPspXkNJK1E3FrQipBCFZSkGC
+Kpce0MZZYBN7jbzrqMec+CH8g56QOKCoR34UYtYJcENwmZ15b96M962///j6
+DcBjPGToCj3OMzU+Da00NjymcDgyNhex7Wfpx44LQqtM+2AMzYmYiTAR+n34
+ajSRsfXhMVSfKK3sUwavuz1sYA3VABX4DBX7QRmGncG/Ljlg6HUH08wmSoeT
+WRoqbWWuRRI+l+9EkVC7Jl0R2yw/EvlU5gfbwwDcLdvsxH/It2nJMuz+3zSG
+9V+CI2nFWFhBGE9nHhnGXKi7AAY2JfxUuWqPsnGPobOYBwFv8YA3KVvMa+ef
+vNZivs/32DO/xs8/V3mTu9595iZs/d0VHzcZ6r+tYfBdx+7Ukq/9bCwZLg+U
+li+LdCTzYzFKCNkYZLFIhiJXrl6BjUhrmfcTYYyk1wjeZEUeyxfKce3XhbYq
+lUNlFDUfap1ZYWmdQY9srbi70sndo9In36EqdJenc23nC2pnJX2XYrUEe7hH
+sbFsQB0B0GSUXViJH9HJV+LGWWmkE1xbgktBmV3EJeI83KcqKEW3cBttPCgX
+bqFT/sDkAfU2T+BFWI+wEWETVyjF1YhmXj8BM2ihTbxBYHDDoPoTIO6Wpv0C
+AAA=
+""",
+ """
+androidx/test/TestAbstractComp.class:
+H4sIAAAAAAAA/4VRXWsTQRQ9s5vPdWOT+pVYramtMc2D2xRBsEWoEWEhTUFL
+QPI0ScY6yWZWdiahj/kt/oPiQ0FBgo/+KPHuNrYPQn25Z+6dc889c+fX728/
+ADxHg2Gdq2EUyuGpZ4Q23jGFg742ER+YVjj5nAVjKI74jHsBVyfeUX8kBiYL
+myGzL5U0rxjs+nbXRRoZBylkGVLmk9QM1fb10nsMuf1BsBSpX0/eigNXMlRZ
+uAzNenscGur1RrOJJ5URkeKB90Z85NOAGhR1TgcmjA55NBbR3oXBmw4KWGHI
+X4oxNP7j8mrwnosSVvOwcIthsx1GJ95ImH7EpdIeVyo03BBNe53QdKZBQO8r
+/XV5KAwfcsOpZk1mNi2fxSEfBzCwMdVPZZzt0GnYZKgt5q5jlS3HKi7mjpWz
+crXyYl61d60d9pLZr9M/v2SsohWzd1mskY2dPxsbhrV3U2XkRPhqJrXsB+Lg
+yhz9TiscCoaVtlSiM530RXTMicOw2g4HPOjySMb5suj6SomoFXCtBTU778Np
+NBBvZXxXWc7p/jMltUFbSiVPq8RLI9ykLEN4h9AiTCfZFmVevADCdOMcubPk
++smSDDRRo+heEJCHQ5jDjcvmMpIVwv2Owgd2juJX3D5LKjaeUnSIVyDFEhmp
+J9qPsU34gup3SfFeD7aPso+Kj/tYoyMe+HiI9R6YxiNUe0hpOBobGhmN0h/c
+zA/4OwMAAA==
+""",
+ """
+androidx/test/TestClass.class:
+H4sIAAAAAAAA/3VRu04CQRQ9d5BFVpQFX+CrVgsXjZ3GRE1MSFATNTRWA7vR
+gWU2YQZCybf4B1YmFoZY+lHGO6utzcl53Jk5N/P1/f4B4BjbhHWpo2Gqoklo
+Y2PDB4bLRBpTABGCnhzLMJH6Kbzt9OKuLSBH8E6VVvaMkNvda5eQh+djDgXC
+nH1WhlBv/XPnCaHS6qc2UTq8jq2MpJXsicE4x3XIQdEBCNRnf6KcajCLDgk7
+s6nvi5rwRcBsNq3NpkeiQRf5zxdPBMJNHZE7W3APHvQtF7pMo5hQbikd34wG
+nXj4IDsJO9VW2pVJWw6V03+mf5+Oht34SjlRvxtpqwZxWxnF6bnWqZVWpdrg
+EIL3/evp1messQozDeT33zD/ykSgzuhl5hI2GEu/AyjCz/LNDNexlX0HYYGz
+0iNyTSw2sdREGQFTVJqoYvkRZLCCVc4NfIM1A+8HjoCWJ8sBAAA=
+""",
+ """
+androidx/test/TestClassComp$Companion.class:
+H4sIAAAAAAAA/5VSTW/TQBB9s07jxARIWz4SyjepaJGom4pbERIEIUVKQYIq
+lx7QJllgE3uNvJuox5z4IfyDnpA4oKhHfhRi1ilwQ3CZnXlv3oz3rb//+PoN
+wCNsEjalGeWZHh3HTlkXH3LoJNLaTpZ+bPkgjc5MCCLUx3Im40Sa9/GrwVgN
+XYiAUH6sjXZPCMHWdr+GFZQjlBASSu6DtoT7vX/asE9ob/UmmUu0icezNNbG
+qdzIJH6u3slp4jqZsS6fDl2WH8h8ovL97X4E4Tett4Z/yLdpwRJ2/m8aYfWX
+4EA5OZJOMibSWcBWkQ9VH0CgCePH2le7nI3ahNZiHkWiISJR52wxr5x+ChqL
++Z7YpWdhRZx+Lou68L175Cds/MWSEBuE6m9fCKGndyaOHe1kI0W42NNGvZym
+A5UfykHCyFovG8qkL3Pt6zOw1jVG5cVcxe8Qvcmm+VC90J5rvp4ap1PV11Zz
+81NjMicdr7Nos6clf1E+hX9O/t5bXMX+5nyuPPiCyklB3+ZYLsB7uMOxtmxA
+FRFQJ87OnYkf8inOxLWTwkUvuLIEl4IiO48LzAW4y1VUiK7jBpq8wC+8iVbx
+37IH3Fs/QtDFahdrXazjEqe43OWZV49AFg00mbeILK5ZlH8CxwhM7PQCAAA=
+""",
+ """
+androidx/test/TestClassComp.class:
+H4sIAAAAAAAA/31RXWsTQRQ9s5vPdWOT+pVYq9WmNu2D2xRBMEXQiLCQtqAl
+IHmaJGOdZDMrO7Ohj/kt/oPiQ0FBgo/+KPHuNrYPQl7umXvn3HPP3Pn95/tP
+AM+xy7DG1TAK5fDMM0Ib74RCO+Bat8PJlzwYQ3nEp9wLuDr1jvsjMTB52Ay5
+A6mkecVgN3a6LrLIOcggz5Axn6VmWO8s0W0xFA4GwUJhawmzngSuZKjycBma
+jc44NNTojaYTTyojIsUD7634xOPAtEOlTRQPTBgd8mgsotaltZsOSlhhKF6J
+MWwv83c9teWigtUiLNxi2OyE0ak3EqYfcam0x5UKDTdE095RaI7iIKCXVf5Z
+PBSGD7nhVLMmU5sWzpJQTAIY2JjqZzLJ9ug0bDLU5zPXsaqWY5XnM8cqWNX5
+bMPet/bYS2a/yf76mrPKVsLdZ4lCPjH9bGzoE9/HysiJ8NVUatkPxOtra/Ql
+7XAoGFY6UomjeNIX0QknDsNqJxzwoMsjmeSLousrJaJ0F4KanQ9hHA3EO5nc
+1RZzuv9NQZN2lEkfVktWRrhJWY7wDqFFmE2zOmVe8nzC7O4FCufp9daCDGp7
+StG9JKAIh7CAG1fNVaQLhPsDpY/sAuVvuH2eVmxsU3SIVyLFChlppNpPsEP4
+gup3SfFeD7aPqo+aj/tYoyMe+FjHwx6YxiNs9JDRcDQea+Q0Kn8BDq7wpy0D
+AAA=
+""",
+ """
+androidx/test/TestClassWithArg.class:
+H4sIAAAAAAAA/31QTWsTURQ9781nxsRM4leaaq3aRZtFJy3ulGIMCANRoZZ0
+kdVLZkhfM5mBeS+ly/wW124ERXAhwaU/SrwvDa5EeJx7z32Hcz9+/f7+A8Bz
+7DHsiDwpC5lcRzpVOjoj6GdCqXOpL3rl1ANjCC/FlYgykU+j9+PLdKI9WAzu
+S5lLfcJg78cHQwZr/2BYhQMvgA2fuCinDCyuIsCtCjiqJNUXUjHsDv7f9QW5
+T1PdMwZkGzM0BrNCZzKP3qZaJEILkvD5lUVrMAMVA6B2M6pfS8O6lCVHDP3V
+shnwFg94uFoG9HjoB9y3WqvlMe+y17WmG/I271o/P7o8tE8bf5lP6rbtO6Fr
+rI6ZaeCZWQ9nmnbpF0nKUB/IPH23mI/T8kyMM6o0B8VEZENRSsM3xeBDsSgn
+6RtpyNbpItdyng6lkvTby/NCCy2LXOGIDmWvV2mau1HGKXfgEj4mdkKcUww6
+31DpbH9F7fNas0toNECIJ4T3b1S4jbq5DGXGjQ5J/42NV2QORtHpfEHt0z9t
+qjeCjQ3H0zXu4BnFV+shHdwZwYpxN8a9mNo+oBStGFtoj8AUtvFwBE+hrvBI
+IVijqxAqNP4AZEwrjogCAAA=
+""",
+ """
+androidx/test/TestClassWithArgComp$Companion.class:
+H4sIAAAAAAAA/5VSTW8TMRB99qbZZAmQtnwkfLdNpRZBt6m4FSGVIKRIKUhQ
+hUMPyElM62TXi2wn6jEnfgj/oCckDijqkR+FGG8CHFEv45n35s14n/fnr+8/
+ADzDJsMToQcmU4Oz2Enr4iMKrURY+0G50wNz0srSzw0fhFaZDsEYqkMxEXEi
+9En8tjeUfRciYCg+V1q5FwzB1na3giUUIxQQMhTcqbIMO53LLNpnaG51RplL
+lI6HkzRW2kmjRRK/kp/EOHGtTFtnxn2XmUNhRtLsb3cjcL9wtdH/R35Mc9bv
+v9Q0huU/gkPpxEA4QRhPJwEZx3wo+wAGNiL8TPlql7JBk6Exm0YRr/GIVymb
+TUsXX4LabLrHd9nLsMQvvhZ5lfvePeYnrP/fmRB3Gcp/7WEIfdfOyJG/rWwg
+Ga53lJZvxmlPmiPRSwhZ6WR9kXSFUb5egJW21tLk4yW9SvQ+G5u+fK08V383
+1k6lsqusouYDrTMnHK2zaJK1Bf+9dHL/uHTth1TF3gA6lx5/Q+k8px9RLObg
+JtYoVuYNKCMCqoyyKwvxUzr5Qlw5z830gltzcC7Is6u4RlyAdaqiXHQP91HH
+Rr7wARr5z0weUG/1GEEby22stLGKG5TiZptm3j4Gs6ihTrxFZHHHovgbuLwQ
+4QkDAAA=
+""",
+ """
+androidx/test/TestClassWithArgComp.class:
+H4sIAAAAAAAA/41SW08TQRT+ZnvZ7VpkqYgFvCAXLRXZQnwSQoI1xk1KTZDU
+GJ6m7Vi23c6a3WnDI7/FZ1+IGhJNDPHRH2U8s63woAkmu+fMOXPO953L/Pz1
+9TuAJ9hgWOSyHYV++9hVIlbuAYlqwOP4ja+OdqNONey/N8EYnC4fcjfgsuO+
+anZFS5lIMWS3femrHYZ0yVttMKRKq408MjBtpGGRzaMOA/PysHEtBwN5ClVH
+fsywXLuaeYsYOkLtahCC9his7VYwply7On9ZCy79UJq4wbBRqvVCRflud9h3
+falEJHngPhfv+CBQ1VDGKhq0VBjt8agnoq1RLzdtTGOGIXcBxrD+H8Vfkm/l
+UcSsbn+OYakWRh23K1Qz4r6MXS5lqLiisNith6o+CAJqe+pPpXtC8TZXnHxG
+f5iitTEtclqARtsj/7GvrQqd2rTRl+cnBdsoGrbhnJ/Y9BmOZRtWunh+smBu
+GhX2lJnPJgpZx5gzKqkfH7KGk96furAsSplLWxknq/E2mWYxdX/rPcUwvz+Q
+yu8LTw792G8GYveyfFptNWwLhsmaL0V90G+K6IBTDEOhFrZ40OCRr+2xM+9J
+KaJkbIKS7dfhIGqJF76+mx3zNP5iwQbNMU39GpjVY6XyymRlSd8mXdAvjXSK
+7EzifUTWDkUbpO3yGXLl+S+YOE0Q1saZwAoek5wZReE6JvV86aTRaB1w6B9h
+uXrspDPlz5j4+E+Y/ChgDGNRUeY4uYhkcch/w/RbdoZbnzB/mnhSWE8IGb02
+I2nMTbBXUSFdJf8dQrx7iJSHex4WPNzHIh2x5GEZK4dgMR7g4SGsGJMxSjHs
+RGZjODGmYhR/A+R3KVn3AwAA
+""",
+ """
+androidx/test/TestGraph.class:
+H4sIAAAAAAAA/3VSTW/TQBB9s/mw4waalo8klO/2UDjgtuJGhVQqQJaMkWgU
+qeppE6/aTRwb2Zuox5z4IfyDikMlkFAEN34UYtZEcEB4pTfzZt887Yz84+fn
+rwCeYovQlmmcZzo+940qjN9jeJ3L92cOiNAayZn0E5me+m8HIzU0DiqE+r5O
+tXlOqGw/6jdRQ91DFQ6has50QeiG//F8RnD3h0nZ7UHYFjeIjnoH0eHLJq7A
+a3DxKmEzzPJTf6TMIJc6LXyZppmRRmecR5mJpknCVmvhODNs5r9RRsbSSK6J
+yazCk5GFhgUQaMz1c23ZDmfxLmFrMfc80RGeaHG2mLvfP4jOYr4nduiF44pv
+H+uiJax2j6yDYyd4MjaEjXfT1OiJCtKZLvQgUQd/n8bzH2axIqyGOlXRdDJQ
+eU+yhrAeZkOZ9GWuLV8WvaNsmg/VK21Jd2nc/8cWu7yUajlJ1+6I411mdY4t
+joJPrWT3mPl2Xo61x5dwL8rr+0sxuPUBY/O3AA3mgIuVP81tVttv5QvE8SWa
+n7B6URYEHpZ4B5vlb8O7Z4P1E1QCXAtwPcAN3OQU7QAddE9ABW5hg+8LeAVu
+F6j/Ai/P7yRzAgAA
+""",
+ """
+androidx/test/TestInterface.class:
+H4sIAAAAAAAA/32Oz0rDQBDGv9lo08Z/qVqoiK9g2tKbJy9CoCKoeMlpm2xl
+m3QD2Wnpsc/lQXr2ocSJ3p2Bb76Zgd/M1/fHJ4ApBoRr7YqmtsU2YeM5eRVJ
+HZtmoXMTggjxUm90Umn3njzNlybnEAGhPytrrqxLHg3rQrO+I6jVJhAstdJr
+BQQqZb61bTcSV4wJg/2uG6mhilQsbjHc7yZqRO1yQriZ/fOP3BBk2M5uSyZE
+L/W6yc2DrQzh6nnt2K7Mm/V2Xpl752rWbGvnO8LGAf5C4eJXz3EpdSy8Q8lO
+hiBFmKKboodILI5SHOMkA3mc4iyD8og9+j/Vk+x/PAEAAA==
+""",
+ """
+androidx/test/TestKt.class:
+H4sIAAAAAAAA/4VUbU/TUBR+bjvWrgzW8b6BiLzo5gsFfBc0ISQmjRMSJBhC
+YtJt11kYbdJ7R/jIb/EXqHwgkcQQP/qjjOc2xGkLug/3nvvc53l6zunpfvz8
++g3AAzxnGPKCZhT6zSNHciGdLVpeSQOMwd7zDj2n7QUtZ6O+xxuE6gyDLS7X
+2p4QbiCkFzT4Jn/PMF6p1tJGMW+ZYaYWRi1nj8t65PmBcLwgCKUn/ZDi9VCu
+d9ptYtmNlG3pStM8TORy0GAxlJMpvfXlh9WoFVtMX53ZBY0ePdq4Sj71P3Ee
+/SioRGyGMUrEDQIepRuUTmOjI3k026VTGsP+5eJkEilpHkMYVkmMMJgrjbYf
++PIFg16pblNxV1RgoMyQXYm5eUygZGEc1xgm/12xgesMmYpb3VaiGxamMJ0S
+JTM0MGthThGLtf1QUoLOay69pic9qls7ONRpHplacmoBA9tXgUaXR76KFihq
+LjK8Oz8uW+fHljamWZqpJ3ZtumgTQVtg3z9mTeKVM6Zm64RmCOzpglnbINAk
+MNcFLbtXPWWJpvyScgzcZ7C6NTEYqjfz+5Lmf7MTSP+Au8GhL/x6m692R5y6
+tRY2OUOh5gd8vXNQ59GWRxyGfNeNE896E3aiBn/pq7vSheV2yhCL9KYz1BMd
+ZfUZULce0ylLu0F7WU1kCqMBSWAZlNBDJw1P6DRBqPplvqD3U/wGnl5wFfNP
+XQl59KVVxaQqm1ANYDCtGk2qzL9UJsZIyWLVGuLRwMwZxndOMXmC3jNM7diF
+U8ycoHiGuTi+eYLRz79N+2NRBhZZjpCdjmd0tuh2jv7/HpL5shozPMIK7RuE
+36KmVHahu6i6uO3iDu66uId5Fw4WdsFU+5d2kRcwBXICPQJZgX6BglBgn8CQ
+wLDAgMDgL1Y9gwBpBQAA
+""",
+ """
+androidx/test/TestObject.class:
+H4sIAAAAAAAA/3VSTWvbQBB9s7ZlWXEbN/2InfQrH4ekhyoJvTUUktCCQFWh
+MYaQ09pa0rVlCaS1ydGn/pD+g9BDoIVi2lt/VOmscNpDqRbezHs789gZ9PPX
+l28AXmCb0JZpnGc6vvSNKozfZXjXH6qBqYMIraGcSj+R6YV/o1YIzqFOtXlF
+qOzs9pqowfFQRZ1QNR90QVgL/2f6kuAeDpKy3YOwPW4QnXaPopPXTdyC12Dx
+NmErzPILf6hMP5c6LXyZppmRRmecR5mJJknCVnfCUWbYzH+rjIylkayJ8bTC
+s5GFhgUQaMT6pbZsj7N4n7A9n3meaAtPtDibz9wfH0V7PjsQe3Rcd8X3T45o
+CVt7QNahbkd4PjKE9feT1OixCtKpLnQ/UUd/n8YLOMliRVgOdaqiybiv8q7k
+GsJKmA1k0pO5tnwheqfZJB+oN9qSzsK4948t9nkp1XKSjt0Rx8fMHI4tjoJP
+rWRPmPl2Xo61Z9dwr8rrp4tioImNEssCNNgKcLH0p3mVq+239BXi7BrNz1i+
+KgWBzRIfYav8cXj3bLByjkqAuwHuBbiPB5xiNUAbnXNQgTWs830Br8DDAs5v
+jU1b0HUCAAA=
"""
+ )
+ .bytecode
diff --git a/navigation/navigation-runtime-lint/src/test/java/androidx/navigation/runtime/lint/WrongNavigateRouteDetectorTest.kt b/navigation/navigation-runtime-lint/src/test/java/androidx/navigation/runtime/lint/WrongNavigateRouteDetectorTest.kt
index 4af2ebc..ce9918d 100644
--- a/navigation/navigation-runtime-lint/src/test/java/androidx/navigation/runtime/lint/WrongNavigateRouteDetectorTest.kt
+++ b/navigation/navigation-runtime-lint/src/test/java/androidx/navigation/runtime/lint/WrongNavigateRouteDetectorTest.kt
@@ -17,28 +17,21 @@
package androidx.navigation.runtime.lint
import com.android.tools.lint.checks.infrastructure.LintDetectorTest
-import com.android.tools.lint.checks.infrastructure.LintDetectorTest.compiled
-import com.android.tools.lint.checks.infrastructure.LintDetectorTest.kotlin
-import com.android.tools.lint.checks.infrastructure.TestFile
import com.android.tools.lint.checks.infrastructure.TestMode
import com.android.tools.lint.detector.api.Detector
import com.android.tools.lint.detector.api.Issue
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
+import org.junit.runners.JUnit4
-@RunWith(Parameterized::class)
-class WrongNavigateRouteDetectorTest(private val testFile: TestFile) : LintDetectorTest() {
+@RunWith(JUnit4::class)
+class WrongNavigateRouteDetectorTest : LintDetectorTest() {
override fun getDetector(): Detector = WrongNavigateRouteDetector()
override fun getIssues(): MutableList<Issue> =
mutableListOf(WrongNavigateRouteDetector.WrongNavigateRouteType)
- private companion object {
- @JvmStatic @Parameterized.Parameters public fun data() = listOf(SOURCECODE, BYTECODE)
- }
-
@Test
fun testEmptyConstructorNoError() {
lint()
@@ -47,7 +40,8 @@
"""
package com.example
- import androidx.navigation.*
+ import androidx.navigation.NavController
+ import androidx.test.*
fun createGraph() {
val navController = NavController()
@@ -57,7 +51,8 @@
"""
)
.indented(),
- testFile,
+ *NAVIGATION_STUBS,
+ TEST_CODE
)
.skipTestModes(TestMode.FULLY_QUALIFIED)
.run()
@@ -72,7 +67,8 @@
"""
package com.example
- import androidx.navigation.*
+ import androidx.navigation.NavController
+ import androidx.test.*
fun createGraph() {
val navController = NavController()
@@ -98,7 +94,8 @@
"""
)
.indented(),
- testFile,
+ *NAVIGATION_STUBS,
+ TEST_CODE
)
.skipTestModes(TestMode.FULLY_QUALIFIED)
.run()
@@ -113,7 +110,8 @@
"""
package com.example
- import androidx.navigation.*
+ import androidx.navigation.NavController
+ import androidx.test.*
fun createGraph() {
val navController = NavController()
@@ -150,91 +148,92 @@
"""
)
.indented(),
- testFile,
+ *NAVIGATION_STUBS,
+ TEST_CODE
)
.skipTestModes(TestMode.FULLY_QUALIFIED)
.run()
.expect(
"""
-src/com/example/test.kt:7: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
+src/com/example/test.kt:8: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
navController.navigate(route = TestClass)
~~~~~~~~~
-src/com/example/test.kt:8: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
+src/com/example/test.kt:9: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
navController.navigate(route = TestClass::class)
~~~~~~~~~~~~~~~~
-src/com/example/test.kt:9: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
+src/com/example/test.kt:10: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
navController.navigate(route = TestClassWithArg)
~~~~~~~~~~~~~~~~
-src/com/example/test.kt:10: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
+src/com/example/test.kt:11: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
navController.navigate(route = TestClassWithArg::class)
~~~~~~~~~~~~~~~~~~~~~~~
-src/com/example/test.kt:11: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
+src/com/example/test.kt:12: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
navController.navigate(route = TestInterface)
~~~~~~~~~~~~~
-src/com/example/test.kt:12: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
+src/com/example/test.kt:13: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
navController.navigate(route = TestInterface::class)
~~~~~~~~~~~~~~~~~~~~
-src/com/example/test.kt:13: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
+src/com/example/test.kt:14: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
navController.navigate(route = InterfaceChildClass)
~~~~~~~~~~~~~~~~~~~
-src/com/example/test.kt:14: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
+src/com/example/test.kt:15: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
navController.navigate(route = InterfaceChildClass::class)
~~~~~~~~~~~~~~~~~~~~~~~~~~
-src/com/example/test.kt:15: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
+src/com/example/test.kt:16: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
navController.navigate(route = TestAbstract)
~~~~~~~~~~~~
-src/com/example/test.kt:16: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
+src/com/example/test.kt:17: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
navController.navigate(route = TestAbstract::class)
~~~~~~~~~~~~~~~~~~~
-src/com/example/test.kt:17: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
+src/com/example/test.kt:18: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
navController.navigate(route = AbstractChildClass)
~~~~~~~~~~~~~~~~~~
-src/com/example/test.kt:18: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
+src/com/example/test.kt:19: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
navController.navigate(route = AbstractChildClass::class)
~~~~~~~~~~~~~~~~~~~~~~~~~
-src/com/example/test.kt:19: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
+src/com/example/test.kt:20: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
navController.navigate(route = InterfaceChildClass::class)
~~~~~~~~~~~~~~~~~~~~~~~~~~
-src/com/example/test.kt:20: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
+src/com/example/test.kt:21: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
navController.navigate(route = Outer.InnerClass)
~~~~~~~~~~~~~~~~
-src/com/example/test.kt:21: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
+src/com/example/test.kt:22: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
navController.navigate(route = Outer.InnerClass::class)
~~~~~~~~~~~~~~~~~~~~~~~
-src/com/example/test.kt:24: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
+src/com/example/test.kt:25: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
navController.navigate(route = TestClassComp)
~~~~~~~~~~~~~
-src/com/example/test.kt:25: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
+src/com/example/test.kt:26: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
navController.navigate(route = TestClassComp::class)
~~~~~~~~~~~~~~~~~~~~
-src/com/example/test.kt:26: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
+src/com/example/test.kt:27: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
navController.navigate(route = TestClassWithArgComp)
~~~~~~~~~~~~~~~~~~~~
-src/com/example/test.kt:27: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
+src/com/example/test.kt:28: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
navController.navigate(route = TestClassWithArgComp::class)
~~~~~~~~~~~~~~~~~~~~~~~~~~~
-src/com/example/test.kt:28: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
+src/com/example/test.kt:29: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
navController.navigate(route = OuterComp.InnerClassComp)
~~~~~~~~~~~~~~~~~~~~~~~~
-src/com/example/test.kt:29: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
+src/com/example/test.kt:30: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
navController.navigate(route = OuterComp.InnerClassComp::class)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-src/com/example/test.kt:30: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
+src/com/example/test.kt:31: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
navController.navigate(route = InterfaceChildClassComp)
~~~~~~~~~~~~~~~~~~~~~~~
-src/com/example/test.kt:31: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
+src/com/example/test.kt:32: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
navController.navigate(route = InterfaceChildClassComp::class)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-src/com/example/test.kt:32: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
+src/com/example/test.kt:33: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
navController.navigate(route = AbstractChildClassComp)
~~~~~~~~~~~~~~~~~~~~~~
-src/com/example/test.kt:33: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
+src/com/example/test.kt:34: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
navController.navigate(route = AbstractChildClassComp::class)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-src/com/example/test.kt:34: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
+src/com/example/test.kt:35: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
navController.navigate(route = TestAbstractComp)
~~~~~~~~~~~~~~~~
-src/com/example/test.kt:35: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
+src/com/example/test.kt:36: Error: The route should be a destination class instance or destination object. [WrongNavigateRouteType]
navController.navigate(route = TestAbstractComp::class)
~~~~~~~~~~~~~~~~~~~~~~~
27 errors, 0 warnings
@@ -242,528 +241,3 @@
)
}
}
-
-private val SOURCECODE =
- kotlin(
- """
-
-package androidx.navigation
-
-public open class NavController {
-
- public fun navigate(resId: Int) {}
-
- public fun navigate(route: String) {}
-
- public fun <T : Any> navigate(route: T) {}
-}
-""" +
- TEST_CLASS
- )
- .indented()
-
-private val BYTECODE =
- compiled(
- "libs/StartDestinationLint.jar",
- SOURCECODE,
- 0xb1569275,
- """
- META-INF/main.kotlin_module:
- H4sIAAAAAAAA/2NgYGBmYGBgBGJOBijgUuMSTsxLKcrPTKnQy0ssy0xPLMnM
- zxPi90ssc87PKynKz8lJLfIu4RLl4k7Oz9VLrUjMLchJFWILSS0u8S5RYtBi
- AADcysPxVwAAAA==
- """,
- """
- androidx/navigation/AbstractChildClass.class:
- H4sIAAAAAAAA/41QTW9SQRQ9M+8DeAV54Belamv9CGUhtHGnaaQkJiTYRW1Y
- wGrgvdAJj/eSNwPpkt/i2o2JxsSFIS79UcY7j2pcsHAxZ+65c3LunfPz17fv
- AF7iGcNzEQdpIoPrViyWciq0TOJWZ6x0Kia6eyWjoBsJpXJgDAfbtJeh0n/0
- OVgM7msZS33KYDeGRwMGq3E0KMJBzoONPHGRThnYsAgPOwVwFEmqr6RiaPT/
- b5tXNGUa6o4xIvshQ6U/S3Qk49a7UItAaEESPl9a9E1moGAANHZG/WtpWJuq
- 4JjhbL2qerzGs7Needzf8Xjeqq1XJ7zNzkpV1+d13rZ+fHC5b19U/rI8qet2
- 3vFd43TCcLh1/X8Doq1oCf9cLLtJrNMkisL0xUxTAN0kCBnKfRmH54v5OEwv
- xTiiTrWfTEQ0EKk0/KbpvU8W6SR8Kw3ZvVjEWs7DgVSSXjtxnOhsssIxpWtn
- /66asKniVDtwCQ+InRLndHvNryg0976g9CnTPCY0GqCBQ8J7GxVuoWxipMq4
- Uerw6Wy8WiZdup3mZ5Q+brUpbgQ3NhxPMtzHU7rfZEs6uD2C1cOdHu72aOx9
- KlHrYRf1EZjCHh6MkFMoKzxU8BQeKbgKvkLlN2AWm1jVAgAA
- """,
- """
- androidx/navigation/AbstractChildClassComp$Companion.class:
- H4sIAAAAAAAA/51Sy27TQBQ9Y6dxYgKkLY+E9yNIbSXqpqrYBCGVIKRIaZEo
- yqYLNLGHdhJ7Bo0nUZdZ8SH8QVdILFDUJR+FuOME2AKb+zr33Ds+199/fP0G
- YA9PGPa4SoyWyVmk+FSecCu1ivaHuTU8tt1TmSbdlOd5V2cfW85wRQ0BGEN9
- xKc8Srk6id4MRyK2AXyG8nOppH3B4G9sDmpYQTlECQFDyZ7KnOFZ/38Wdhja
- G/2xtqlU0WiaRVJZYRRPo1fiA5+ktqsVTZjEVpsDbsbCdDYHITy3eL0V/wHf
- ZwXKsP1v0xhWfxEOhOUJt5xqXjb1SUjmTNUZMLAx1c+ky3YoStoMrfksDL2G
- F3p1iuazysUnvzGf7Xo77GVQ8S4+l72653p3mZuw9fcKBbjNUP0tEx3lkE/p
- 9dboNBVme2xJ+K5OBMPVvlTicJINhXnHhylV1vo65umAG+nyZbHWU0qYYoGg
- c4VHemJi8Vo6rPl2oqzMxEDmkpr3ldK2eF2ONmldcgKQ99zV6TvuUxY5Rciv
- bH1B5byAH5AtF8UOHpKtLRpQRQjUGUWXluSn5L0luXZeqOsINxbFBaGILuMK
- YT4eURYWpDu4iyYeFwvvoVX87aQB9daP4few2sNaD+u4RiGu92jmzWOwHA00
- Cc8R5riVo/wTjWwvPCoDAAA=
- """,
- """
- androidx/navigation/AbstractChildClassComp.class:
- H4sIAAAAAAAA/5VSS08TURT+7vQ1HYqUiljABwpiqcgUQlxYQoI1mialCyRN
- hNVtey2XTu+YubcNS36LazdEDYkmhrj0RxnPTCskygIXc849Z77znefPX1+/
- A9jAOkORq3bgy/axq/hAdriRvnK3m9oEvGUqh9JrVzyudcXvvU+BMSxehd8T
- 2lzERMgYQ3JTKmm2GOKF/eUGQ6yw3MgggZSDOGyyedBhYPsZOBhLw0KGoOZQ
- aoaV2vWrKlOmjjDbIRml2GewN1veKPXG9XkWQ8EVAVK4ybBWqHV9Qzzu0aDn
- SmVEoLjnvhTveN+jJhVx9FvGD3Z40BVBedjbLQdTmGZIX5AxPPuPZi6LKGeQ
- x0w4llmGhZofdNwjYZoBl0q7XCnfRDzarfum3vc8GsPkn4p3hOFtbjj5rN4g
- RqtmoUiHAjTyLvmPZWiV6NVeY3h9fpJzrLwVfecnjpUdcyw7nj8/mU+tWyX2
- nKVejOeSWWvWKsV+fEha2fju5IVlU8hs3E5kkyEdHdXSlS3/fSVUHlWTrfMB
- jdMEvueJYLVrGOZ2+8rInqiqgdSy6Ynty37pRip+WzBM1KQS9X6vKYI9ThiG
- XM1vca/BAxnaI2emqpQIogELCnbe+P2gJV7J8N/MKE/jnyxYo8HHaUAWZsI9
- UJ1PyEqSvkM6F54s6RjZici7QtYWoS3STvEM6eLcF4yfRgxPR5FAGaskp4co
- 3MBEuBB6hWy0P2TpG3K54Z5IJ4qfMf7xSprMEDCisamo1Cg4j2jTyHzD1Ft2
- htufMHcaeWJEHCZkdKZW1Fgp4i5Sw0CF/HeJ8d4BYlXcr2K+igd4SE8sVLGI
- RwdgGkt4fABbY0KjoOFoLGskNbIakxr53/A96KpcBAAA
- """,
- """
- androidx/navigation/AbstractChildObject.class:
- H4sIAAAAAAAA/41Sy27TQBQ9M3k5bqChPJpQHqVFgrDAbcWOCilEIFkyRqJR
- JNTV2B610zgzkj2JusyKD+EPKhaVQEIR7PgoxLUJD4kusOV759w5c67uGX/7
- /vEzgCe4z/BA6CQzKjn1tJipI2GV0V4/ym0mYjs4VmnyOjqRsW2AMWxeRB7K
- 3P460ECFob6vtLLPGCoPe6MWaqi7qKLBULXHKmfoBf/Z8ymDsx+npZoLXkg4
- fngw7IeDFy1cgtuk4mWG7cBkR96JtFEmlM49obWxpWruhcaG0zQlqSvB2FgS
- 815JKxJhBdX4ZFYhJ1gRmkUAAxtT/VQVaIdWyS41WMxdl3d4+S3mztd3vLOY
- 7/Ed9rzh8C/v67zNC+oew9aFw/3tEbVth2I2MNpmJk1l9nhsGTbeTLVVE+nr
- mcpVlMr+nyHIuYFJJMNqoLQMp5NIZkNBHIa1wMQiHYlMFXhZdA/MNIvlS1WA
- 7lJ49I8sdsm+ajlzt3CT8h1CdcptypzeWonuEvIKZyjXHp3DOSu3N5dkoId7
- FFs/CWiSFOBg5ffhdWIXz8on8LfnaH3A6llZ4Ngq421slz8k3RIJrB2i4uOq
- j2s+ruMGLbHuo4PuIViOm9ig/Rxujls56j8AJAtPiM0CAAA=
- """,
- """
- androidx/navigation/AbstractChildObjectComp.class:
- H4sIAAAAAAAA/5VSW2sTQRT+ZnLbbKNN66WJVau2FC/otsU3gxCDwsK6gg0B
- 6dNsdmin2czI7iT0MU/+EP9B8aGgIEHf/FHi2TVU0L64y57bfOc7nG/2x8/P
- XwE8xRbDI6Hj1Kj4xNNiqg6FVUZ73SizqRja3pFK4jfRsaTQjN/XwBi2Lmro
- y8yeNxXIEkO1o7SyzxlK9x8MGqig6qKMGkPZHqmM4XHwH7OfMTidYVIwuuA5
- jeOH+/1u2HvZwCW4dSpeZtgMTHroHUsbpULpzBNaG1swZ15obDhJEqJaCUbG
- Epn3WloRCyuoxsfTEqnCclPPDRjYiOonKs92KIp3acB85rq8xYtvPnO+f+Ct
- +WyP77AXNYd/+1jlTZ5D9xi2L1zwb61odDMU057RNjVJItMnI8uw/nairRpL
- X09VpqJEdv8sQgr2TCwZlgOlZTgZRzLtC8IwrAZmKJKBSFWeL4ruvpmkQ/lK
- 5Ul7QTz4hxa7JGG52LudK0r+NmVV8k3ynN5KkW1Q5uXqkK88PINzWhzfWYCB
- Du6SbfwGoE5UgIOl8+Y1QufP0hfwd2dofMLyaVHguFfYW9gsflC6KSJYPUDJ
- xxUfV31cw3UKseajhfYBWIYbWKfzDG6GmxmqvwBXhfge3QIAAA==
- """,
- """
- androidx/navigation/InterfaceChildClass.class:
- H4sIAAAAAAAA/41Qz2sTQRT+ZvJjk21qNqnWNPVXrdomBzct3pRiGxACsUJb
- ckhOk+yaTrOZhZ1J6DF/i2cvgiJ4kODRP0p8k4aCkIMs89775n37vTff7z8/
- fgJ4hT2GPaGCJJbBta/EVA6FkbHyW8qEyUcxCJuXMgqakdDaAWPwrsRU+JFQ
- Q/9D/yocGAcphp1VEhehNrcyDjIM2TdSSXPEkN7v1joMqf1apwAHeRdpuIRF
- MmRg3QIKWM+D4w5RzaXUDLX2f275msYMQ3NslUi/y1Bqj2ITSeW/D40IhBFE
- 4eNpit7PbMjbAJo7ovtraVGDquCA4WQ+K7u8whdnPnO5t+byXKoynx3yBjtZ
- L2c9XuWN1K9PWe6lz0q3KEfsajqX8bJW6ZBhd+X+/1hEa9EW3qmYNmNlkjiK
- wuTlyJAFzTgIGYptqcLTybgfJheiH9FNuR0PRNQRibR4eemex5NkEL6TFmyd
- TZSR47AjtaTusVKxWYzWOCB/0zQwS6dsDad3c6od5Cg+JXREmFN269+xVt/+
- huKXBWeXov0LeEYfsHnDgoeSdZIqq0bGk+7GUsu3BlPO1L+i+HmlTOGGsJTh
- eL6IO3hB+S317lLvXg+pFjZbuN9CBVtUotrCNh70wDQe4lEPjkZJ47FGQeOJ
- Rk6jrLHxF0rwRpzxAgAA
- """,
- """
- androidx/navigation/InterfaceChildClassComp$Companion.class:
- H4sIAAAAAAAA/51Sy27TQBQ9Y6d5mABpyyPhXQhSC6JuKhBIRUgQhGQpLRJU
- 2XSBJva0ncSeQeNJ1GVWfAh/0BUSCxR1yUch7jgB1mVzX+eee8fn+uev7z8A
- PMVDhmdcJUbL5CRUfCKPuJVahZGywhzyWHSPZZp0U57nXZ19bjvDFXVUwBga
- Qz7hYcrVUfh+MBSxrcBnKL+UStpXDP76Rr+OJZQDlFBhKNljmTM87/3Xxh2G
- znpvpG0qVTicZKF0DMXT8K045OPUdrXKrRnHVptdbkbC7Gz0A3hu82o7/gd+
- ygqUYfN80xiW/xB2heUJt5xqXjbxSUrmTM0ZMLAR1U+ky7YoSjoM7dk0CLym
- F3gNimbT6tkXvzmbbntb7E2l6p19LXsNz/VuMzfh8TkkquAmQ+2vTnSWPT6h
- 51uj01SYzZEl6bs6EQyXe1KJvXE2EGafD1KqrPR0zNM+N9Lli2I9UkqYYoGg
- gwUf9djE4p10WOvDWFmZib7MJTW/Vkrb4nk5OiR2ySlA3nN3pw+5S1noJCG/
- 9OgbqqcFfI9suSi+wBrZ+rwBNQRAg1F0YUF+Qt5bkOunhbyOcG1enBOK6CIu
- EebjPmVBQbqF22jhQbHwDtrFD08aUG/jAH6E5QgrEVZxhUJcjWjm9QOwHE20
- CM8R5LiRo/wbyZhkAy0DAAA=
- """,
- """
- androidx/navigation/InterfaceChildClassComp.class:
- H4sIAAAAAAAA/5VSXU8TQRQ9sy3dtizSFsVS/ABBLUXYghiNEBKs0TQpNUHS
- RHiatkPZdjtrdqYNj/wWn30hakg0McRHf5TxTqnwoInhYe+dc/fecz9//vr6
- HcAa1hgWuWyGgdc8ciXvey2uvUC6ZalFeMAbonTo+c2Sz5UqBd33NhhDqs37
- 3PW5bLlv6m3R0DYiDLP/otkVSl9Q2RhhiG140tObDNH83kKNIZJfqDmwkUgi
- iiRhHrYY2J4DB2MJWLhGrvrQUwxLlStUuk6pWkJvGTbKsccQ32j4w9xPrkA0
- bwSX5GHjBsNKvtIJNBG57X7X9UyM5L77Uhzwnq9LgVQ67DV0EG7zsCPC9fPu
- biYxiSxD4oKM4elV2rmsYt1BDtNmMrcY5ipB2HLbQtdD7knlcikDPSBSbjXQ
- 1Z7v0yDSf0reFpo3ueZks7r9CF0AMyJhBGjqHbIfeQYV6dVcYXh9dpxJWllr
- 8J0dJ63UaNKKR7NnxzP2qlVkz5n9YiwTS1k5qxj58SFmpaI76QsUp5BcND6S
- ihm6VVPvf6+EaqNSUlXep2HqMPB9ES53NMP0Tk9qryvKsu8pr+6Lrctm6UZK
- QVMwjFc8Kaq9bl2Eu5x8GDKVoMH9Gg89g4dGpyylCAfTFRScfBv0woZ45Zl/
- U8M8tb+yYIWmHqXqYqSnzBrovUTTipG+QzpjjpZ0hLCNOMllQpvkbZFOFk4x
- Wpj+gvETQhbcYSTwDEWSk+deSCFt9kEvw0brI96JIZdr1kR6pPAZ4x//SeOc
- Owxp4riOxDA4i8Gi4XzD5Dt2iqlPuH0ysESoNZOQDYrIUXOrA+5HeEy6RPa7
- xDizj0gZs2XcK2MO8/TE/TIe4OE+mEIeC/uIK6QVCgqOwqIyMKMwoZD7DYsK
- p/5yBAAA
- """,
- """
- androidx/navigation/InterfaceChildObject.class:
- H4sIAAAAAAAA/41STW/TQBB9u0kTxw00LV8J5asUUOkBtxU3KqQSgWQpGIlG
- kVBPG3tJN3F2JXsT9ZgTP4R/UHGoBBKK4MaPQsyaUA4gga2dmfd25u3O2N++
- f/wM4DEeMGwJnWRGJSeBFlM1EFYZHYTayuytiGX7WKXJq/5QxrYKxtAYiqkI
- UqEHwS+2xLDxN42uzO25ThVLDJV9pZV9ylDaetirowrPRxk1hrI9VjnDdud/
- 7/KEwduP00LOB3caXhgddg+i9vM6VlCvEdlg2OyYbBAMpe1nQuk8EFobW8jm
- QWRsNElTklrtjIwlseCltCIRVhDHx9MSjYg5U3MGDGxE/IlyaIeiZJcOmM98
- nzd5seYz7+s73pzP9vgOe1b1+Jf3Fd7gLnXP3eWfU6JzG5GYto22mUlTmT0a
- WYb11xNt1ViGeqpy1U/lwe8uaHZtk0iGlY7SMpqM+zLrCsphWOuYWKQ9kSmH
- F6R/aCZZLF8oB1oL4d4fstil+ZWp5Qqtlhso+TvUt8Nr5Dm99P0IbRAK3HDI
- L22fwT8ttu8ukoH72CRb/5mAZYpAhRfOi69RtnuWP4G/OcPFD1g9LQiOe4W9
- TRLuZ2W4RAKXj1AKcSXE1ZBKmxSiFeI61o/ActzATdrPUc9xK4f3Az/F2jnp
- AgAA
- """,
- """
- androidx/navigation/NavController.class:
- H4sIAAAAAAAA/41SW08TQRT+Zttut8utrYJQQeWiFFC2EH2xhERJjEtqNdL0
- hadpuynTbmeT3WnDY3+L/8AnjQ+G+OiPMp7ZNlBAo5vsuX/fnDNzfv769h3A
- c+wzrHLZCgPROnckH4g2VyKQTpUPjgKpwsD3vTANxpDt8AF3fC7bzvtGx2uq
- NBIM5oGQQh0yJIpb9WmkYNpIIs2QVGciYliv/JO9zGCNcx7hiu5WnSEVepHb
- YmAuw3yxcnX2iQqFbJd1zXolCNtOx1ONkAsZOVzKQMUHRE41UNW+75c1U9BX
- noUcw4NuoHwhnc6g5wipvFBy33GlZoxEM0rjDh3WPPOa3TH8Aw95z6NChs3J
- JkYXUP5TW9OYx4KNu7jHkL9dcGOaMZGeZvmg9vJ25rBYq8Xp/O0cQ64ynuid
- p3iLK04xozdI0NMyLTJagG6xS/Fzob0SWa09hvBiuGwbi4ZtZC+GtmFpg34r
- Sdqif9ZaWLwY7hsl9jr145NJyeOVbKJglJJrlnUxzKa2KbVvZs2C8XZUkD6e
- HRVQ1CKdmfCpqmTrk2nfdD812qdrS7DbVfT2R0GLVmCuIqRX7fcaXljjDd/T
- wwdN7td5KLQ/Dm587Eslep4rByISFLp8rVdXi8CQORFtyVU/JIh9EvTDpvdG
- aPzSGF8foSdAWIVBW6y/JHVLS01yhzxH9046tf0F1mcyDDwlacbBJJ6RnB4V
- IAObdA5TcUSDX8T1NP5NoBkDF0bJMVBbM5glqSnmKKcpyqR1VXonn/+KxetE
- JqwJovQlUZoolii/q23df3bcWAGJ/2G1/8q6THknrr5/nd1AKZbb2CNdoegK
- XcmDUyRcPHTxyKUbXiMT6y428PgULMITbJ5iKoIdoRjBjLRNxlaEXIRChJnY
- Lf4G70EG2roEAAA=
- """,
- """
- androidx/navigation/NavControllerKt.class:
- H4sIAAAAAAAA/41UW08TQRg9s1va7VJoy70F5Y6tFxbwLmhCmphsrCVBgiEk
- Jtt2rAvLbLIzbXjkt/gLVB5IJDHER3+U8duV2NCC0IedmTPnnD3z9Zv99fv7
- DwCP8JJh1hH1wHfrh5ZwWm7DUa4vrIrTKvlCBb7n8eCNSoAxZPaclmN5jmhY
- G9U9XiNUZxhscFXyHCltIZUjanyTf2SYLhTLl/lucfmXvUovLvtBw9rjqho4
- rpCWI4SvIpq0Kr6qND2PWJlal/nkNdYpGEgmocFkyHfGe++qT+tBIzIqXJfy
- nEwxRmtXmSzczCKFfqTDUBmGMQplC8GD7sJdFWmjqXgw1xZRpGH3covLA3UZ
- pDCE4TDQCIOxVvNc4apXDHqhuM1w679nSiDPEF+LFClMIGdiHLcY5m5SiQQm
- GWIFu7gdSqdNTGHmCmln5gTmTMyH9Gx531cU2XrLlVN3lEP10A5aOjU1Cx/J
- 8AEGth9ONNo8dMPZEs3qywwfzo7y5tmRqY1ppmboHaM2k80QQVtiPz/HDeLl
- Y4aW0QmNEdjTBuOZBIEGgck2aGZ6w7esMOSuPFQCDxnM9smozy9cucV9xTC+
- 2RTKPeC2aLnSrXp8vX1DqIQlv84Z0mVX8ErzoMqDLYc4DKm2LSee+c5vBjX+
- 2g33cueW212GWKZWiFGJdOTD+0PFe0qrOI0JGvNh+3Zh1EEdWAw59NBKwzNa
- TRAa/mLf0Psl+kOen3OBvgu6HFKEdKmynap0h2oAg92q0U5V9oLKwBgpWaQq
- IeoUzJ5ifOcEt4/Re4qpnUz6BLPHyJ5iPpovHGP06z/T/kjUB5PijJC5jhe0
- Nml3nr6pj8l8New6PMEajRuE36GiFHah2yjauGvjHu7beIBFGxaWdsHC8q/s
- IiVhSCQleiTiEv0SaRmCfRJDEsMSAxKDfwAPYTX9vQUAAA==
- """,
- """
- androidx/navigation/Outer$InnerClass.class:
- H4sIAAAAAAAA/41U31MbVRT+7m5+bJYENkBbfkRQiZiEtgvYai20CiiyGEIF
- h7HiyyVZw8Kyi7sbpr44PPVP6Iy+OOM4PvFQZxQcO+Ng++bf5Diem90mNVSG
- meSec8+e853vnnPu/euf3/8AcAPrDHnu1DzXqj3QHX5g1XlguY6+2ghML284
- jukt2Nz3k2AM2g4/4LrNnbq+urVjVoMkZIbErOVYwV2GWMEobjDIheJGGnEk
- VcSgMCiWQJnz6gzMSENFVwoS0uQfbFs+w3j5IgRmGLrqZmC0sCiNwaBW3b19
- 1zGdYIoAq+7+1wxF4nFRzLGy69X1HTPY8rjl+Dp3HDdoevt6xQ0qDdueEYdJ
- qMT5MkNapMjXzC95ww4YtgoXS2QY5c7azVyQYxp96BfZh6iUgbseeJZDx+8v
- FF+ADK10niudtvmGZddML4kRFaOiHf1t7MLzztxR8Bo1ku/vm06N4VrhLPTZ
- bBEyERxDXoC/wZATpT/P8U3hWBCOC+c7loTjRBo5vCK0a3T4be5vL7g1kyHb
- jjScwKyL802GA0gTpmNaxRTeohOZXzW4TTN2qfCS+n9Os39e+6n3fMs2qapx
- N9g2PYbesyhEprzrBrbl6CtmwGs84GST9g5kul9MLCmxgIZ/l+wPLLEjrlKN
- BvbH08MRVRqQVEk7PVTpJ2mKKikJkl0kZZLdytOHysDp4bQ0yeYzvQlNGpIm
- 5ac/JCQttpzSkmK39OyhvNynKaSTo6JIoROZGZlTpKvTitY1FBtgk2zp2SOZ
- AtOhxyNGeob0bqGvZVvwCtEZiilxLSG4TjNxgsH/HdgkFukutieL3ooKP1hw
- ncBzbdv0ru/SZYmFzespW45Zaextmd6nor6irG6V2xvcs8Q+Mg6vNZzA2jMN
- 58DyLTLNtXvDkFkPeHV3he9H3vlO73vc43smUftPWLpN0aStuu42vKq5aAmI
- wQhi40w6GiaJ3jJRgl7xfpGmkE6vAq3LtFuk7xJJtXSCVGn4V2R+pp2Ej2nt
- huj4CMWPIkWyTLvLoTd96xGzQZpApVGCRv8QUxcjQzJe+gWZoxZcomkcbcKk
- Q4cIJkvkngePdQazlwbQy0KwImCKWApOqSeQ7g+f4MrjVlBINtUim4rIrkRs
- LgFaCgMYjHKPR8XK5mLffAtFMJgtDR9jOISs0CqDCQS621H62yQFtdwTjN4/
- wau9rx9jXEQeo6gVj3H1GNcfdxwjFzF6gQeteqsG41ENmgx+w43OMihRPMNN
- vB3x+IKkaFe+NPET4rGjiT8hfYe4fDRxCmlFAF2l//fCEgt7Umm2T04qfyOb
- pH27YvlWxfK4hXcpzyrpSUHqnWYN7jVD6XrhIyxR+T5pAhpYI/kZ2W9Tp2Y2
- IRuYNXDHwF28RyreNzCH+U0wHwv4YBM9vvh96ENtrgkfmo+sj14ffT5uNo23
- fOg+cqT/CwA97z/6BwAA
- """,
- """
- androidx/navigation/Outer$InnerObject.class:
- H4sIAAAAAAAA/41US08TURT+7p0+plMe5SFvnxR5KVMQVxATJBqHlGKEYJTV
- bTvCwDCjM7cNS1bu3Lpw6cIVC4kLEk0MStz4o4jnTkdBCMakvec7Z84535nv
- 3Pbn8eevAKZxl2FIeNXAd6o7pifqzrqQju+ZSzVpB3nL8+xgqbxpV2QajCG3
- KerCdIW3bv6OagypWcdz5D0GbWR0tQlJpAwkkGZIyA0nZBgu/hfDDIMu/WUZ
- ON46Q+fIaPGErRGljMGiH6ybm7YsB8LxQlN4ni+jhqFZ8mWp5rqUlT3VVkcL
- Nd4Q4ca8X7WjIS3tx/TxGxrcflUTLk14aaR49s1mRp8z5P/FRlSi7NpEl/Tl
- hh0wtJ/vQtSzFTfSxwBXouhWaXllrjT/oAl9MDIU7GdoK275ktLMRVuKqpCC
- Cvl2XaMdMXVk1AEGtkXxHUd5BULVSYYXh7tXDN7DDZ473DW4rkA2trqhQrkW
- /ei10XO4O8UL7H5a59/fp3iOL3TktD5eSEzpuWRfoocV2KOjt9pCJpeiaJow
- I6wTziis2KaYmqH3wm2mMUp3pCTq874nA9917WBiSzL0P6l50tm2La/uhA5p
- NneiI92Sxl5ai45nl2rbZTtYUboqOf2KcFdF4Cg/DjYvS1HZWhQvYz9/tvdj
- EYhtm6b5i6QpuhHzrghDm1xj2a8FFfuho1r0xi1Wzw2HSVpPIlK+V22L7C3y
- UmSbySbpaTLybpNnqv2o6NgB9H0CHBNxMjBAj4GmRgIy1Eo1zVKER8XX42Kt
- vfVj9OgkXYvTTzOTzGiLeU9K2/cuKGXoQGfMZJHlZLvHxj8gmdgb/wb+Dklt
- b/wQ/GliLxq8QGcCPK1HzboaBXEzhbroy0gdqBtNvx8COnr+SNEdFQDZL+DP
- DtD7CQP7UUDDFJ1KR44xtJCqdyK+cforUqMxXCZ5rqxBs3DVwjWL3u4GQQxa
- yGNoDSzETQyvwQjVZyREKkRHBLpC5CKQpfMXu5HXfeAEAAA=
- """,
- """
- androidx/navigation/Outer.class:
- H4sIAAAAAAAA/4VRW2sTQRg9M5vLZhNtGi9NjK2XptpUcNviUy1CDQoLMYW2
- BCRPk2SIk2xmYXcS+pgnf4j/oPhQUJCgb/4o8dttNA9S3GG/M9/tfDNnfv76
- 8g3ACzxjqAjdDwPVP3e1mKqBMCrQ7vHEyDALxlAciqlwfaEH7nF3KHsmC4sh
- c6i0Mq8YrO16u4A0Mg5SyDKkzAcVMVSb17K+ZLAPe37S74DHTbbXOj07ajXe
- FHADTo6CNxk2m0E4cIfSdEOhdOQKrQOT8ERuKzCtie8T1WpzFBgic99JI/rC
- CIrx8dSi27HY5GIDBjai+LmKvV3a9fcY6vNZweFl7vDifOZw27J/fOTl+Wyf
- 77IDbqVeZ23+/VOGF3ncsM9iGsfTWoYNX0R0yXziXKnCULv2xrVlUxYPGLb+
- U/lH50ekfktMG4E2YeD7Mnw+ojnVk4k2aiw9PVWR6vryaCkM6d8I+pJhpam0
- bE3GXRmeCaphKDWDnvDbIlSxvwgWlieT1OycBpOwJ9+qOFdZzGn/MwV79EKp
- RNZK/GCENfIyhEVCTiudeFvkubH4hOmdS9gXSfrJohio4inZwlUBckQF2Mj/
- bV6j6vjLfwV/f4nCZ6xcJAEL22RLlH5I/zqd4zHhBmE9GbGJHcIDolkl4lIH
- lodbHm57uIO7tMWahzIqHbAI91DtIB3BiXA/QibCeoSN33FlSFUiAwAA
- """,
- """
- androidx/navigation/OuterComp$InnerClassComp$Companion.class:
- H4sIAAAAAAAA/51TTW/TQBB9a6d2YkJJUz4SoHwGSBHUTQUIqQgJgpAipa0E
- KJce0CZZyib2Gq3XUY858UP4Bz0hcUBRj/woxKwTqLgglcvMm3nzZr0z3h8/
- v30H8AhNhidcDXUih4eh4hN5wI1MVLiXGaHbSfyp0VGKUMTTNA+t4YpKfDCG
- yohPeBhxdRDu9UdiYHy4DN4zqaR5zuA213tlLMELUIDPUDAfZcrwtPt/R24z
- tJrdcWIiqcLRJA6lIoniUfhKfOBZZNqJSo3OBibRO1yPhd5e7wVw7NGrjcEJ
- +T7OWYaN03VjWPkt2BGGD7nhlHPiiUvDZNaUrAEDG1P+UNpok9CwxdCYTYPA
- qTmBUyE0mxaPP7u12XTL2WQv/aJz/MVzKo6t3WK2w4PTzMjHFYa1fyp8rDEs
- /y1jKP0ZLi1zl0/ozkYnUST0xtjQwtrJUDCc60oldrO4L/Q73o8oU+0mAx71
- uJY2XiTLJ90FrTl4m2R6IF5Ly9XfZMrIWPRkKqn4hVKJyb8wRYs2VLBjI+/Y
- v4Vuf4ui0M6R/NL9ryge5fRtsl6efIwG2fK8ACUEQIUROrMQPyTvLMTlo3wn
- VnBxnpwLcnQWy8S5uENRldiruIbrqOfoBvm7+cE3cS9/LzQL0lT24Xaw0kG1
- g1WcJ4gLHep9aR8sRQ114lMEKS6n8H4BDr3L2mwDAAA=
- """,
- """
- androidx/navigation/OuterComp$InnerClassComp.class:
- H4sIAAAAAAAA/5VUW0/cVhD+jvfmNQt4IRcCmyYt23S5BHNLSoE0XFKK6QIp
- pDSE9uGw64LB2NT2ovSlylN+QqT2pVIf+sRDorZQFamiyVt/U1V1jm2WW4S0
- 0u45M+OZb74zM+f889+ffwEYxNcM3dwuu45ZfqrZfMdc477p2Np8xTfcSWdr
- O6/bNkkW9zyhpsAY1A2+wzWL22va/OqGUfJTiDEkR03b9D9miBf0jiWGWKFj
- KYMEUgrikBlkUyCNu2sMTM9AQV0aEjLk76+bHkNPsRYiIwx1a4avVzEpnc6g
- lOibYxu230fAJWf7O4Y+4lMrdnvRcde0DcNfdblpexq3bccPojxtzvHnKpY1
- Ig6XVOgMVxgyIlW+bHzDK5bP4BZqS6jrxbM1HamRcwbNuCTYtFKpfWfRd02b
- ynKp0HECOrTS+a6etU1UTKtsuCm8o+CGaFfLafzCUffuyXiXms23tw27zHC7
- cB7+fMYInUi2Iy8SvM+QE225yPED4VgQjpMXO3YKx64McrgupNtUgHXurU86
- ZYMhexyp276xJs7YGw4pTaGGfgV9GKATGd9WuEVzeLnwll48YchfNBI0D3zV
- MqiyCcdfN1yGpvMoxGu0ZEW35G4t3c2LhdvkksKImOjipuMTkraxs6WZdCzX
- 5pb2IBy/SWLku5WS77iz3N2kIoUX8Z6CUVDmdBWMYaimITumQXUfw7i4wBNU
- 4iM2s4bPy9znRFHa2onRC8PEkhYL6Npvkv2pKTTqgFSmK7p7+OymIrVIiqQe
- PlPoJ6myIslJ2utoj9HeQGb59XO5hVwb+6VeNswaJ+qbkqrUKvXGXv+clNT4
- TFpNCW36zfPYTLMqk3z4rF+WpdCJzIzMaZKVflmta423sF42/eZFjAIzoccL
- RnI9yQ1CXshW4WXK3xqXE2pScO5n4iTXL6xaCg8ZGk6Xjl7NOb5DrfFdx7IM
- t2eTnom2hYrtm1uGbu+YnknzM348UzSi4QA3Fk3bmKtsrRruIzFjYrScEreW
- uGsKPTLWL/q8tDnLtyM9fxb7IXf5lkEUTyXJHNM0SFUWnYpbMqZMAXEtglg6
- R46ujESvOmi9JgaBSvKItCTtl2lvEq+7aDzpicD6BWlT5C3RrnTuI93Z9jvq
- XwUIS7Q2QEzFAGEOUtQAviTtSuhN3xrF/JAkUGncoNI/xNTEWNGe6PwN9btV
- uGRgHAxgMqFDBJMlckfB7WeD2VsD6F0lWBHQRywFp/QBpOW2fVx9WQ0Kyaar
- ZNMR2RNlUdNooXKFuW9FBczm4t//AFkwGO1s20NbCPmY1hiYQKBXLUo/TLug
- ljvAjeV93Gx6bw+3ROQeOtSOPXTvoeflmWPkIkYn28OobNkqj7AGAYM/MHi2
- DHIUz3AHdyMeX9Eu2pXv7PoFifhu19+QfkQittt1CGlWAHXT/ydhiYc9eRy0
- L5aS/0U2RfpxxfLViuUxhI8ozzLJKUHqwyD9MFIR1ZYgKRE7wOgy28f9XzH5
- KrDE8CSYOjFfn2OBijxK0hjtK0H6RaIMkhkeUF8/WUFMx5SOT3VMQycRMzo+
- Q5EcPMxibgWqh0YP8x6UYE16wpL10OSh2cOdwDjkQfOQC+Sx/wHugknKUQkA
- AA==
- """,
- """
- androidx/navigation/OuterComp$InnerObject.class:
- H4sIAAAAAAAA/41US08TURT+7p0+plMe5SFPxQeolCpTUFcQE2w0DinFCMEo
- q0s7wsB0BmduG5as/AkuXLrQDQuJCxJNTJWdP8p47jAKQiQm7T2POef7znzn
- tj9+fv4K4C7uMeSFVwt8p7ZjeqLprAvp+J652JB2UPLr22OW59nB4tqmXZVp
- MIbcpmgK0xXeuvk7qzGkZh3PkfcZtPH8ShuSSBlIIM2QkBtOyFAo/zfLDIMu
- /SUZON46Q+94vnzMeJSlitGyH6ybm7ZcC4TjhabwPF9GoKFZ8WWl4bpUlT0B
- q6ODgDdEuFHya3Y0qKV5H4qzNLz9qiFcmvLCePn0283kXzCMncdGVGLNtYku
- 6csNO2DoPotC1LNVN9LIAFfC6FZlaXmuUnrYhiEYGUoOM3SVt3xJZeaCLUVN
- SEGNvN7UaFdMHRl1gIFtUX7HUVGRvNoUw8vW7ojBB7jBc61dg+vKycZWN1Qq
- 16EfvjYGWrvTvMgepHX+/V2K5/h8T04b4sXEtJ5LDiUGWJE9PnyjzWdyKcqm
- yWfk6+RnlK/Yppma4dK5G00jT3elIpol35OB77p2MLklGYafNjzp1G3Lazqh
- Q7rNHWtJt+VoN51lx7MrjfqaHSwrbZWkflW4KyJwVBwn25ekqG4tiO04HjuN
- /UQEom7TRH+RtEW3ouSKMLQpNJb8RlC1HzkKYjCGWDkzHKZoRYlI/UG1MbK3
- KEqRbSebpKfJKLpNkal2pLITB9D3yeGYjItBQCadbUcFyBCUAs1ShkfNV+Nm
- rbvzY/TouFyLy08yk8zoinmPW7v3/tHK0IPemMkiy8n2TxTeI5nYK3wDf4uk
- tldogT9L7EWDF+lMgKf1CKzvqCEGU14ffRm9FNStpt8QOToG/kjRHzUA2S/g
- zw8w+AkX96OEhmk6lY4cE+ggVe9EfAX6W1Kj0Q0jeUZWoVm4bOGKRW93jVyM
- WhjD9VWwEDdwcxVGqD7jIVIheiKnL0QucrJ0/gKE9w8Q7AQAAA==
- """,
- """
- androidx/navigation/OuterComp.class:
- H4sIAAAAAAAA/41RW2sTQRT+ZjaXzWZt03hpYk2rtmpTxW2LT7UINSgsxC20
- JSB5miRLnGQzK7uT0Mc8+UP8B8WHgoIEffNHiWe30SJCcYc935zbd+ac8+Pn
- 568AnuExQ02oXhTK3qmjxET2hZahcg7H2o8a4eh9HoyhNBAT4QRC9Z3DzsDv
- 6jwMhty+VFK/YDA26y0bWeQsZJBnyOh3MmZYa17J/JzB3O8GKYcFniSarnd8
- cuA1Xtm4BqtAxgWG9WYY9Z2BrzuRkCp2hFKhTrlixwu1Nw4ColpqDkNNZM4b
- X4ue0IJsfDQxqEuWiEIiwMCGZD+VibZNt94OQ302tS1e4RYvzaYWNw3z+wde
- mU13+Tbb40bmZd7k3z7meIknCbssoVlwlaI2AhHHSS8MxdRwMR2GJ1d2vvF3
- ch5r9Ij/yPg9+3u0EU9MGqHSURgEfvR0SDVXjsZKy5HvqomMZSfwDy4HRTtp
- hD2fYbEple+NRx0/OhEUw1Buhl0RtEQkE31utC9f6FOydRyOo67/Wia+6rxO
- 658q2KGNZdIxV5MFEm6QliMsEXI62VR7QJqTLIMwu3UO8yx1P5wH02rwiKR9
- EYACUQEmin+Slyk6+YpfwN+ew/6ExbPUYGCTZJncd+mv0TvuE64S1tMS69gi
- 3COaJSIut2G4uO7ihoubuEVXLLuooNoGi3EbK21kY1gx7sTIxajFWP0Fraxj
- CzoDAAA=
- """,
- """
- androidx/navigation/TestAbstract.class:
- H4sIAAAAAAAA/4VRy0oDMRQ9SduxjlWnPusL1IWvhaPiThFUEApVQaUbV2kn
- aOw0gUlaXPZb/ANXggspLv0o8WZ07+ZwHjfh5Obr+/0DwCFWGFaFTjKjkudY
- i756EE4ZHd9J605b1mWi7UbAGKIn0RdxKvRDfN16kt4tMATHSit3wlDY2m5W
- UEIQoogRhqJ7VJZhvfHf5UcM1UbHuFTp+FI6kQgnyOPdfoEKMg+jHsDAOuQ/
- K6/2iCX71H04CENe4yGPiA0H5Y3acHDA99hZ6fMl4BH3cwfMn46uRP/caJeZ
- NJXZbsdRyXOTSIbJhtLyqtdtyexOtFJyphqmLdKmyJTXf2Z4a3pZW14oLxZu
- etqprmwqqyg91dq4/HG2uAZOO/ir7FdCWCMV5xoo7byh/EqEY4EwyM1NLBJW
- fgcwijDPl3Kcx3L+VwxjlFXuUahjvI6JOiYREUW1jilM34NZzGCWcovQYs4i
- +AEAejLS6AEAAA==
- """,
- """
- androidx/navigation/TestAbstractComp$Companion.class:
- H4sIAAAAAAAA/5VSy24TMRQ99qR5DAHSlkfCuyVILRKdpGJFEVIJQoqUFolW
- 2XSBnIkpTmY8yHaiLrPiQ/iDrpBYoKhLPgpxPQmwpZv7Ovfcax/756/vPwA8
- xxOGHaGHJlPDs0iLqToVTmU6OpbW7Q+sMyJ2nSz93PRGaIJKYAy1kZiKKBH6
- NHo3GMnYlRAwFF8qrdwrhmBru1/FCoohCigxFNwnZRlavcut2mNob/XGmUuU
- jkbTNFLaSaNFEr2RH8UkoXZNvEnsMnMgzFiave1+CO5Xrjfjf+CHNEfprpeb
- xrD6h3AgnRgKJ6jG02lA4jFvKt6AgY2pfqZ81qJo2GZozmdhyOs85DWK5rPy
- xZegPp/t8hZ7XSrzi69FXuO+d5f5Cc3/0aaEuwyVvwLRQxyKKZ3bmSxJpNkZ
- OxK7kw0lw/We0vJwkg6kORaDhCprvSwWSV8Y5fNlsdrVWppOIqyV9EThUTYx
- sXyrPNZ4P9FOpbKvrKLmfa0zl5/Lok0qF/zVyXP/0nSDh5RFXgvyK0+/oXye
- w4/IFvPiC2yQrS4aUEEI1BhFV5bkZ+T5klw9z3X1hFuL4oKQR1dxjbAAm5SF
- Oeke7qOBx/nCB2jmf5s0oN7aCYIuVrtY62IdNyjEzS7NvH0CZlFHg3CL0OKO
- RfE3/dAjtxgDAAA=
- """,
- """
- androidx/navigation/TestAbstractComp.class:
- H4sIAAAAAAAA/41RW2sTQRg9s5vrurFJvSXWS2trTPvQbYsgNEWoESGQpqAl
- IHmaJGOdZDMrM5PQx/4W/0HxoaAgwUd/lPjtNrYPvuTlO/Ndzvku8/vP958A
- XmKLYYOrgY7k4CxQfCpPuZWRCk6EsYc9YzXv20Y0/pIFYygO+ZQHIVenwXFv
- KPo2C5chcyCVtK8Z3Npmx0caGQ8pZBlS9rM0DNXWIg3qDLmDfjiX2l6EshEb
- riiVhc+wW2uNIksKwXA6DqSyQiseBm/FJz4JiaCIOenbSB9xPRK6fjXsbQ8F
- LDHkr8UYdhaa+KZ93UcJy3k4uMOw3or0aTAUtqe5VCbgSkU2UTBBO7LtSRjS
- rqV/sx4Jywfccoo546lLn8Jik48NGNiI4mcy9nboNdile87Ofc8pO55TnJ17
- Ts7JVcuz81V3z9lh+8x9k/71NeMUnbh6j8UaxTaf0vpWR2Eo9PbIMqy8nygr
- x6KpptLIXigOb6akj2tEA8Gw1JJKtCfjntAnnGoYlltRn4cdrmXsz4N+Uymh
- GyE3RhDZ+xBNdF+8k3GuMu/T+a9Lao3OlUp2rMTXI1wnL0N4j9AhTCfeBnlB
- fAnC9NYlchdJ+vm8GNhHlax/VYA8PMIcbl2Ty0huCf8HCh/ZJYrfcPciibh4
- QdajugIplmiQWqL9DJuEryh+nxQfdOE2UW6i0sRDrNATj5p4jCddMIOnWO0i
- ZeAZrBlkDEp/AVeTkqlcAwAA
- """,
- """
- androidx/navigation/TestClass.class:
- H4sIAAAAAAAA/31Ry0oDMRQ9N7VTHauO7/reqgtHxZ0iaEEoVAWVblylnaCx
- 0wQmaXHZb/EPXAkupLj0o8Q7o2s3h/O4CecmX9/vHwCOsEHYkCbJrE6eYyMH
- +kF6bU18p5yvp9K5CogQPcmBjFNpHuLr9pPq+ApKhOBEG+1PCaXtnVYVZQQh
- xlAhjPlH7QhbzX9vPibMNrvWp9rEl8rLRHrJnugNSlyNcpjIAQTqsv+sc7XP
- LDkgbI6GYShqIhQRs9GwNhoein06L3++BCIS+dQh5WejKzmoW+Mzm6Yq2+t6
- 7le3iSLMNLVRV/1eW2V3sp2yM9e0HZm2ZKZz/WeGt7afddSFzsXKTd943VMt
- 7TSnZ8ZYX+zlcADB6/8Vzl+DscYqLjRQ3n3D+CsTgRXGoDCXscpY/R3ABMIi
- XytwGevFHxEmOaveo9TAVAPTDcwgYorZBuYwfw9yWMAi5w6hw5JD8AOsqUxn
- 4AEAAA==
- """,
- """
- androidx/navigation/TestClassComp$Companion.class:
- H4sIAAAAAAAA/5VSy24TMRQ99qR5DAHSlkfCuxCkFminqdgVIUEQUqS0SKXK
- pgvkJKY4mbGR7URdZsWH8AddIbFAUZd8FOJ6UmCJurmPc++5987x/Pz1/QeA
- 53jM8FTooTVqeJJoMVXHwiujk0PpfDsVzrVN9rkZjNCEl8AYaiMxFUkq9HHy
- rj+SA19CxFB8obTyLxmi9Y1eFUsoxiigxFDwn5Rj2OxeYM8uQ2u9OzY+VToZ
- TbNEaS+tFmnyRn4Uk9S3jXbeTgbe2D1hx9LubvRi8LBvtTn4V/yQ5VWGrYtN
- Y1j+Q9iTXgyFF4TxbBqRbCyYSjBgYGPCT1TItikathia81kc8zqPeY2i+ax8
- 9iWqz2c7fJu9LpX52dcir/HQu8PChLX/ClPCbYbKX3XoCfbFlI721qSptFtj
- TzK3zVAyXO0qLfcnWV/aQ9FPCVnpmoFIe8KqkJ+D1Y7W0uYLJD1O/N5M7EC+
- VaHWOJhorzLZU05R8yutjc+PcmiRxIXw3eR5eGM6/z5lSRCC/NKTbyif5uUH
- ZIs5+AxrZKuLBlQQAzVG0aVz8iZ5fk6unuaiBsKNBbgg5NFlXKFahIeUxTnp
- Du6igUf5wnto5r80aUC9tSNEHSx3sNLBKq5RiOsdmnnzCMyhjgbVHWKHWw7F
- 3/peHcMPAwAA
- """,
- """
- androidx/navigation/TestClassComp.class:
- H4sIAAAAAAAA/4VRXWsTQRQ9s5vPdWOT+pVYP1obta0f2xRBsEXQiBBII2gp
- SJ8myVgn2czIzCT0sb/Ff1B8KChI8NEfJd7dxvbBh7zcM/fMuWfuvfP7z/ef
- AJ5hg2GFq77Rsn8UKT6Rh9xJraI9YV0z5tY29ehLHoyhPOATHsVcHUbvugPR
- c3n4DLkdqaR7yeCvre+HyCIXIIM8Q8Z9lpZhtT3XfZuhsNOLZz6P5urrSeCK
- +DxChsZae6gdlUeDySiSygmjeBy9EZ/4OHZNrawz457TZpeboTDbZ21eDlDC
- AkPx3IzhyfxeL97eDlHBYhEeriRTanMYDYTrGi6VjbhS2qXlNupo1xnHMU1Z
- +dfornC8zx0nzhtNfPoIloRiEsDAhsQfySTbpFO/wVCfHoeBV/UCrzw9DryC
- V50eL/tb3iZ7wfzX2V9fc17ZS7RbLHEod/iEJndGx7EwT4eOYen9WDk5Ei01
- kVZ2Y/Hqokf6rabuC4aFtlSiMx51hdnjpGFYbOsej/e5kUk+I8OWUsKkSxFU
- HHzQY9MTb2VyV5u9s//fK2jQsjLphLVkd4SrlOUIrxF6hNk0q1MWJXsgzG6c
- onCSXt+fiYHHeEAxPBOgiICwgEvnxVWkm0T4A6WP7BTlb7h6kjI+HlIMSFci
- xwo1spZ638M64XPir5PjjQP4LVRbqLVwE0t0xK0WbuPOAZjFXSwfIGMRWKxY
- 5CwqfwEoeIqnTgMAAA==
- """,
- """
- androidx/navigation/TestClassWithArg.class:
- H4sIAAAAAAAA/41QTW/TQBScXSdOYhLihK805ZsKtTngtOIGqgiRkCyFIpUq
- HHLaxJa7jbOWvJuox/wWzlyQQEgcUMSRH4V461ScOCDZ897sjuf5za/f338A
- eI49hj2hojyT0WWgxEomwshMBWexNsNUaP1BmvNBnlTAGPwLsRJBKlQSvJte
- xDNTgcPgvpRKmmOG0n54MGZw9g/GdZRR8VBClbjIEwYW1uHhWg0cdZKac6kZ
- no7+Z/YLmpHEZmBtyDxkaI3mmUmlCt7GRkTCCJLwxcqhlZiFmgXQ0DmdX0rL
- +tRFhwzDzbrt8Q73uL9Ze/Rwv+rxqtPZrI94n71utF2fd3nf+fnR5X7ptPWX
- VUndLVXLvmutjpgd4J+I1TBTJs/SNM6fzQ2tNsyimKE5kio+WS6mcX4mpimd
- tEfZTKRjkUvLrw6999kyn8VvpCU7p0tl5CIeSy3pdqBUZopINA4pt1KxU9vG
- SB2nvgyX8AGxY+Kcqtf7hlpv9ysanwvNQ0KrAXbwiPD2VoXraNqIqLNulCh8
- erdegU2Oarn3BY1P/7SpbwVXNhyPC7yPJ1RfFT9Zxo0JnBA3Q9wKaewdatEJ
- 6fvuBExjF3cnqGg0Ne5peAW6Gr5G6w+psmgInQIAAA==
- """,
- """
- androidx/navigation/TestClassWithArgComp$Companion.class:
- H4sIAAAAAAAA/5VSS29SQRT+Zi6FckWlrQ/wVR+YUBO5hXRXY1IxJiS0Jtrg
- ogszwEgH7p0xcwfSJSt/iP+gKxMXhnTpjzKeuaBu7ea8vvOdM/PN/Pz1/QeA
- PTxlaAk9tEYNzyItZmoknDI6Opapa8ciTT8od3pgR22TfK55IzTBBTCG8ljM
- RBQLPYre9sdy4AoIGPIvlFbuJUNQ3+mVsIZ8iBwKDDl3qlKGve7l1+0zNOvd
- iXGx0tF4lkRKO2m1iKPX8pOYxq5tdOrsdOCMPRR2Iu3+Ti8E92u3aoN/4Mck
- Qxkal5vGsPGHcCidGAonqMaTWUAiMm+K3oCBTah+pny2S9GwyVBbzMOQV3jI
- yxQt5usXX4LKYt7iu+xVYZ1ffM3zMve9LeYn1P9XnwLuMhT/ikQPciRmdHZn
- TRxL25g4Er1thpLheldpeTRN+tIei35Mlc2uGYi4J6zy+apY6mgtbbZH0lOF
- 783UDuQb5bHqu6l2KpE9lSpqPtDauOxsKZqkdM5fnzz3L0632KYs8nqQX3v2
- DevnGfyQbD4rNvCIbGnZgCJCoMwourIiPyfPV+TSeaatJ9xaFpeELLqKa4QF
- eExZmJHu4T6qeJItfIBa9s9JA+otnyDoYKODzQ62cINC3OzQzNsnYCkqqBKe
- IkxxJ0X+N9Gij4okAwAA
- """,
- """
- androidx/navigation/TestClassWithArgComp.class:
- H4sIAAAAAAAA/41S308TQRD+9vrrehY5KmIBf6CglqpcITwJIcEa4yWlJkhq
- DE/bdi3bXvfM3rbhkb/FZ1+IGhJNDPHRP8o4d634oA8kdzM7szPfzHyzP399
- /Q5gE+sMZa46OpSdY0/xkexyI0PlHYjI1AIeRW+kOdrV3Vo4eJ8DY3B7fMS9
- gKuu96rVE22TQ4ohuy2VNDsM6bK/2mRIlVebBWSQc5CGTTbXXQbmF+DgSh4W
- ChRqjmTEUKlftv4W1ekKsxtDUQGfwd5uB5PCG5dFWYkFV3SdwzWG9XK9HxpC
- 8XqjgSeVEVrxwHsu3vFhYGqhiowetk2o97juC701nuu6g1nMMeQvwBg2Lz3I
- 3xa2CihhPiZkgWG5Huqu1xOmpblUkceVCk2CEnmN0DSGQUAUzPzpd08Y3uGG
- k88ajFK0ThaLfCxAZPfJfyxjq0qnDm365flJ0bFKlmO55ycOfZZrO5adLp2f
- LOU2rCp7ynLPpopZ11qwqqkfH7KWm96fubBsSllI2xk3G+NtsLiK2+AjIsno
- MAiEXusbhsX9oTJyIHw1kpFsBWL37xy09VrYEQzTdalEYzhoCX3AKYahWA/b
- PGhyLWN74iz4Sgmd8Cco2XkdDnVbvJDx3fykTvOfKlgnQtM0uIX5mF/qs0JW
- lvRN0sX4EZJOkZ1JvI/I2qFoi7RTOUO+svgFU6cJwuNJJrCGJyTnxlG4iumY
- aDrFaLQXuPSPsbyYf9KZymdMffwvTGEcMIGxqancJLmEZIMofMPsW3aGG5+w
- eJp4UpQbF2T0+KxkMC/BXkWVdI38twjx9iFSPu74WPJxF/foiGUfK7h/CBbh
- AR4ewo4wHaEcwUlkNoIbYSZC6Tedt709GAQAAA==
- """,
- """
- androidx/navigation/TestGraph.class:
- H4sIAAAAAAAA/31SQWsTQRT+ZpJsNttoY6s2sdaq7UE9uG3xZhFqUFmIK9gQ
- kJ4m2SGdZDMju5PQY07+EP9B8VBQKKHe/FHimzXoQXAW3nvfN998zHuzP35+
- vQTwDLsMW0InmVHJWajFTA2FVUaHXZnbN5n4eFoFY2iMxEyEqdDD8F1/JAe2
- ihKDd6i0si8YSo8e9+qowAtQRpWhbE9VzrDd+a/zcwb/cJAWHgG4O+hH8XH3
- KG6/quMaghqR1xl2OiYbhiNp+5lQOg+F1sYWXnkYGxtP05SsbnTGxpJZ+FZa
- kQgriOOTWYm6ZC7UXAADGxN/phzaoyrZZ9hdzIOAN3nAG1Qt5v73T7y5mB/w
- Pfay6vOrzx5vcKc9YM6hEYtZ22ibmTSV2dOxZdh8P9VWTWSkZypX/VQe/b0j
- jaNtEsmw2lFaxtNJX2ZdQRqGtY4ZiLQnMuXwkgyOzTQbyNfKgdbSuPePLfZp
- OuWipZYbFuV7hDx3QcqcvkqBtgmFrnHKlScX8M+L7ftLMbCOBxTrvwWokRXg
- Y+XP4Q1Su7XyDfzDBepfsHpeEBwPi7iFneJfokcgg7UTlCKsR7gZ4RZuU4mN
- CE20TsBy3MEm7ecIctzN4f0CEKx6togCAAA=
- """,
- """
- androidx/navigation/TestInterface.class:
- H4sIAAAAAAAA/4WOz0rDQBDGv9lo08Z/qVqoR/Fu2tKbJykIgaqg4iWnbbIt
- 22x3IbsNPfa5PEjPPpR0Ux/AGfjmmxn4zfz8fn0DGKNHuOW6qIwsNonmtVxw
- J41OPoR1qXaimvNchCBCvOQ1TxTXi+R1thS5CxEQutPSOCV18iwcL7jjDwS2
- qgMPp0Y6jYBApZ9vZNMNvCuGhN5u245Yn0Us9m7e321HbEDNckS4m/77lb/k
- wfELrydGu8ooJar70hGid7OucvEklSDcvK21kyvxKa2cKfGotXEHmG35UzjC
- XzBcHfQS174OPfjYZytDkCJM0U7RQeQtTlKc4iwDWZzjIgOziC26e5qGvyhR
- AQAA
- """,
- """
- androidx/navigation/TestObject.class:
- H4sIAAAAAAAA/32Sz2sTQRTHvzNJNptttLH+aGK1VtuDenDb4s0i1KCwEFew
- IVB6mmSHOMlmBnYnS485+Yf4HxQPBQUJevOPEt+sUQ+Cu/De+37nzYeZt/v9
- x6cvAJ5ij2Fb6CQzKjkPtSjUWFhldNiXuX0znMiRrYMxtCaiEGEq9Dj87VYY
- vCOllX3OUHn4aNBEDV6AKuoMVftO5Qw7vf+jnzH4R6O0hATgbqcfxSf947j7
- sokrCBpkXmXY7ZlsHE6kHWZC6TwUWhtbwvIwNjaepymhrvWmxhIsfC2tSIQV
- 5PFZUaF7MhcaLoCBTck/V07tU5UcMOwtF0HA2zzgLaqWC//be95eLg75PntR
- 9/nXDx5vcdd7yByhFYuia7TNTJrK7MnUMmy9nWurZjLShcrVMJXHf89I8+ia
- RDKs95SW8Xw2lFlfUA/DRs+MRDoQmXJ6ZQYnZp6N5CvlRGcFHvyDxQFNp1pe
- qeOGRXmblOcOSJnTWyvVPVKhuzjl2uNL+Bfl8s6qGbiJ+xSbvxrQIBTgY+3P
- 5k3qds/aZ/DTSzQ/Yv2iNDgelPEudsu/iT4CATbOUIlwPcKNiNC3qMRmhDY6
- Z2A5bmOL1nMEOe7k8H4CjO1ti4oCAAA=
- """
- )
diff --git a/navigation/navigation-runtime-lint/src/test/java/androidx/navigation/runtime/lint/WrongStartDestinationTypeDetectorTest.kt b/navigation/navigation-runtime-lint/src/test/java/androidx/navigation/runtime/lint/WrongStartDestinationTypeDetectorTest.kt
index eaaf466..7b31a26 100644
--- a/navigation/navigation-runtime-lint/src/test/java/androidx/navigation/runtime/lint/WrongStartDestinationTypeDetectorTest.kt
+++ b/navigation/navigation-runtime-lint/src/test/java/androidx/navigation/runtime/lint/WrongStartDestinationTypeDetectorTest.kt
@@ -17,22 +17,15 @@
package androidx.navigation.runtime.lint
import com.android.tools.lint.checks.infrastructure.LintDetectorTest
-import com.android.tools.lint.checks.infrastructure.LintDetectorTest.compiled
-import com.android.tools.lint.checks.infrastructure.LintDetectorTest.kotlin
-import com.android.tools.lint.checks.infrastructure.TestFile
import com.android.tools.lint.checks.infrastructure.TestMode
import com.android.tools.lint.detector.api.Detector
import com.android.tools.lint.detector.api.Issue
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
+import org.junit.runners.JUnit4
-@RunWith(Parameterized::class)
-class WrongStartDestinationTypeDetectorTest(private val testFile: TestFile) : LintDetectorTest() {
-
- private companion object {
- @JvmStatic @Parameterized.Parameters public fun data() = listOf(SOURCECODE, BYTECODE)
- }
+@RunWith(JUnit4::class)
+class WrongStartDestinationTypeDetectorTest : LintDetectorTest() {
@Test
fun testEmptyConstructorNoError() {
@@ -42,7 +35,10 @@
"""
package com.example
- import androidx.navigation.*
+ import androidx.navigation.NavController
+ import androidx.navigation.TestNavHost
+ import androidx.navigation.createGraph
+ import androidx.test.*
fun createGraph() {
val navController = NavController()
@@ -56,7 +52,8 @@
"""
)
.indented(),
- testFile,
+ *NAVIGATION_STUBS,
+ TEST_CODE
)
.skipTestModes(TestMode.FULLY_QUALIFIED)
.run()
@@ -71,7 +68,9 @@
"""
package com.example
- import androidx.navigation.*
+ import androidx.navigation.NavController
+ import androidx.navigation.createGraph
+ import androidx.test.*
fun createGraph() {
val navController = NavController()
@@ -93,7 +92,8 @@
"""
)
.indented(),
- testFile,
+ *NAVIGATION_STUBS,
+ TEST_CODE
)
.run()
.expectClean()
@@ -107,7 +107,9 @@
"""
package com.example
- import androidx.navigation.*
+ import androidx.navigation.NavController
+ import androidx.navigation.createGraph
+ import androidx.test.*
fun createGraph() {
val navController = NavController()
@@ -124,7 +126,8 @@
"""
)
.indented(),
- testFile,
+ *NAVIGATION_STUBS,
+ TEST_CODE
)
.run()
.expectClean()
@@ -138,7 +141,9 @@
"""
package com.example
- import androidx.navigation.*
+ import androidx.navigation.NavController
+ import androidx.navigation.createGraph
+ import androidx.test.*
fun createGraph() {
val navController = NavController()
@@ -160,84 +165,85 @@
"""
)
.indented(),
- testFile,
+ *NAVIGATION_STUBS,
+ TEST_CODE
)
.run()
.expect(
"""
-src/com/example/test.kt:7: Error: StartDestination should not be a simple class name reference.
+src/com/example/test.kt:9: Error: StartDestination should not be a simple class name reference.
Did you mean to call its constructor TestClass(...)?
If the class TestClass does not contain arguments,
you can also pass in its KClass reference TestClass::class [WrongStartDestinationType]
navController.createGraph(startDestination = TestClass) {}
~~~~~~~~~
-src/com/example/test.kt:8: Error: StartDestination should not be a simple class name reference.
+src/com/example/test.kt:10: Error: StartDestination should not be a simple class name reference.
Did you mean to call its constructor TestClassWithArg(...)?
If the class TestClassWithArg does not contain arguments,
you can also pass in its KClass reference TestClassWithArg::class [WrongStartDestinationType]
navController.createGraph(startDestination = TestClassWithArg) {}
~~~~~~~~~~~~~~~~
-src/com/example/test.kt:9: Error: StartDestination should not be a simple class name reference.
+src/com/example/test.kt:11: Error: StartDestination should not be a simple class name reference.
Did you mean to call its constructor InnerClass(...)?
If the class InnerClass does not contain arguments,
you can also pass in its KClass reference InnerClass::class [WrongStartDestinationType]
navController.createGraph(startDestination = Outer.InnerClass) {}
~~~~~~~~~~~~~~~~
-src/com/example/test.kt:10: Error: StartDestination should not be a simple class name reference.
+src/com/example/test.kt:12: Error: StartDestination should not be a simple class name reference.
Did you mean to call its constructor InterfaceChildClass(...)?
If the class InterfaceChildClass does not contain arguments,
you can also pass in its KClass reference InterfaceChildClass::class [WrongStartDestinationType]
navController.createGraph(startDestination = InterfaceChildClass) {}
~~~~~~~~~~~~~~~~~~~
-src/com/example/test.kt:11: Error: StartDestination should not be a simple class name reference.
+src/com/example/test.kt:13: Error: StartDestination should not be a simple class name reference.
Did you mean to call its constructor AbstractChildClass(...)?
If the class AbstractChildClass does not contain arguments,
you can also pass in its KClass reference AbstractChildClass::class [WrongStartDestinationType]
navController.createGraph(startDestination = AbstractChildClass) {}
~~~~~~~~~~~~~~~~~~
-src/com/example/test.kt:12: Error: StartDestination should not be a simple class name reference.
+src/com/example/test.kt:14: Error: StartDestination should not be a simple class name reference.
Did you mean to call its constructor TestInterface(...)?
If the class TestInterface does not contain arguments,
you can also pass in its KClass reference TestInterface::class [WrongStartDestinationType]
navController.createGraph(startDestination = TestInterface)
~~~~~~~~~~~~~
-src/com/example/test.kt:13: Error: StartDestination should not be a simple class name reference.
+src/com/example/test.kt:15: Error: StartDestination should not be a simple class name reference.
Did you mean to call its constructor TestAbstract(...)?
If the class TestAbstract does not contain arguments,
you can also pass in its KClass reference TestAbstract::class [WrongStartDestinationType]
navController.createGraph(startDestination = TestAbstract)
~~~~~~~~~~~~
-src/com/example/test.kt:15: Error: StartDestination should not be a simple class name reference.
+src/com/example/test.kt:17: Error: StartDestination should not be a simple class name reference.
Did you mean to call its constructor Companion(...)?
If the class Companion does not contain arguments,
you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
navController.createGraph(startDestination = TestClassComp)
~~~~~~~~~~~~~
-src/com/example/test.kt:16: Error: StartDestination should not be a simple class name reference.
+src/com/example/test.kt:18: Error: StartDestination should not be a simple class name reference.
Did you mean to call its constructor Companion(...)?
If the class Companion does not contain arguments,
you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
navController.createGraph(startDestination = TestClassWithArgComp)
~~~~~~~~~~~~~~~~~~~~
-src/com/example/test.kt:17: Error: StartDestination should not be a simple class name reference.
+src/com/example/test.kt:19: Error: StartDestination should not be a simple class name reference.
Did you mean to call its constructor Companion(...)?
If the class Companion does not contain arguments,
you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
navController.createGraph(startDestination = OuterComp.InnerClassComp)
~~~~~~~~~~~~~~~~~~~~~~~~
-src/com/example/test.kt:18: Error: StartDestination should not be a simple class name reference.
+src/com/example/test.kt:20: Error: StartDestination should not be a simple class name reference.
Did you mean to call its constructor Companion(...)?
If the class Companion does not contain arguments,
you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
navController.createGraph(startDestination = InterfaceChildClassComp)
~~~~~~~~~~~~~~~~~~~~~~~
-src/com/example/test.kt:19: Error: StartDestination should not be a simple class name reference.
+src/com/example/test.kt:21: Error: StartDestination should not be a simple class name reference.
Did you mean to call its constructor Companion(...)?
If the class Companion does not contain arguments,
you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
navController.createGraph(startDestination = AbstractChildClassComp)
~~~~~~~~~~~~~~~~~~~~~~
-src/com/example/test.kt:20: Error: StartDestination should not be a simple class name reference.
+src/com/example/test.kt:22: Error: StartDestination should not be a simple class name reference.
Did you mean to call its constructor Companion(...)?
If the class Companion does not contain arguments,
you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
@@ -256,7 +262,9 @@
"""
package com.example
- import androidx.navigation.*
+ import androidx.navigation.TestNavHost
+ import androidx.navigation.createGraph
+ import androidx.test.*
fun createGraph() {
val navHost = TestNavHost()
@@ -278,7 +286,8 @@
"""
)
.indented(),
- testFile,
+ *NAVIGATION_STUBS,
+ TEST_CODE
)
.run()
.expectClean()
@@ -292,7 +301,9 @@
"""
package com.example
- import androidx.navigation.*
+ import androidx.navigation.TestNavHost
+ import androidx.navigation.createGraph
+ import androidx.test.*
fun createGraph() {
val navHost = TestNavHost()
@@ -310,7 +321,8 @@
"""
)
.indented(),
- testFile,
+ *NAVIGATION_STUBS,
+ TEST_CODE
)
.run()
.expectClean()
@@ -324,7 +336,9 @@
"""
package com.example
- import androidx.navigation.*
+ import androidx.navigation.TestNavHost
+ import androidx.navigation.createGraph
+ import androidx.test.*
fun createGraph() {
val navHost = TestNavHost()
@@ -345,84 +359,85 @@
"""
)
.indented(),
- testFile,
+ *NAVIGATION_STUBS,
+ TEST_CODE
)
.run()
.expect(
"""
-src/com/example/test.kt:7: Error: StartDestination should not be a simple class name reference.
+src/com/example/test.kt:9: Error: StartDestination should not be a simple class name reference.
Did you mean to call its constructor TestClass(...)?
If the class TestClass does not contain arguments,
you can also pass in its KClass reference TestClass::class [WrongStartDestinationType]
navHost.createGraph(startDestination = TestClass) {}
~~~~~~~~~
-src/com/example/test.kt:8: Error: StartDestination should not be a simple class name reference.
+src/com/example/test.kt:10: Error: StartDestination should not be a simple class name reference.
Did you mean to call its constructor TestClassWithArg(...)?
If the class TestClassWithArg does not contain arguments,
you can also pass in its KClass reference TestClassWithArg::class [WrongStartDestinationType]
navHost.createGraph(startDestination = TestClassWithArg) {}
~~~~~~~~~~~~~~~~
-src/com/example/test.kt:9: Error: StartDestination should not be a simple class name reference.
+src/com/example/test.kt:11: Error: StartDestination should not be a simple class name reference.
Did you mean to call its constructor InnerClass(...)?
If the class InnerClass does not contain arguments,
you can also pass in its KClass reference InnerClass::class [WrongStartDestinationType]
navHost.createGraph(startDestination = Outer.InnerClass) {}
~~~~~~~~~~~~~~~~
-src/com/example/test.kt:10: Error: StartDestination should not be a simple class name reference.
+src/com/example/test.kt:12: Error: StartDestination should not be a simple class name reference.
Did you mean to call its constructor InterfaceChildClass(...)?
If the class InterfaceChildClass does not contain arguments,
you can also pass in its KClass reference InterfaceChildClass::class [WrongStartDestinationType]
navHost.createGraph(startDestination = InterfaceChildClass) {}
~~~~~~~~~~~~~~~~~~~
-src/com/example/test.kt:11: Error: StartDestination should not be a simple class name reference.
+src/com/example/test.kt:13: Error: StartDestination should not be a simple class name reference.
Did you mean to call its constructor AbstractChildClass(...)?
If the class AbstractChildClass does not contain arguments,
you can also pass in its KClass reference AbstractChildClass::class [WrongStartDestinationType]
navHost.createGraph(startDestination = AbstractChildClass) {}
~~~~~~~~~~~~~~~~~~
-src/com/example/test.kt:12: Error: StartDestination should not be a simple class name reference.
+src/com/example/test.kt:14: Error: StartDestination should not be a simple class name reference.
Did you mean to call its constructor TestInterface(...)?
If the class TestInterface does not contain arguments,
you can also pass in its KClass reference TestInterface::class [WrongStartDestinationType]
navHost.createGraph(startDestination = TestInterface)
~~~~~~~~~~~~~
-src/com/example/test.kt:13: Error: StartDestination should not be a simple class name reference.
+src/com/example/test.kt:15: Error: StartDestination should not be a simple class name reference.
Did you mean to call its constructor TestAbstract(...)?
If the class TestAbstract does not contain arguments,
you can also pass in its KClass reference TestAbstract::class [WrongStartDestinationType]
navHost.createGraph(startDestination = TestAbstract)
~~~~~~~~~~~~
-src/com/example/test.kt:14: Error: StartDestination should not be a simple class name reference.
+src/com/example/test.kt:16: Error: StartDestination should not be a simple class name reference.
Did you mean to call its constructor Companion(...)?
If the class Companion does not contain arguments,
you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
navHost.createGraph(startDestination = TestClassComp) {}
~~~~~~~~~~~~~
-src/com/example/test.kt:15: Error: StartDestination should not be a simple class name reference.
+src/com/example/test.kt:17: Error: StartDestination should not be a simple class name reference.
Did you mean to call its constructor Companion(...)?
If the class Companion does not contain arguments,
you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
navHost.createGraph(startDestination = TestClassWithArgComp) {}
~~~~~~~~~~~~~~~~~~~~
-src/com/example/test.kt:16: Error: StartDestination should not be a simple class name reference.
+src/com/example/test.kt:18: Error: StartDestination should not be a simple class name reference.
Did you mean to call its constructor Companion(...)?
If the class Companion does not contain arguments,
you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
navHost.createGraph(startDestination = OuterComp.InnerClassComp) {}
~~~~~~~~~~~~~~~~~~~~~~~~
-src/com/example/test.kt:17: Error: StartDestination should not be a simple class name reference.
+src/com/example/test.kt:19: Error: StartDestination should not be a simple class name reference.
Did you mean to call its constructor Companion(...)?
If the class Companion does not contain arguments,
you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
navHost.createGraph(startDestination = InterfaceChildClassComp) {}
~~~~~~~~~~~~~~~~~~~~~~~
-src/com/example/test.kt:18: Error: StartDestination should not be a simple class name reference.
+src/com/example/test.kt:20: Error: StartDestination should not be a simple class name reference.
Did you mean to call its constructor Companion(...)?
If the class Companion does not contain arguments,
you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
navHost.createGraph(startDestination = AbstractChildClassComp) {}
~~~~~~~~~~~~~~~~~~~~~~
-src/com/example/test.kt:19: Error: StartDestination should not be a simple class name reference.
+src/com/example/test.kt:21: Error: StartDestination should not be a simple class name reference.
Did you mean to call its constructor Companion(...)?
If the class Companion does not contain arguments,
you can also pass in its KClass reference Companion::class [WrongStartDestinationType]
@@ -438,600 +453,3 @@
override fun getIssues(): MutableList<Issue> =
mutableListOf(WrongStartDestinationTypeDetector.WrongStartDestinationType)
}
-
-private val SOURCECODE =
- kotlin(
- """
-package androidx.navigation
-
-import kotlin.reflect.KClass
-import kotlin.reflect.KType
-
-public open class NavDestination
-
-// NavGraph
-public open class NavGraph: NavDestination() {
- public fun <T : Any> setStartDestination(startDestRoute: T) {}
-}
-
-// NavController
-public open class NavController
-
-public inline fun NavController.createGraph(
- startDestination: Any,
- route: KClass<*>? = null,
-): NavGraph { return NavGraph() }
-
-// NavHost
-public interface NavHost
-public class TestNavHost: NavHost
-
-public inline fun NavHost.createGraph(
- startDestination: Any,
- route: KClass<*>? = null,
-): NavGraph { return NavGraph() }
-""" +
- TEST_CLASS
- )
- .indented()
-
-// Stub
-private val BYTECODE =
- compiled(
- "libs/StartDestinationLint.jar",
- SOURCECODE,
- 0x8e62b385,
- """
- META-INF/main.kotlin_module:
- H4sIAAAAAAAA/2NgYGBmYGBgBGJOBijgUucSTsxLKcrPTKnQy0ssy0xPLMnM
- zxMS8Essc0ktLsnMA/O9S7hEubiT83P1UisScwtyUoXYQoCy3iVKDFoMAGXO
- +shYAAAA
- """,
- """
- androidx/navigation/AbstractChildClass.class:
- H4sIAAAAAAAA/41QTW8SURQ9780wwBRkwC9K/ajVNpSF0MaN0TRSjAkJdtE2
- LGD1YCb0hWEmmfcgXfJbXLsx0Zi4MMSlP8p431CNCxYu3rn33Hdy7sfPX9++
- A3iBfYYDEflJLP3rZiQWciK0jKNme6R0Isa6cyVDvxMKpbJgDLubtJeB0n/0
- WVgMzmsZSX3CYNcHh30Gq37YLyCDrAsbOeIimTCwQQEutvLgKJBUX0nFUO/9
- 3zSvqMsk0G1jRPYDhnJvGutQRs33gRa+0IIkfLawaE1mIG8A1HZK9WtpWIsy
- /4jhdLWsuLzK07dautzbcnnOqq6Wx7zFTosVx+M13rJ+fHC4Z5+X/7IcqWt2
- LuM5xumYYW/j+P8eiKaiIcpnYvGWqjJKFc+nmi7Qif2AodSTUXA2n42C5FKM
- QqpUevFYhH2RSMNviu5FPE/GwTtpyPb5PNJyFvSlkvTbjqJYp8YKR3ReO128
- Yq5NGac8A4dwl9gJcU7RbXxFvrHzBcVPqeYJodEAL7FHeG+twi2UzB0pM260
- CTx6a6+mOS/FTOMzih832hTWghsbjqcpPsYzim/SITO4PYTVxZ0u7nap7X1K
- Ue1iG7UhmMIOHgyRVSgpPFRwFR4pOAqeQvk3MHA2w9YCAAA=
- """,
- """
- androidx/navigation/AbstractChildClassComp$Companion.class:
- H4sIAAAAAAAA/51Sy24TMRQ99qR5DAHSlkfC+xGkthKdpqrYFCGVVEiR0iIB
- yqYL5MyY1smMB42dqMuu+BD+oCskFijqko9CXDsBtsDm+t5z7rnXczzff3z9
- BmAHTxh2hE6KXCWnkRZTdSysynW0NzS2ELHtnqg06abCmG6efWy7IDQ1VMAY
- GiMxFVEq9HH0ejiSsa0gYCg/V1rZFwzB2vqgjiWUQ5RQYSjZE2UYnvX/Z+Eu
- Q2etP85tqnQ0mmaR0lYWWqTRvvwgJqnt5pomTGKbFweiGMtid30QgrvFq+34
- D/k+8yzD5r9NY1j+JTiQViTCCsJ4Ng3ISOZCzQUwsDHhp8pVW5QlHYb27CwM
- eZOHvEHZ7Kx68Slozs62+RZ7Wanyi89l3uCud5u5CRt/71AFtxlqv22iWx6K
- 6b40Vmkv2xxbcr6bJ5Lhal9peTjJhrJ4J4YpISv9PBbpQBTK1Quw3tNaFn6D
- pPcK3+aTIpavlONabybaqkwOlFHUvKd1bv0egw6ZXXIO0Mnds9OH3KcqcpbQ
- ubTxBdVzTz+gWPZgHw8p1ucNqCEEGoyySwvxUzr5Qlw/9/Y6wY05OBf47DKu
- EBfgEVWhF93BXbTw2C+8h7b/3ckD6m0cIehhuYeVHlZxjVJc79HMm0dgBk20
- iDcIDW4ZlH8CofOnlCsDAAA=
- """,
- """
- androidx/navigation/AbstractChildClassComp.class:
- H4sIAAAAAAAA/5VSzU8TQRT/zW4/lyJtRSzgBwpiqcgWQjwIIcESTZPSA5Im
- wmnarmXodtbsTBuO/C2evRA1JJoY4tE/yvhmWyFRDnqYefPe/N7vff74+eUb
- gHWsMZS4bIeBaJ+4kg9Eh2sRSHe7qXTIW7pyJPx2xedKVYLeuyQYw8J1+H1P
- 6UufCGkzJDaFFHqLIVY8WGow2MWlRgZxJB3EkCKdhx0GdpCBg7E0LGQIqo+E
- Yliu/XtWGxSp4+ltQ0YhDhhSmy1/FHr933kWzMUlAZK4ybBarHUDTTzu8aDn
- Cqm9UHLf3fHe8r5PRUri6Ld0EO7ysOuFG8PabjmYxBRD+pKM4dl/FHOVxEYG
- BUybtswwzNeCsOMee7oZciGVy6UMdMSj3Hqg633fpzbkfme862ne5pqTzeoN
- bBo1M1faXKCWd8l+IoxWpld7leHVxWnesQpWdC5OHSs75lipWOHidC65ZpXZ
- c5Z8MZ5PZK0Zq2x/f5+wsrG93KWWIpeZWCqeTRg6WqrFa0v+c0soPcomV+eD
- HfoRMkKtdDXD7F5fatHzqnIglGj63vZVwbQklaDtMUzUhPTq/V7TC/c5YRjy
- taDF/QYPhdFHxkxVSi+MOuyRs/M66Ict76Uwf9OjOI2/omCVOh+jDlmYNoOg
- RJ+QliB5h2Te7CxJm/R4ZF0mbYvQFkmndI50afYzxs8ihqcjT6CGFbqnhijc
- wISZCL0MG7UCWTpDLtcMimS89AnjH66lyQwBI5oUJZUcORcQjRqZr5h8w85x
- +yNmzyKLTcQmIKM9taLCyhF3iQoGKmS/S4z3DmFXcb+KuSoe4CE9MV/FAh4d
- giks4vEhUgoTCkUFR2FJIaGQVcgpFH4BxvFHnF0EAAA=
- """,
- """
- androidx/navigation/AbstractChildObject.class:
- H4sIAAAAAAAA/41Sy2oUQRQ9VfPq6Yzm4SMzxkdMhBgXdhJcaRDGUaGhbcEM
- A5JV9YOkMj3V0F0zZDkrP8Q/CC4CCjLozo8Sb5XjA8zCbvreuueeOtX3UN++
- f/wM4BHuMWwJlRS5TE49JSbySGiZK68blboQse4dyyx5HZ2ksW6AMaxfRO6n
- pf61oYEKQ31fKqmfMlTubw9aqKHuoooGQ1Ufy5JhO/jPM58wOPtxZtVccCPh
- +OFBvxv2XrRwCW6TwMsMm0FeHHknqY4KIVXpCaVybVVLL8x1OM4ykloOhrkm
- Me9VqkUitCCMjyYVcoKZ0DQBDGxI+Kk01Q6tkl06YDZ1Xd7m9ptNna/veHs2
- 3eM77FnD4V/e1/kSN9Q9ho0Lh/vbI/MroZg8J0gq23441Axrb8ZKy1Hqq4ks
- ZZSl3T9TkHW9PEkZFgOp0nA8itKiL4jDsBLkscgGopCmnoPuQT4u4vSlNEVn
- Ljz4Rxa75F/VDt0xdlK+TVWd8hJlTm/NVneo8ow1lGsPzuGc2fb6nAw8xl2K
- rZ8ENEkKcLDwe/Mqsc2z8An87TlaH7B4ZgGODRtvYdPeSPKGBFYOUfFxxcdV
- H9dwnZZY9dFG5xCsxA2sUb+EW+JmifoP1gZuws4CAAA=
- """,
- """
- androidx/navigation/AbstractChildObjectComp.class:
- H4sIAAAAAAAA/5VSW2sTQRg9M0k2m220tV6aWO8t4gXdtvhmEWJUWNiu0IaA
- 9Gk2u7TTbGZldxL6mCd/iP+g+FBQkKBv/ijxmzFU0L64y36XM+c7s3OYHz8/
- fwXwDOsMj4VKilwmx74SE3kgtMyV34lLXYiB7h7KLHkbH6VU5qP3dTCG9fMG
- emmpz4Yss8LgbEsl9QuGyoOH/SZqcDxUUWeo6kNZMjwJ/2Pv5wzu9iCzih64
- kXGDaK/Xibqvm7gAr0HgRYa1MC8O/KNUx4WQqvSFUrm2yqUf5ToaZxlJXQqH
- uSYxfyfVIhFaEMZHkwq5wkxomAAGNiT8WJpug6pkkzaYTT2Pt7j9ZlP3+wfe
- mk23+AZ7WXf5t48OX+KGusVw/9wD/u2V+Z1ITF4RLJWlPB1qhtXdsdJylAZq
- IksZZ2nnz0nIwm6epAyLoVRpNB7FadETxGFYDvOByPqikKafg95ePi4G6Rtp
- mvZcuP+PLDbJw6o9eNtYSvkWdQ7lJcqc3prtblPnG3so1x6dwj2xy3fmZGAH
- dyk2fxPQICnAxcLZ8AqxzbPwBfzdKZqfsHhiAY57Nt7Emr2h5A0JLO+jEuBy
- gCsBruIalVgJ0EJ7H6zEdazSegmvxI0Szi+zsivd3gIAAA==
- """,
- """
- androidx/navigation/InterfaceChildClass.class:
- H4sIAAAAAAAA/41Qz28SQRT+ZhZY2FJZqFZK/VWrtnBwaaMnTWNbY0KCbVIb
- DnAaYKVTltlkZyA98rd49mKiMfFgiEf/KOMbSpqYcPAw773vvW++9+P3nx8/
- AbzADsOOUP0klv2rQImJHAgjYxU0lAmTj6IXHl/IqH8cCa1dMAb/UkxEEAk1
- CE67l2HPuHAYtpZJnIfa3Mi4SDNkXkslzQFDarddbTE4u9VWHi5yHlLwCItk
- wMDaeeSxmgPHLaKaC6kZqs3/nPIVtRmE5tAqkX6bodgcxiaSKngfGtEXRhCF
- jyYO7c+syVkD6juk/JW0qE5Rf4/haDYtebzM52829bi/4vGsU55N93mdHa2W
- Mj6v8Lrz61OG+6mz4g3KEruSyqb9jFXaZ9heOv8/J6KxaIriiZi8pbRUc8rz
- oaEbHMf9kKHQlCo8GY+6YXIuuhFlSs24J6KWSKTFi6T3IR4nvfCdtGDjbKyM
- HIUtqSVVD5WKzVxYY48OnKKOGXole3FanFPsIkv2MaEDwpy8V/uOldrmNxS+
- zDnbZO0v4CWekF2/ZsFH0Z6SIqtGu5Du2kIrsBcmn659ReHzUpn8NWEhw/F0
- brfwjPwbqt2m2p0OnAbWG7jbQBkbFKLSwCbudcA07uNBB65GUeOhRl7jkUZW
- o6Sx9hcxw6Dy8gIAAA==
- """,
- """
- androidx/navigation/InterfaceChildClassComp$Companion.class:
- H4sIAAAAAAAA/51STW/TQBB9u07zYQJNWz4SvgtBakHUTQXiUIQEqZAspUUC
- lEsPaGNv203sNfJuoh5z4ofwD3pC4oCiHvlRiFknwLlcZmfemzezfuufv77/
- APAMjxieCx3nmYpPAy0m6lhYlekg1FbmRyKS3ROVxN1EGNPN0s9tF4SmjgoY
- Q2MoJiJIhD4O3g2GMrIVeAzll0or+4rB29js17GEso8SKgwle6IMw4vef23c
- Zehs9EaZTZQOhpM0UE6hRRLsySMxTmw308bm48hm+b7IRzLf3ez74G7zWjv6
- R35KC5Zh62LTGFb+CPalFbGwgjCeTjyykrlQcwEMbET4qXLVNmVxh6E9m/o+
- b3KfNyibTavnX7zmbLrDt9mbSpWffy3zBne9O8xNeHIBiyq4xVD76xNd80BM
- 9qSxShe6rZEl77tZLBmWe0rLg3E6kPlHMUgIWe1lkUj6IleuXoD1UGuZFxsk
- vZj/IRvnkXyrHNd6P9ZWpbKvjKLm11pntthj0CG3S84COrl7ePqSe1QFzhM6
- lx5/Q/WsoO9TLBdgiHWK9XkDavCBBqPs0kL8lE6+ENfPCn+d4PocnAuK7DKu
- EOfhAVV+IbqNO2jhYbHwLtrFH08eUG/jEF6IlRCrIdZwlVJcC2nmjUMwgyZa
- xBv4BjcNyr8BSdfj0S4DAAA=
- """,
- """
- androidx/navigation/InterfaceChildClassComp.class:
- H4sIAAAAAAAA/5VSW08TURD+zrb0RpG2KJbiBQS1FGELYkyEkGCNZpNSEyRN
- hKfT9lBOuz1rdk8bHvktPvtC1JBoYoiP/ijjnKXCgySGh52Zb3bmm8uZX7+/
- /QCwhjWGRa5avidbR7biA9nmWnrKdpQW/gFvisqhdFsVlwdBxet9iIMxZDp8
- wG2Xq7b9ttERTR1HhGH2KppdEegLqjhGGGIbUkm9yRAt7i3UGSLFhXoacSRT
- iCJFmPttBraXRhpjSVi4QaH6UAYMS9VrdLpOpdpCbxk2qrHHkNhousPaz65B
- NG8EVxQRxy2GlWK162kisjuDni1NjuKu/Uoc8L6rK54KtN9vas/f5n5X+Ovn
- 091OYRJ5huQFGcPz64xz2cV6GgVMm83cYZiren7b7gjd8LlUgc2V8nRIFNg1
- T9f6rkuLyP5teVto3uKak8/qDSJ0AcyIpBGgrXfJfyQNKpPVWmF4c3acS1l5
- K/zOjlNWZjRlJaL5s+OZ+KpVZi9Y/OVYLpaxClY58vNjzMpEd7IXKEEphWhi
- JBMzdKum3/9eCfVGrWRrfPCK3FKFIctdzTC901da9oSjBjKQDVdsXU5LR1Lx
- WoJhvCqVqPV7DeHvcophyFW9Jnfr3JcGD51pRynhh+sVlJx65/X9pngtzb+p
- YZ36P1WwQmuPUnsx0lPmHcheonXFSN8jnTNXSzpCOI4EyWVCmxRtkU6VTjFa
- mv6K8RNCFuxhJuCgTHLyPAoZZM2DkGXYaBnEOzHkss07kR4pfcH4pytp0ucB
- Q5oEbiI5TM4jfGmkv2PyPTvF1GfcPQk9ERrNFGRhEwUabjXkfoKnpCvkv0+M
- M/uIOJh18MDBHObJxEMHj/B4HyxAEQv7SATIBigFSAdYDAzMBZgIUPgDTG80
- x3MEAAA=
- """,
- """
- androidx/navigation/InterfaceChildObject.class:
- H4sIAAAAAAAA/41SS2/TQBD+dpMmjmtoWl4J5VXaotIDbivEhQqpBJAsBSPR
- KBLqaRMv6SbOWrI3Vo858UP4BxWHSiChCG78KMSsCeUAEtjaeXwz83lm1t++
- f/wM4CHuMWwJHaWJik58LXI1EEYl2g+0kelb0ZetYxVHr3pD2TdVMIb6UOTC
- j4Ue+L/QEsPa3zg6MjPnPFUsMFT2lVbmCUNp637XQxWOizJqDGVzrDKG7fb/
- 9vKYwdnvxwWdC245nCA87ByEreceluDVCKwzrLeTdOAPpemlQunMF1onpqDN
- /DAx4SSOiWq5PUoMkfkvpRGRMIIwPs5LtCJmRc0KMLAR4SfKejtkRbv0gdnU
- dXmDF2c2db6+443ZdI/vsKdVh395X+F1blP3bC//3JLtJRT5M8KULuIPRoZh
- 9fVEGzWWgc5VpnqxPPg9Bi2vlUSSYamttAwn455MO4JyGFbaSV/EXZEq689B
- 9zCZpH35QlmnOSfu/kGLXVpgmWau0GnajZK+Q4Nbf4U0p5cukLw18ny7HdIL
- 22dwT4vw3Xky8AjrJL2fCVgkC1R44bz4GmXbZ/ET+JszXPyA5dMC4Ngo5G1s
- Fn8rwyUiuHyEUoArAa4GVNogE80A17F6BJbhBm5SPIOX4VYG5wfApo4N6gIA
- AA==
- """,
- """
- androidx/navigation/NavController.class:
- H4sIAAAAAAAA/4VRu0oDQRQ9d2I2ukZNfMYXGGzUwlWxUwSNCIGooJLGapId
- 4pjNDOxOgmW+xT+wEiwkWPpR4t01vc3hPGbuHO58/3x8AjjGJqEqTRhbHb4E
- Rg50RzptTXAjBzVrXGyjSMUFEKH0LAcyiKTpBLetZ9V2BeQI3qk22p0Rcju7
- zSLy8HxMoECYcE86IWw3/p1+Qig3utZF2gTXyslQOsme6A1yXJFSmEoBBOqy
- /6JTdcAsPCRsjYa+LyrCFyVmo+HkcmU0PBIHdJH/evVESaTnjii9XeZnL1Xi
- tMla7Hcd16zZUBHmGtqom36vpeIH2YrYmW/YtoyaMtapHpv+ve3HbXWlU7F6
- 1zdO91RTJ5rTc2OsywYnqELwFsad06UwVlgFmQbye++YfGMisMroZeYs1hiL
- fwcwBT/L1zNcwUb2XYRpzoqPyNUxU8dsHXMoMUW5jnksPIISLGKJ8wR+guUE
- 3i+86bUs6wEAAA==
- """,
- """
- androidx/navigation/NavDestination.class:
- H4sIAAAAAAAA/4VRO08CQRD+ZoEDTpSHiuAjUWOhFh4SO42Jj5iQICZqaKwW
- 7oLLYy/hFkLJb/EfWJlYGGLpjzLOnTRWNl++x+zMZPbr+/0DwAm2CLtSu0Nf
- uRNHy7HqSKN87TTk+NoLjNKRTIIIua4cS6cvdce5a3W9tkkiRrDOlFbmnBDb
- P2hmkIBlI44kIW6eVUDYq//f/pSQr/d801faufWMdKWR7InBOMZLUgjpEECg
- HvsTFaoKM/eYsD2b2rYoCVvkmM2mqWJpNq2KCl0mPl8skRNhXZXC1/m/c496
- hve88l2PkK0r7TVGg5Y3fJStPjuFut+W/aYcqlDPTfvBHw3b3o0KRfl+pI0a
- eE0VKE4vtPZN1DjADgSfYb5zeBXGEisn0kDi8A2pVyYCZUYrMi2sM2Z+C5CG
- HeUbEa5hM/owwgJnmSfEalisYamGLHJMka+hgOUnUIAVrHIewA5QDGD9AKYj
- 0APtAQAA
- """,
- """
- androidx/navigation/NavDestinationKt.class:
- H4sIAAAAAAAA/61WbVPbRhB+zsbYCGOECW8GDAGHGGgQEJI0hZJSaILKS1Kg
- pJS26WGEEQgpo5OZdDrT5lP/Q7/2FyTlQzplppPJt/ZHdbqnCgw2BibDB5/2
- 9naffXZv787//PvnXwDGsMGQ4fam65ibLzSb75t57pmOrS3y/RlDeKbtT+e8
- KBiDusP3uWZxO6893tgxcqQNM9TmXIN7xiOXP99msLLzFfCmHdtzHcsy3PH5
- UqDx+V3Hs0xbc40ti+ba3LTFhRjvrwTmRxtncK4y3MTA5MURe+cdN6/tGN6G
- y01baNy2Hc+3Etqi4y0WLIusMudZkQnfsAwyq57wtk0xGYPCkA447ezvaabt
- Ga7NLU2nJMjfzIko4gxNuW0jtxuEecJdvmeQIcPN7Bk5FjXLEiQ/3r8aRwL1
- Cuqg0m4Kj7veiV2OIcnQcV76UVyTnE3b9CYZwlkJ2IwWBU1oJcCMmdnKnOoG
- pjM0ZGSOp/W9l9g1hmR5UgwR1yl4BkNLhZZhaDwRKrNpbPGC5TH8fKWNqZdb
- Xtg5HWWFeFYYHTsmuFWR4KwjvCs8M+bVBLrUaek8NxTDD1eU9PvsR2Pe8Hxv
- 3aajYOeMJWOL4Xr2bMcVOihHHabmyty6LnCKYwCDNQjhA4ZUaeCnprc95eZ9
- oOxF8QNjotGSqwTSdzmIOIYxIkmNMrQSKd22Dbe8JJUoPaaD6GaKTkSpyTwb
- 4mxCZQBx3MU9SehDhthEzgpums5zs4liXMGEvIHOfMpKs46CEKuy+v+X1ycK
- HmCqgmspvyimFcxI84ajPlwwPL7JPU65h/b2w/SoMjnUyAF0Ae5KIUSLL0wp
- DZO0OcLw99uXo8rbl0qoNaSEYuGzv/STJioNR6atodQNNZ4KDdcNhIYTo9Vq
- PcnqaCIWUhtSsSS5trLh5Oy7X2In7BrPs+tpIHxaZO9+q45RnFQV2YRJW0XK
- SFFZrUZJGSNlTVGpqLUyIWqf9EWnjYrRVrHGUawwKMVCU4FP/wMZ2qUbsn2p
- YHvmnqHb+6Yw6QWdKr6qtKXTzia9C/Xzpm0sFvY2DHdFvrLyGXFy3Frlrinn
- gbJm2cwTdMElOVOKe/yyngpQt+zx3O4Cfx5AxIt8DVpWlp2CmzMemnKtLYBc
- LSOKEWrvKmqFMFLyTqC6fEWzavrG6JuSR7JMR6eiRBdDGyI0q8Iazb4LMJsH
- krV/oGEw2UhjePIQTWtv0PZKtiC+DrwTaMQ6yQPkkSCcFNohW7MZHeiUXUtS
- EmmylFIXusn3Gx8heorBt/RrDAeTo7EGUGtwHT0kS2I/kluEvunOyE+/IsIW
- KhIMUxJyZDGfadLPR6VoSYJWiUmRdfMJ1mn0BqzTx6zTAWtZocx7VaijYoX6
- zq3QjctX6ObVVKiNorX73dB8gnVphfoqViiLfvpKIh3+ClD1O2698qke1UPa
- n8yrDUPQyr1ul3p1l3iN4U651/1Sr56SPv8ILUGppv18gN5DTFBRPj7ArUM8
- WFPr3+DTA9w+xIwvf3aA+6+PQRNBERSi00zgYTyjuUKrM/gSq0Tre3/rnoLT
- N0/6h7Qfj9YR1jGrQ9fxOeZ0zGNBxyIer4MJPMEX67gmMCAwKJAV6BcYppMt
- MCSgCdwVuCcwJnBHICKwJNApkBRYFugS6Bbo+w//HMZ8gQ0AAA==
- """,
- """
- androidx/navigation/NavGraph.class:
- H4sIAAAAAAAA/31SXU8TQRQ9s6XbbUFaQBAq+AEoBZStxCdLSPwIWlOr0qYv
- PE3bSZl+zJqdacNjf4v/wCeND6bx0R9lvLOtCkHcZO6de+65d87OnR8/v34D
- 8Bh7DKtcNcNANs98xQeyxY0MlF/mg5ch/3CaAGNYv4LxQmgjVRQmEGNwD6SS
- 5pAhltuuzSAON4UpJBimzKnUDLdK/zuqwLCghakYHppznRkWc6U2H3C/y1XL
- f1tvi4YpbNdI+EH1yeXMYa5ajdIbpSBs+W1h6iGXSvtcqcBELbVfDky53+3S
- kbP693nHQd8ID2nS2QlMVyq/Pej5UhkRKt71i8qE1EY2dAJzJKpxKhqdSZ93
- POQ9QUSGrX+IPYdUbJNWwV7PAq6nMI9FhvnLJQxzpYmKN8LwJjecMKc3iNHY
- mDVJa8DAOoSfSRvladd8xPB+NMymnGVnvDxaGSc1GpKzxnO8peXRcN/Js2fx
- 7x9dSr5ey8SyTn5q3fNGw0x8x8m7+24mkXVejQmebbzPsHnVAM/Ni2RaVVX6
- g4uJvY6hl/A8aAqGdEkqUe736iKs8npX2DsIGrxb46G08QRMVmSLivsh7TeP
- +8rIniiqgdSS0n8u/enfwTKkKkE/bIgjaetXJjW1ccU5Iu7CobdpP4fk0lMl
- u0WRb8WTj+98hvcpSufIuhGYxDbZmTGBohT5OUwTEouKC8R2yCd25zNfsHSx
- 3CW6LV8aUybldpfGDcrvROxr2LWYFTEbAQ8iex8PyR8Rukwnr5wgVkS2iJtF
- rGKNtrhVxG3cOQGzv7Z+gqRGSmNDw9WY1tjUuBfZtMbML5qRw8f+AwAA
- """,
- """
- androidx/navigation/NavHost.class:
- H4sIAAAAAAAA/31OTUvDQBB9s9F+xK9ELVTEv2Da4s2TUMRAVVDwktO2Wcs2
- 6S50t6HH/i4P0nN/lDiJd2fgzZt5w5vZ/3x9A7hDj3AtTb6yOt8kRlZ6Lr22
- JnmR1ZN1vg0iRAtZyaSUZp68ThdqxtOAEE8K60ttkmflZS69vCeIZRWwLdXQ
- rQEEKni+0XU3YJYPCb3dthOKvghFxOyzv9uOxIBqcUS4mfzzD99gy5i7sXJe
- m0a8LTwhfLfr1Uw96lIRrt7Wxuul+tBOT0v1YIz1zapr8RUc4C8ELho8xyXX
- ITsfcrYyBCnaKTopugiZ4ijFMU4ykMMpzjIIh8gh/gUZbPE0RgEAAA==
- """,
- """
- androidx/navigation/Outer$InnerClass.class:
- H4sIAAAAAAAA/41U30/bVhT+rp0fjgngAG35kbXdyFgS2jqwdusK7QZ0DLMQ
- OpjQOvZySbxgCDazHdS9TDz1T6i0vUyapj3x0EobTKtUsfZtf9M07VzbTbrQ
- IST7nnOPz/nOd88513/988czANexypDjds11rNoD3eZ7Vp37lmPry03fdHOG
- bZvuXIN7XhKMQdvie1xvcLuuL29smVU/CZkhMW3Zln+HIZY3CmsMcr6wlkYc
- SRUxKAyKJVBm3DoDM9JQ0ZWChDT5+5uWxzBWPguBKYauuukbLSxKYzCoVWdn
- 17FN258gwKqz+y1DgXicFXO07Lh1fcv0N1xu2Z7ObdvxA29Przh+pdloTInD
- JFTifJ4hLVLkaubXvNnwGTbyZ0tkGOXO2k2dkWMa/RgQ2YeplL6z6ruWTccf
- yBdegQytdJ4LnbbZptWomW4SF1VcEu0YaGPnX3bmtoI3qZF8d9e0awxX8yeh
- T2aLkIngKHIC/G2GrCj9aY7vCMe8cJw73bEoHMfTyOINoV2lw29yb3POqZkM
- mXakYftmXZyvFA4gTZiOSRUTeJdOZH7T5A2asXP519T/S5r909pPvecbDZOq
- Gnf8TdNl6DuJQmTK247fsGx9yfR5jfucbNLOnkz3i4klJRbQ8G+T/YEldsRV
- qtHA/ny8f1GVBiVV0o73VXokTVElJUGyi6RMskd5/lAZPN6flEpstrsvoUnD
- Ukl+/lNC0mKLKS0pdgsvHsqL/ZpCOjkqihQ6kZmROUW6OqloXcOxQVZiCy8e
- yRSYDj0eMdK7Se8R+kqmBa8QneGYEtcSguskEycY+t+BTWKe7mJ7sqgqFb53
- 1/R8yw7crm3TbYmF3estW7ZZae5smO7nosCirk6VN9a4a4l9ZBxZadq+tWMa
- 9p7lWWSaaTeHoXvV59XtJb4beec6ve9xl++YxO0/Yek2R5O26qrTdKvmvCUg
- hiKItRPpaJok+pmJGvSJHxhpCun0W6B1kXbz9F0iqRaPkCqO/IbuJ7ST8Cmt
- PRAt1ym+hBTJMu3Oh970rVcMB2kClaoGjd4QUxczQzJe/BXdBy24RGAsBTDp
- 0CGCyRC5l8GjncHstQH0ayFYETBBLAWn1FNI90eOcOFxKygkm2qRTUVklyI2
- 5wAthUEMRbnHomJlsrHvvociGEwXRw4xEkJWaJXBBAJd7ij9LZKCWvYpLt0/
- wuW+tw4xJiIPUdAKh7hyiGuPO46RjRi9woNWvVWDsagGAYPfcb2zDEoUz3AD
- 70U8viIp2pUrjv+CeOxg/E9IPyAuH4wfQ1oSQFfo/VFYYmFPKkH75KTyNzJJ
- 2rcrlmtVLIeb+IDyLJOeFKTeD2pwLwil+4VPsEDl+ywANLBC8guy36JOTa1D
- NjBt4LaBO/iQVHxkYAaz62Ae5nB3Hb2eeD72oAZrwoPmIeOhz0O/hxuB8aYH
- 3UOW9H8BSGQIivsHAAA=
- """,
- """
- androidx/navigation/Outer$InnerObject.class:
- H4sIAAAAAAAA/41US08TURT+7p0+plMe5SFPxQdFXsoUxBXEBFHjkFKMJRhl
- dWlHGGhndOa2YcnKnVsXLl24YiFxQaKJqRI3/ijiudNREKIxac/5zrnnNd+5
- Mz+OP30BMIvbDCPCLfueU941XVF3NoV0PNdcqUnbz1qua/srG9t2SSbBGDLb
- oi7MinA3zV9ejSEx77iOvMOgjY2vtSCOhIEYkgwxueUEDKP5/+owx6BLryh9
- x91k6B4bz590a3opYjjv+Zvmti03fOG4gSlc15NhwcAseLJQq1QoKn2qrI42
- Krwlgq1Fr2yHQ1ra99nj1zS4/bImKjThhbH82SebG3/GkP1XN2olNio2tYt7
- csv2GTrPV6HW86VKyI8BrkjRrUJxdaGweL8FAzBS5Bxk6MjveJLCzGVbirKQ
- ghJ5ta7RjpgSKSXAwHbIv+soK0eoPM3wvLE3ZPA+bvBMY8/gugLpSOuGcmXa
- 9KNXRl9jb4bn2N2kzr+9S/AMX+rKaAM8F5vRM/GBWB/LsYdHb7SlVCZB3iRh
- RlgnnFJYdZthaob+v24ziXF6lIKo37MD6bjhydSOZBh8XHOlU7Utt+4EDpG2
- cEIkXZPmYtrzjmsXatUN219VxCo+vZKorAnfUXbkbC1KUdpZFi8iO3u29iPh
- i6pN4/zRpCW8EosVEQQ2mUbRq/kl+4GjSvRHJdbODYdp2k8spL5frYv0DbIS
- pFtJx+k0Hlo3yTLVgpR34hD6AQGOqSgYFGCSbGkGIEWlVNE0eXiYfDVK1jrb
- P4RHJ+FaFH66M72L6Ij6nqR27v8llaEL3VEnizQn3Tsx+R7x2P7kV/C3iGv7
- kw3wJ7H9cPAcyRh4Ug+L9TQTomIK9dCfETtQV5peIAI6+n5T0RsmAOnP4E8P
- 0f8RFw9Ch4YZkopHjgm0Eau3wn6T9C1SozFcInqG1qFZuGzhikVPd40ghi1k
- MbIOFuA6RtdhBOo3FiARoCsEPQEyIUiT/AmvDPuL4QQAAA==
- """,
- """
- androidx/navigation/Outer.class:
- H4sIAAAAAAAA/4VRW2sTQRT+ZjaXzSbaNF6aWFsvTbWp4rbFp1qEGhUW0hRs
- CUieJskQJ9nMwu4k9DFP/hD/QfGhoCBB3/xR4tltNA9S3GHPN+f2nTnn/Pz1
- 5RuA53jCUBG6Fwaqd+ZqMVF9YVSg3eOxkWEWjKE4EBPh+kL33ePOQHZNFhZD
- 5kBpZV4yWFu1VgFpZBykkGVImQ8qYlhtXMn6gsE+6PpJvgMeJ9le8+T0sFl/
- U8A1ODkyXmfYaARh3x1I0wmF0pErtA5MwhO5zcA0x75PVMuNYWCIzD2SRvSE
- EWTjo4lF3bFY5GIBBjYk+5mKtR269XYZarNpweFl7vDibOpw27J/fOTl2XSP
- 77B9bqVeZW3+/VOGF3mcsMdiGsfTWoZ1X0TUZD5RLqfCUL2y4+oiKYt7DJv/
- ifwz5wfUXlNMXsvIKJ1EPRtSodV3Y23USHp6oiLV8eXhYjK0gHrQkwxLDaVl
- czzqyPBUUAxDqRF0hd8SoYr1ubGweJqkZOckGIdd+VbFvsq8TuufKtilFaWS
- uVbijRFWScsQFgk5nXSibZLmxtMnTG9fwD5P3I/mwcBTPCZZuAxAjqgAG/m/
- ySsUHX/5r+DvL1D4jKXzxGBhi2SJ3PfpX6N3PCRcJ6wlJTawTbhPNMtEXGrD
- 8nDDw00Pt3CbrljxUEalDRbhDlbbSEdwItyNkImwFmH9NzJGDiwjAwAA
- """,
- """
- androidx/navigation/OuterComp$InnerClassComp$Companion.class:
- H4sIAAAAAAAA/51TTW/TQBB9a6dxYkJJUz4SoJRCgBRB3VQIIRUhQapKkdJW
- giqXHtAmWcom9hp511GPOfFD+Ac9IXFAUY/8KMSsE6i4IJXLzJt582a0M/aP
- n9++A3iGBsNzrgZJLAcngeJjecyNjFVwkBqRtOLoU72tFKGQa52F1nBFJR4Y
- Q3nIxzwIuToODnpD0TceXIb8S6mkecXgNta7JSwg7yMHjyFnPkrN8KLzfyO3
- GZqNzig2oVTBcBwFUpFE8TDYER94GppWrLRJ0r6Jkz2ejESyvd714djRy/X+
- Ofk+yliGjYt1Y1j6LdgThg+44ZRzorFLy2TWFK0BAxtR/kTaaJPQoMlQn058
- 36k6vlMmNJ0Uzj671elky9lkb7yCc/Yl75QdW7vFbIcnF9mRh1sMK/9UeFhh
- WPxbxlD8s1x62z4f7whtpMqkGyNDF2vFA8FwpSOV2E+jnkgOeS+kTKUT93nY
- 5Ym08TxZOm8v6M7+uzhN+mJXWq72NlVGRqIrtaTi10rFJpuj0aQT5ezeyDv2
- c6Hn36MosIskv/D4KwqnGX2fbD5L7qJOtjQrQBE+UGaELs3FT8k7c3HpNDuK
- FVyfJWeCDF3GInEuHlBUIfY27mAVtQzdJf8wG7yGR9kPQ7sgTfkIbhtLbVTa
- WMZVgrjWpt43jsA0qqgRr+Fr3NTI/wIu2QAobQMAAA==
- """,
- """
- androidx/navigation/OuterComp$InnerClassComp.class:
- H4sIAAAAAAAA/5VUW08bVxD+zvq2XgysIRcHSJMWNzWGsECTlAJpuJelXFJI
- aQjtw8HemgV7l+6urfSlylN+QqT2pVIf+sRDorZQFamiyVt/U1V1zu5ibhES
- kn3OzOzMN9+ZmXP++e/PvwDcwdcMPdwqOrZZfKpZvGaWuGfalrZY9Qxnwq5s
- Z3XLIqnMXVeoCTAGdZPXuFbmVklbXN80Cl4CEYb4iGmZ3icM0ZzetcIQyXWt
- pBBDQkEUMoNsCqQxp8TA9BQUNCQhIUX+3obpMvTOXYTIMENDyfD0Oial0xmU
- An2zLcPy+gm4YG9/x9BPfC6K3TlnOyVt0/DWHW5arsYty/b8KFdbsL2Fark8
- LA4XV+gMVxhSIlW2aHzDq2WPwcldLKGuz52u6fAFOafQikuCTRuV2rOXPce0
- qCyXcl3HoAMrne/qadt41SwXDSeBdxTcEO3KnMTPHXbvvox3qdl8e9uwigy3
- c2fhz2YM0YlkJ7IiwfsMHaIt5zl+IBxzwnHifMe8cOxOoQPXhXSbCrDB3Y0J
- u2gwpI8idcszSuKMfcGQ0hRqGFDQjw/pRMa3VV6mObyce0svnjBkzxsJmge+
- XjaosjHb2zAchpazKMRrpFAOb8m9i3Q3KxZukUsCw2Ki57Zsj5C0zVpFM+lY
- jsXL2mQwfhPEyHOqBc925rmzRUUKLuJ9BSOgzMk6GMPghYbsiAbVfRRj4gKP
- U4kP2cwbHi9yjxNFqVKL0AvDxJIUC+jab5H9qSk06oBUpCu6c/DspiJlJEVS
- D54p9JNUWZHkOO0NtEdobyKz/Pq5nCHX5gGpjw2x5vHGlrgqtUl9kdc/xyU1
- OptUE0KbefM8MtuqyiQfPBuQZSlwIjMjc5JkZUBWG9qiGdbHZt68iFBgKvB4
- wUhuJLlJyEvpOrxM+duickyNC84DTJzk+rlVS+AhQ9PJ0lGVFnht0nA90/Ld
- e7fonWhfqlqeWTF0q2a6Jg3Q2NFQ0YwGE9w8Z1rGQrWybjiPxJCJ2bILvLzC
- HVPoobFx2eOFrXm+HerZ09gPucMrBnE8kSR1xNMgVVm2q07BmDYFxLUQYuUM
- ObozEj3roPWamASqySPS4rRfpr1FPO+i86THfOsXpE2Tt0S7kt9DMt/+Oxpf
- +QgrtDZBjMUkYU5R1CS+JO1K4E3fmsUAkSRQqZJQ6R9gamKuaI/lf0PjTh0u
- 7hunfJhU4BDCpIncYXDn6WD21gB6WAlWBPQTS8EpuQ9ptX0PV1/WgwKyyTrZ
- ZEj2WFnUJDJUriD3rbCA6Y7o9z9AFgxG8u27aA8gH9MaARMI9KyF6YdoF9Q6
- 9nFjdQ83W97bxS0RuYsutWsXPbvofXnqGB0ho+PtYVS2dJ1HUAOfwR+4c7oM
- chjPcBf3Qh5f0S7alc13/4JYdKf7b0g/IhbZ6T6ANC+Aeuj/k7BEg5489tsX
- Scj/Ip0g/ahi2XrFshjEx5RnleSEIPWRn34IiZBqxk9KxPYxssr28OBXTLzy
- LRE88adOzNfnWKIij5A0Svuan36ZKINkRpMVw9QaIjqmdXyqYwY6iZjV8Rnm
- yMHFPBbWoLpodrHoQvHXuCssaRctLlpd3PWNgy40Fx2+PPo/FBJ91lIJAAA=
- """,
- """
- androidx/navigation/OuterComp$InnerObject.class:
- H4sIAAAAAAAA/41US08TURT+7p0+plMe5SFPxQeoLVWmoK4gJoAah5RihGCU
- 1aUdy0A7gzO3DUtW/gQXLl3ohoXEBYkmpsrOH2U8dxgFIRKT9t7vnHvO+c58
- 5878+Pn5K4C7uMeQE27F95zKjumKplMV0vFcc6khbX/eq2+PWa5r+0vrm3ZZ
- JsEYMpuiKcyacKvmb6/GkJhxXEfeZ9CyudU2xJEwEEOSISY3nIAhX/xvlmkG
- XXrL0nfcKkNvNlc8ZjzyUsRo0fOr5qYt133huIEpXNeTYdHALHmy1KjVKCp9
- oqyODiq8IYKNea9ih41amvuhMEPN268aokZdXsgWTz/ddO4Fw9h5bEQl1ms2
- 0cU9uWH7DN1nqxD1TLkWamSAK2F0q7S8Mluaf9iGIRgpcg4zdBW3PElh5qIt
- RUVIQYm83tRoVkwtKbWAgW2Rf8dRVoFQZZLhZWt3xOAD3OCZ1q7BdQXS0a4b
- ypXp0A9fGwOt3SleYHNJnX9/l+AZvtCT0YZ4ITalZ+JDsQFWYI8P32gLqUyC
- vEnCjLBOOKWwYptiqodL5040iRw9Tkk0H9iBdNzwdGJLMgw/bbjSqduW23QC
- h4SbPRaTrsvRcDqLjmuXGvV1219R4ipNvbKorQrfUXbkbF+Wory1KLYje+x0
- 7SfCF3WbWvqLpC28FvM1EQQ2mcay1/DL9iNHlRiMSqyeaQ6TNKNYKP+gGhnt
- t8hK0N5Oe5xO46F1myxTDUl5xw+g7xPgmIiCgTk6BtqOApCiUqpomjw8TL4a
- JWvdnR/Do+NwLQo/yUzvJLoi3uPU7r1/pDL0oDdismjntPeP598jHtvLfwN/
- i7i2l2+BP4vthY0XaI2BJ/WwWN9RQlRMoT76M1IH6lrTS0RAx8AfKfrDBCD9
- Bfz5AQY/4eJ+6NAwRavSkWMcHaTqnZAvT98l1RpdMZJnZA2ahcsWrlj0dNcI
- YtTCGK6vgQW4gZtrMAL1ywZIBOgJQV+ATAjStP4Cqe39ku0EAAA=
- """,
- """
- androidx/navigation/OuterComp.class:
- H4sIAAAAAAAA/41RW2sTQRT+ZjaXzSa2abw0sbZVW7Wp4rbFp1qEGBUWYgq2
- BCRPk2SJk2xmZWcS+pgnf4j/oPhQUJCgb/4o8ew2WkQo7rDnm3P7zpxzfvz8
- /BXAEzxkWBWqF4Wyd+IqMZF9YWSo3MOx8aN6OHqfBWMoDsREuIFQffewM/C7
- JguLIXMglTTPGKytaquANDIOUsgypMw7qRnWG5cyP2WwD7pBwuGAx4m21zw6
- rjXrLwu4AidHxgWGjUYY9d2BbzqRkEq7QqnQJFzabYamOQ4ColpqDENDZO5r
- 34ieMIJsfDSxqEsWi1wswMCGZD+RsbZDt94uQ3U2LTi8zB1enE0dblv29w+8
- PJvu8R22z63U86zNv33M8CKPE/ZYTLPgKUVtBELruBeGfGI4nw7Do0s73/w7
- OYt1esR/ZPye/R1qtykmL3xtpEoiHw+p6MqbsTJy5HtqIrXsBH7tYlK0lHrY
- 8xkWG1L5zfGo40fHgmIYSo2wK4KWiGSsz42Fiyf6lOwcheOo67+Ssa8yr9P6
- pwp2aWWpZM6VeIOEm6RlCIuEnE460e6R5sbbIExvn8E+Tdz358FADQ9IFs4D
- kCMqwEb+T/IyRcdf/gv42zMUPmHxNDFY2CJZIvdt+lfpHXcJ1wirSYkNbBPu
- E80SEZfasDxc9XDNw3XcoCuWPZRRaYNp3MRKG2kNR+OWRkZjVWPtF1d0yO47
- AwAA
- """,
- """
- androidx/navigation/TestAbstract.class:
- H4sIAAAAAAAA/4VRy0oDMRQ9SduxHau29dX6AHUh6sLR4kJQhKoIA7WCSjeu
- 0s6gsdMMTNList/iH7gSXEhx6UeJN2P3bg7nkdwcbr5/Pj4BHGGdYUOoIIll
- 8OIpMZSPwshYefehNo2ONonomikwhtKzGAovEurRu+k8h9bNMDinUklzxpDZ
- 2W0XkYPjIosphqx5kpphq/nf8BOGcrMXm0gq7zo0IhBGkMf7wwwVZBYKFsDA
- euS/SKsOiAWH1H08cl1e5S4vERuP8tvV8ajOD9h57uvV4SVuz9WZvV1uieEl
- PSxVWmK/Z6jlRRyEDHNNqcLWoN8Jk3vRicipNOOuiNoikVZPTPcuHiTd8Epa
- UbsdKCP7YVtqSWlDqdikg3V2E5yWMOlsd0JYJeWlGsjtvSP/RoSjRuik5jFW
- CIt/B1CAm+arKS5jLf0shmnKig/I+JjxMetjDiWiKPuoYP4BTGMBi5RruBpL
- Gs4vELgJXekBAAA=
- """,
- """
- androidx/navigation/TestAbstractComp$Companion.class:
- H4sIAAAAAAAA/5VSy27TQBQ9M07zMAHclkfCmzZILRJ1UrErQiqpkCLSIkGV
- TRdo4gxlEnuMPBOry6z4EP6gKyQWKOqSj0LccQJs6ea+zj33+p7xz1/ffwB4
- jicMO0KPslSNzkItcnUqrEp1eCyN3R8am4nIdtPkc8sZoQmqgDEEY5GLMBb6
- NHw7HMvIVuAxlF8orexLBm9re1DHCso+SqgwlOwnZRja/cut2mPobPUnqY2V
- Dsd5EiptZaZFHB7Ij2IaU7sm3jSyaXYosonM9rYHPrhbud6K/oEfkgKlWy83
- jWH1D+FQWjESVlCNJ7lH4jFnas6AgU2ofqZc1qZo1GFozWe+zxvc5wFF81n1
- 4ovXmM92eZu9qlT5xdcyD7jr3WVuQut/tKngLkPtr0D0fUciP6AmpQvCzsSS
- 2t10JBmu95WWR9NkKLNjMYypstZPIxEPRKZcvizWe1rLrBsLYyS9kf8+nWaR
- fK0c1nw31VYlcqCMouZ9rVNb7DHokMwldzt57p6aTnhIWejEIL/y9Buq5wX8
- iGy5KL7BY7L1RQNq8IGAUXRlSX5Gni/J9fNCWEe4tSguCEV0FdcI87BBmV+Q
- 7uE+mtgsFj5Aq/i5SQPqDU7g9bDaw1oP67hBIW72aObtEzCDBpqEG/gGdwzK
- vwGgwqXcGQMAAA==
- """,
- """
- androidx/navigation/TestAbstractComp.class:
- H4sIAAAAAAAA/41RW28SQRT+ZpfrulioN7BeWotI+9CljYmJNCaVxoRIMdGG
- xPA0wIgDy6zZGUgf+S3+g8aHJpoY4qM/ynh2i+2DL7ycb87tO9858/vP958A
- nmOXoczVIAzk4MxTfCaH3MhAeadCm6OeNiHvm0Yw+ZIGY8iP+Ix7PldD711v
- JPomDZshdSiVNK8Y7OpOx0USKQcJpBkS5rPUDJXWKgPqDJnDvr+k2lulpRwZ
- riiVhsuwX22NA0MM3mg28aQyIlTc947FJz71qUFR57RvgvCEh2MR1i/F3nSQ
- wxpD9oqMobaS4uvxdRcFrGdh4RbDdisIh95ImF7IpdIeVyowMYP22oFpT32f
- di3803oiDB9wwylmTWY2fQqLTDYyYGBjip/JyKvRa7BP91zMXccqWo6VX8wd
- K2NlKsXFfNM+sGrsJbNfJ399TVl5K6o+YBFHoc1nxyReqljG3tgwbLyfKiMn
- oqlmUsueL46uZdLPNYKBYFhrSSXa00lPhKecahjWW0Gf+x0eyshfBt2mUiJs
- +FxrQc3Oh2Aa9sUbGeVKyzmd/6YktuheiXjJUnQ+wm3yUoR3CC3CZOyVyfOi
- UxAmdy+QOY/TT5fFwFtUyLqXBcjCIczgxlVzEfEx4f5A7iO7QP4bbp/HERvP
- yDpUlyPGAgmpxtxPsEP4guJ3ifFeF3YTxSZKTdzHBj3xoImHeNQF03iMzS4S
- Go7GlkZKo/AX8BZ2A10DAAA=
- """,
- """
- androidx/navigation/TestClass.class:
- H4sIAAAAAAAA/31Ru04CQRQ9d4BFVlTAF/hs1cJFYqcxUYwJCWKihsZqYDc4
- sMwmzEAs+Rb/wMrEwhBLP8p4d6W2OTmPO/femfn++fgEcIpdwq7U/ihS/oun
- 5UT1pFWR9h4DY+uhNCYLIhT6ciK9UOqed9fpB12bRYrgnCut7AUhdXDYziMD
- x0UaWULaPitD2G/+2/mMUGwOIhsq7d0GVvrSSvbEcJLi1SiGXAwg0ID9FxWr
- KjP/hLA3m7quKAtXFJjNpuXZtCaqdJX5enVEQcRVNYrPFltycs0zlU7mHw8s
- L1iP/ICw0lQ6aI2HnWD0KDshO6Vm1JVhW45UrOem+xCNR93gRsWicj/WVg2D
- tjKK00utI5s0NjiB4PvPN46fg7HMyks0kDl6x8IbE4EKo5OYB9hizP8VIAc3
- ybcT3MRO8kmERc7yT0g1sNTAcgMrKDBFsYESVp9ABmtY59zANdgwcH4BpKME
- iuEBAAA=
- """,
- """
- androidx/navigation/TestClassComp$Companion.class:
- H4sIAAAAAAAA/5VSTW8TMRB99qb5WAJsWz4SvtsGqQW121TcCkiQCilSWiSo
- cukBOYkpTna9aO1EPebED+Ef9ITEAUU98qMQYyfAEfUynnnz3oz3eX/++v4D
- wDM8Zngq9CDP1OAs1mKiToVVmY6PpbGtRBjTytLPDReEJrwExhANxUTEidCn
- 8dveUPZtCQFD8bnSyr5kCDa3ulUsoRiigBJDwX5ShmG7c4k9+wzNzc4os4nS
- 8XCSxkpbmWuRxAfyoxgntpVpY/Nx32b5ochHMt/f6obgbt9qo/+v+SH1XYad
- y01jWP4jOJRWDIQVhPF0EpBtzIWKC2BgI8LPlKt2KRs0GRqzaRjyGg95RNls
- Wr74EtRm0z2+y16Xyvzia5FH3HH3mJuw9l9jSrjLUPnrDl3uSEwOiKG0Z++M
- LPncygaS4XpHaXk0TnsyPxa9hJCVTtYXSVfkytULsNrWWuZ+g6TXCd9n47wv
- 3yjXq78ba6tS2VVGEfmV1pn1ewya5HHBfTid3D0y3f8hVbFzgs6lJ99QPvft
- RxSLHnyBNYrVOQEVhEDEKLuyEG/TyRfi6rl31QluzcG5wGdXcY16AdapCr3o
- Hu6jjg2/8AEa/p8mD4gbnSBoY7mNlTZWcYNS3GzTzNsnYAY11KlvEBrcMSj+
- BuBNy2UQAwAA
- """,
- """
- androidx/navigation/TestClassComp.class:
- H4sIAAAAAAAA/4VRXWsTQRQ9s5vPdWOT+pVYta2NmlZ0myIIpgqaIiykEbQE
- JE+TZIyTbGZlZxL6mN/iPyg+FBQk+OiPEu9uY/vgQ1/umXvn3HPP3Pn95/tP
- AM+ww7DJ1SAK5eDYU3wmh9zIUHlHQptmwLVuhpMvWTCG4ojPuBdwNfTe9Uai
- b7KwGTL7UknzisGubXdcpJFxkEKWIWU+S82w1bpUvcGQ2+8HS53Hl/KrceCK
- 6lm4DPVaaxwaavdGs4knlRGR4oF3ID7xaWCaodImmvZNGB3yaCyixpnNqw4K
- WGHIn4sxPLnc68XshosSVvOwcC1+ZRgNvZEwvYhLpT2uVGiSdu21Q9OeBgG9
- svTP6KEwfMANp5o1mdn0ESwO+TiAgY2pfizjbJdOgzpDdTF3HatsOVZxMXes
- nFVezDfsPWuXvWD2m/SvrxmraMXcPRYrlNp8dkC+pUpMPB0bhrX3U2XkRPhq
- JrXsBeL1hUn6rmY4EAwrLalEezrpieiIE4dhtRX2edDhkYzzZdH1lRJRshVB
- zc6HcBr1xVsZ31WWczr/TUGdtpVKnliJl0e4RVmG8AahRZhOsiplXrwIwvTO
- KXInyfWDJRl4iYcU3TMC8nAIc7hy3lxGskq4P1D4yE5R/IbrJ0nFxiOKDvEK
- pFgiI7VE+z62CZ9T/SYp3urC9lH2UfFxG2t0xB0fd3GvC6axjo0uUhqOxqZG
- RqP0F+8+XexPAwAA
- """,
- """
- androidx/navigation/TestClassWithArg.class:
- H4sIAAAAAAAA/41QTWsTURQ9781kkoyJmcSvNFWrtpQ2Cyct7pRijAgDsUIt
- cZHVSzKkr5m8gXkvocv8FtduBEVwIcGlP0q8b1JcuRBmzr3nvsO5H79+f/8B
- 4Bn2GPaEmmSpnFyFSizlVBiZqvA81qaXCK0/SHPRzaZFMIbgUixFmAg1Dd+N
- LuOxKcJh8F5IJc0Jg3sQHQ4YnIPDQQUFFH24KBEX2ZSBRRX4uFEGR4Wk5kJq
- hv3+//R+Tj2mselaGzKPGOr9WWoSqcK3sRETYQRJ+Hzp0ErMQtkCqOmM6lfS
- sg5lkyOG3nrV8HmT+zxYr3z6eFDyeclprlfHvMNeVRtewFu84/z86PHAPav/
- ZSVSt9xSIfCs1TGzDeqnYvmaxpUqH/3pzNBuvXQSM9T6UsWni/kozs7FKKFK
- o5+ORTIQmbT8uui/TxfZOH4jLdk6Wygj5/FAakmvXaVSkxtrHNHh3Hyphr0j
- ZZzyAjzCHWInxDlFv/0N5fb2V1Q/55pHhFYDtPGY8O5GhZuo2RtRZt1oEwT0
- b7xCezqKhfYXVD/906ayEVzbcDzJ8SF2Kb7Mhyzg1hBOhNsR7kTU9h6laEbY
- QmsIprGN+0MUNWoaDzT8HD2NQKP+B9uGEJmeAgAA
- """,
- """
- androidx/navigation/TestClassWithArgComp$Companion.class:
- H4sIAAAAAAAA/5VSy24TMRQ99qR5DAHSlkfC+xGkFIlOE3VXBCqpkCKlRaJV
- WHSBnMS0TmY8yHaiLrPiQ/iDrpBYoKhLPgpxPQmwpZvre8+5597x8fz89f0H
- gG08Y2gJPTSpGp5FWkzViXAq1dGRtK4dC2s/KHe6a07aafK57oPQRBfAGCoj
- MRVRLPRJ9K4/kgNXQMCQf6m0cq8YgsZGr4wV5EPkUGDIuVNlGba7l1+3w9Bs
- dMepi5WORtMkUtpJo0Uc7clPYhK7dqqtM5OBS82+MGNpdjZ6Ibhfu14f/CM/
- JhnLsHm5aQyrfwT70omhcIIwnkwDMpH5UPIBDGxM+Jny1RZlwyZDfT4LQ17l
- Ia9QNp8VL74E1fmsxbfYm0KRX3zN8wr3vS3mJzT+158C7jKU/ppE33ggpnvU
- qHQm2hw7cr2dDiXD9a7S8mCS9KU5Ev2YkLVuOhBxTxjl6yVY7mgtTbZI0luF
- h+nEDORb5bna+4l2KpE9ZRU172qdumyPRZOszvn708n9k9M1HlIVeUPoXHn+
- DcXzjH5EMZ+Br/GYYnnRgBJCoMIou7IUv6CTL8Xl88xcL7i1ABeCLLuKa8QF
- eEJVmInu4T5qeJotfIB69qOTB9RbOUbQwWoHax2s4waluNmhmbePwSyqqBFv
- EVrcscj/BkiAeOUlAwAA
- """,
- """
- androidx/navigation/TestClassWithArgComp.class:
- H4sIAAAAAAAA/41SW08TQRT+ZnvZ7VpkWxELeEFBLVXZQngSgsEa4yalJkhq
- DE/TdizTbmfN7rThkd/isy9EDYkmhvjojzKe3VZ40AeS3XPmnDnn+85lfv3+
- 9gPABtYYylx1wkB2jlzFR7LLtQyUuy8iXfN5FL2V+nAn7NaCwQcTjMHp8RF3
- fa667utWT7S1iRRDdksqqbcZ0mVvpcmQKq8088jAtJGGRTYPuwzMy8PGlRwM
- 5ClUH8qIoVK/LP8m8XSF3omhiMBjsLba/oR4/bIoy7Hgiq5NXGNYK9f7gSYU
- tzcauFJpESruuy/Eez70dS1QkQ6HbR2Euzzsi3Bz3Nd1GzOYZcidgzFsXLqR
- ixI28yhhLh7IPMNSPQi7bk/oVsililyuVKATlMhtBLox9H0aQeFvvbtC8w7X
- nHzGYJSidbJY5GIBGnaf/Ecytqp06tCmX50dF22jZNiGc3Zs02c4lm1Y6dLZ
- 8aK5blTZU2Y+nypmHWPeqKZ+fswaTnqvcG5ZlDKftjJONsZbZzFLocFHL6hF
- qZJCV/uaYWFvqLQcCE+NZCRbvti5aITWXgs6gmG6LpVoDActEe5zimEo1oM2
- 95s8lLE9ceY9pUSYDFBQsv0mGIZt8VLGd3MTnuY/LFijiaapcwNz8YCp0ApZ
- WdI3SRfjV0g6RXYm8T4ia5uiDdJ25RS5ysJXTJ0kCI8nmcAzPCE5O47CVUzH
- k6ZTjEajgEP/GMuNF0A6U/mCqU//hcmPAyYwFhVlTpJLSFaI/HfMvGOnuPEZ
- CyeJJ4XVhJDR6zOSxtwEewVV0jXy3yLE2wdIebjjYdHDXdyjI5Y8LOP+AViE
- B3h4ACvCdIRyBDuR2QhOhEKE0h8DM7acGQQAAA==
- """,
- """
- androidx/navigation/TestGraph.class:
- H4sIAAAAAAAA/31SXWsTQRQ9M0k2m220af1oYq1Vmwf1wW2Lbxah1g8W1hVs
- CJQ+TbJDOslmVnYnSx/z5A/xHxQfCgoS9M0fJd5Zgz4IzsC995w5c5h7mR8/
- P38F8ARdhi2h4yxV8bmvRaFGwqhU+z2Zm9eZeH9WB2NojUUh/ETokf92MJZD
- U0eFwTlQWplnDJUHD/tN1OB4qKLOUDVnKmfYDv/r/JTBPRgmpYcHbi+6QXTc
- O4yOXjZxBV6DyKsMO2GajfyxNINMKJ37QuvUlF65H6UmmiUJWa2Fk9SQmf9G
- GhELI4jj06JCXTIbGjaAgU2IP1cW7VIV7zF0F3PP423u8RZVi7n7/QNvL+b7
- fJc9r7v820eHt7jV7jPrsBaJ4gU1oXT5iMcTw7D5bqaNmspAFypXg0Qe/n0k
- zeMojSXDaqi0jGbTgcx6gjQM62E6FElfZMriJekdp7NsKF8pCzpL4/4/ttij
- 8VTLnjp2WpTvEHIotyhz2rUSbRPybeeUa48u4V6Ux3eXYqCLexSbvwVokBXg
- YuXP5Q1S27XyBfzkEs1PWL0oCY77ZdzCTvmZaDZksH6KSoBrAa4HuIGbVGIj
- QBudU7Act7BJ5zm8HLdzOL8ATyx+j4kCAAA=
- """,
- """
- androidx/navigation/TestInterface.class:
- H4sIAAAAAAAA/4VOTUvDQBB9s9GmjV+JWqhH8W7a0psnQYRAVVDxktM22ZZt
- 0g10t6HH/i4P0rM/SpzEH+AMvHkz83gz3z+fXwAm6BOupcnXlc63sZG1Xkin
- KxO/K+sS49R6LjPlgwjhUtYyLqVZxC+zpcqcD48QTYvKldrET8rJXDp5RxCr
- 2mNzaqDXAAhU8Hyrm27ILB8R+vtdNxADEYiQ2Xyw343FkJrlmHAz/fcrvsTG
- 0bOsH3isTSu5LRwheKs260w96lIRrl43xumV+tBWz0p1b0zlWqnt8C0c4C8E
- Llo8xyXXETsfcnZSeAn8BN0EPQRMcZTgGCcpyOIUZymERWgR/QKEKxfgUgEA
- AA==
- """,
- """
- androidx/navigation/TestNavHost.class:
- H4sIAAAAAAAA/4VRu04CQRQ9d3koKyrgA/BF7NTCBWKnMfERIwlqooaGamA3
- OLLMJsxAKPkW/8DKxMIQSz/KeHcxxsLEYk7O487MnTsfn69vAA5QIpSEcvuB
- dEeOEkPZEUYGyrn3tLkWw8tAmxkQIfMohsLxheo4N61Hr81ujLD+19afbQlC
- 8kgqaY4JsZ3dRhozmLURR4oQNw9SE7br/1x+SMjWu4HxpXKuPCNcYQR7Vm8Y
- 4/4phFQIIFCX/ZEMVZmZWyFsTsa2bRWsaE3Ghcm4apXpNPH+lLQyVlhU5aI/
- e/h1P9Nz7kiqKNnvGm7/LHA9wmJdKu960Gt5/XvR8tnJ1YO28BuiL0P9bdp3
- waDf9i5kKIq3A2Vkz2tILTk9USow0cEaFVg8HZ7a9D3huBjXWDmRBhJ7L7Cf
- mVhYZ0xGZh4bjOlpAeaYhflmhEVsRf9MmOdsoYlYDYs1ZGrIIscUSzUsY6UJ
- 0lhFnnONtEZBY/YLEThWOyQCAAA=
- """,
- """
- androidx/navigation/TestObject.class:
- H4sIAAAAAAAA/31S0WoTQRQ9M0k2m220aW1tYrVWW0R96LbFN4tQq8LCuoIN
- AenTJDvESTazsDtZ+pgnP8Q/KH0oKEjQNz9KvLNGfRCcgXvvOXPmMPcy3398
- +gLgCXYZtoSOs1TF574WhRoKo1Ltd2Vu3vRHcmDqYAytkSiEnwg99H+zFQbn
- SGllnjFUHj7qNVGD46GKOkPVvFc5w3b4f+unDO7RIClNPHB70w2i0+5xdPKy
- iWvwGkReZ9gJ02zoj6TpZ0Lp3Bdap6Y0y/0oNdE0SchqJRynhsz819KIWBhB
- HJ8UFeqT2dCwAQxsTPy5smifqviAYXc+8zze5h5vUTWfud8+8PZ8dsj32fO6
- y79+dHiLW+0hsw4rkSheUBdKl4/YGxuGzbdTbdREBrpQueon8vjvI2kgJ2ks
- GZZDpWU0nfRl1hWkYVgN04FIeiJTFi9I7zSdZgP5SlnQWRj3/rHFAY2nWvbU
- sdOivEXIodyizGnXSnSXkG87p1x7fAX3ojzeXoiBB7hHsflLgAZZAS6W/lze
- ILVdS5/B312heYnli5LguF/GO9gpvxPNhgxWz1AJcCPAWoB13KQSGwHa6JyB
- 5biFTTrP4eW4ncP5CW9bciiLAgAA
- """
- )
diff --git a/navigation/navigation-runtime/build.gradle b/navigation/navigation-runtime/build.gradle
index f3a2d7e..2698334 100644
--- a/navigation/navigation-runtime/build.gradle
+++ b/navigation/navigation-runtime/build.gradle
@@ -21,6 +21,8 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
+
+import androidx.build.KotlinTarget
import androidx.build.LibraryType
plugins {
@@ -69,4 +71,5 @@
inceptionYear = "2017"
description = "Android Navigation-Runtime"
legacyDisableKotlinStrictApiMode = true
+ kotlinTarget = KotlinTarget.KOTLIN_1_9
}
diff --git a/paging/paging-common/build.gradle b/paging/paging-common/build.gradle
index 2bfc291..1c5792b 100644
--- a/paging/paging-common/build.gradle
+++ b/paging/paging-common/build.gradle
@@ -27,9 +27,9 @@
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.konan.target.Family
+
plugins {
id("AndroidXPlugin")
- id("com.android.library")
}
androidXMultiplatform {
@@ -39,7 +39,14 @@
ios()
watchos()
tvos()
- android()
+ androidLibrary {
+ namespace = "androidx.paging.common"
+ withAndroidTestOnDeviceBuilder {
+ it.compilationName = "instrumentedTest"
+ it.defaultSourceSetName = "androidInstrumentedTest"
+ it.sourceSetTreeName = "test"
+ }
+ }
defaultPlatform(PlatformIdentifier.JVM)
@@ -150,7 +157,3 @@
metalavaK2UastEnabled = false
samples(project(":paging:paging-samples"))
}
-
-android {
- namespace "androidx.paging.common"
-}
diff --git a/paging/paging-common/lint-baseline.xml b/paging/paging-common/lint-baseline.xml
new file mode 100644
index 0000000..c360e17
--- /dev/null
+++ b/paging/paging-common/lint-baseline.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.7.0-alpha02" type="baseline" client="gradle" dependencies="false" name="AGP (8.7.0-alpha02)" variant="all" version="8.7.0-alpha02">
+
+ <issue
+ id="NewApi"
+ message="This Kotlin extension function will be hidden by `java.util.SequencedCollection` starting in API 35"
+ errorLine1=" @OptIn(ExperimentalStdlibApi::class) repeat(result.size) { _hints.removeFirst() }"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/commonTest/kotlin/androidx/paging/PagingDataPresenterTest.kt"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="This Kotlin extension function will be hidden by `java.util.SequencedCollection` starting in API 35"
+ errorLine1=" @OptIn(ExperimentalStdlibApi::class) repeat(result.size) { _hints.removeFirst() }"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/commonTest/kotlin/androidx/paging/PagingDataPresenterTest.kt"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="This Kotlin extension function will be hidden by `java.util.SequencedCollection` starting in API 35"
+ errorLine1=" repeat(data.size) { removeFirst() }"
+ errorLine2=" ~~~~~~~~~~~~~">
+ <location
+ file="src/commonTest/kotlin/androidx/paging/TestUtils.kt"/>
+ </issue>
+
+ <issue
+ id="BanThreadSleep"
+ message="Uses Thread.sleep()"
+ errorLine1=" Thread.sleep(1000)"
+ errorLine2=" ~~~~~">
+ <location
+ file="src/commonJvmAndroidTest/kotlin/androidx/paging/PagedListTest.kt"/>
+ </issue>
+
+ <issue
+ id="BanThreadSleep"
+ message="Uses Thread.sleep()"
+ errorLine1=" @Suppress("BlockingMethodInNonBlockingContext") Thread.sleep(100)"
+ errorLine2=" ~~~~~">
+ <location
+ file="src/commonJvmAndroidTest/kotlin/androidx/paging/SingleRunnerTest.kt"/>
+ </issue>
+
+</issues>
diff --git a/paging/paging-testing/build.gradle b/paging/paging-testing/build.gradle
index 9212202..50837cb 100644
--- a/paging/paging-testing/build.gradle
+++ b/paging/paging-testing/build.gradle
@@ -29,7 +29,6 @@
plugins {
id("AndroidXPlugin")
- id("com.android.library")
}
androidXMultiplatform {
@@ -39,7 +38,14 @@
ios()
watchos()
tvos()
- android()
+ androidLibrary {
+ namespace = "androidx.paging.testing"
+ withAndroidTestOnDeviceBuilder {
+ it.compilationName = "instrumentedTest"
+ it.defaultSourceSetName = "androidInstrumentedTest"
+ it.sourceSetTreeName = "test"
+ }
+ }
defaultPlatform(PlatformIdentifier.ANDROID)
@@ -131,6 +137,3 @@
metalavaK2UastEnabled = false
}
-android {
- namespace "androidx.paging.testing"
-}
diff --git a/pdf/pdf-viewer-fragment/src/main/java/androidx/pdf/viewer/fragment/PdfViewerFragment.kt b/pdf/pdf-viewer-fragment/src/main/java/androidx/pdf/viewer/fragment/PdfViewerFragment.kt
index 59a2b5a7..d31fa8e 100644
--- a/pdf/pdf-viewer-fragment/src/main/java/androidx/pdf/viewer/fragment/PdfViewerFragment.kt
+++ b/pdf/pdf-viewer-fragment/src/main/java/androidx/pdf/viewer/fragment/PdfViewerFragment.kt
@@ -247,7 +247,6 @@
fastScrollView = pdfViewer?.findViewById(R.id.fast_scroll_view)
loadingView = pdfViewer?.findViewById(R.id.loadingView)
paginatedView = fastScrollView?.findViewById(R.id.pdf_view)
- paginationModel = paginatedView!!.paginationModel
zoomView = pdfViewer?.findViewById(R.id.zoom_view)
findInFileView = pdfViewer?.findViewById(R.id.search)
findInFileView!!.setPaginatedView(paginatedView!!)
@@ -295,7 +294,7 @@
}
annotationButton?.let { button ->
if ((savedInstanceState == null) && isAnnotationIntentResolvable) {
- button.visibility = View.VISIBLE
+ button.show()
}
}
},
@@ -456,6 +455,7 @@
contents,
pdfLoaderCallbacks!!
)
+ setAnnotationIntentResolvability()
}
private fun setAnnotationIntentResolvability() {
@@ -484,7 +484,7 @@
isAnnotationIntentResolvable &&
state.getBoolean(KEY_ANNOTATION_BUTTON_VISIBILITY)
) {
- annotationButton?.visibility = View.VISIBLE
+ annotationButton?.show()
}
}
}
@@ -552,6 +552,7 @@
SingleTapHandler(
requireContext(),
annotationButton!!,
+ paginatedView!!,
findInFileView!!,
zoomView!!,
selectionModel,
@@ -573,7 +574,7 @@
}
private fun refreshContentAndModels(pdfLoader: PdfLoader) {
- paginationModel = paginatedView!!.initPaginationModelAndPageRangeHandler(requireActivity())
+ paginationModel = paginatedView!!.model
paginatedView?.setPdfLoader(pdfLoader)
findInFileView?.setPdfLoader(pdfLoader)
@@ -636,20 +637,19 @@
override fun onResume() {
super.onResume()
- setAnnotationIntentResolvability()
if (!documentLoaded) {
return
}
+ setAnnotationIntentResolvability()
+ if (!isAnnotationIntentResolvable && annotationButton?.visibility == View.VISIBLE) {
+ annotationButton?.post { annotationButton?.hide() }
+ }
if (
isAnnotationIntentResolvable &&
annotationButton?.visibility != View.VISIBLE &&
findInFileView?.visibility != View.VISIBLE
) {
- annotationButton?.post {
- annotationButton?.visibility = View.VISIBLE
- annotationButton?.alpha = 0f
- annotationButton?.animate()?.alpha(1f)?.setDuration(200)?.start()
- }
+ annotationButton?.post { annotationButton?.show() }
}
}
@@ -705,10 +705,7 @@
private fun detachViewsAndObservers() {
zoomScrollObserver?.let { zoomView?.zoomScroll()?.removeObserver(it) }
- paginatedView?.let { view ->
- view.removeAllViews()
- paginationModel?.removeObserver(view)
- }
+ paginatedView?.let { view -> view.removeAllViews() }
}
override fun onDestroyView() {
@@ -753,6 +750,7 @@
}
if (pdfLoader != null) {
pdfLoaderCallbacks?.uri = fileUri
+ paginatedView?.resetModels()
destroyContentModel()
}
detachViewsAndObservers()
@@ -765,7 +763,7 @@
onLoadDocumentError(e)
}
if (localUri != null && localUri != fileUri) {
- annotationButton?.visibility = View.GONE
+ annotationButton?.hide()
}
localUri = fileUri
}
diff --git a/pdf/pdf-viewer/src/androidTest/java/androidx/pdf/widget/FastScrollViewIntegrationTest.kt b/pdf/pdf-viewer/src/androidTest/java/androidx/pdf/widget/FastScrollViewIntegrationTest.kt
index 5eb44fd..647e77c 100644
--- a/pdf/pdf-viewer/src/androidTest/java/androidx/pdf/widget/FastScrollViewIntegrationTest.kt
+++ b/pdf/pdf-viewer/src/androidTest/java/androidx/pdf/widget/FastScrollViewIntegrationTest.kt
@@ -55,13 +55,12 @@
// Start by adding a PaginatedView with 10 50x50 pages
paginatedView =
PaginatedView(activity).apply {
- initPaginationModelAndPageRangeHandler(activity).apply {
+ model.apply {
initialize(10)
for (i in 0..9) {
addPage(i, Dimensions(50, 50))
}
}
- model = paginationModel
}
// Add a ZoomView to host the PaginatedView
zoomView =
@@ -73,7 +72,7 @@
fastScrollView =
FastScrollView(activity).apply {
layoutParams = ViewGroup.LayoutParams(100, 400)
- setPaginationModel(paginatedView.paginationModel)
+ setPaginationModel(paginatedView.model)
addView(zoomView)
}
activity.setContentView(fastScrollView)
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/find/FindInFileView.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/find/FindInFileView.java
index aae1aa6..46be5f5 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/find/FindInFileView.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/find/FindInFileView.java
@@ -206,7 +206,7 @@
if (visibility) {
this.setVisibility(VISIBLE);
if (mAnnotationButton != null && mAnnotationButton.getVisibility() == VISIBLE) {
- mAnnotationButton.setVisibility(GONE);
+ mAnnotationButton.hide();
}
setupFindInFileBtn();
} else {
@@ -233,7 +233,7 @@
mQueryBox.setText("");
parentLayout.setVisibility(GONE);
if (mIsAnnotationIntentResolvable) {
- mAnnotationButton.setVisibility(VISIBLE);
+ mAnnotationButton.show();
}
});
}
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/select/SelectionActionMode.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/select/SelectionActionMode.java
index 52ebbce..984cebb 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/select/SelectionActionMode.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/select/SelectionActionMode.java
@@ -192,21 +192,19 @@
int screenWidth = mPaginatedView.getResources().getDisplayMetrics().widthPixels;
int screenHeight = mPaginatedView.getResources().getDisplayMetrics().heightPixels;
- if (pageSelection.getRects().size() == 1 || startHandlerect.intersect(0, 0,
- screenWidth, screenHeight)) {
+ if (pageSelection.getRects().size() == 1 || startHandlerect.intersect(0, 0, screenWidth,
+ screenHeight)) {
return pageSelection.getRects().getFirst();
} else if (stopHandleRect.intersect(0, 0, screenWidth, screenHeight)) {
return pageSelection.getRects().getLast();
} else {
// Center of the view in page coordinates
- int viewCentreX =
- mPaginatedView.getPaginationModel().getViewArea().centerX() *
- mPaginatedView.getPaginationModel().getPageSize(
- selectionPage).getWidth()
- / mPaginatedView.getPaginationModel().getWidth();
- int viewCentreY = mPaginatedView.getPaginationModel().getViewArea().centerY()
- - mPaginatedView.getPaginationModel().getPageLocation(selectionPage).top;
-
+ int viewCentreX = mPaginatedView.getViewArea().centerX()
+ * mPaginatedView.getModel().getPageSize(selectionPage).getWidth()
+ / mPaginatedView.getModel().getWidth();
+ int viewCentreY = mPaginatedView.getViewArea().centerY()
+ - mPaginatedView.getModel().getPageLocation(selectionPage,
+ mPaginatedView.getViewArea()).top;
return new Rect(viewCentreX, viewCentreY, viewCentreX, viewCentreY);
}
}
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/AbstractPaginatedView.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/AbstractPaginatedView.java
deleted file mode 100644
index 6575393..0000000
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/AbstractPaginatedView.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.pdf.viewer;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.ViewGroup;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-
-/**
- * Base class for views that will base their display on the {@link PaginationModel}.
- *
- * <p>Provides consistent {@link #onMeasure(int, int)} and {@link #onLayout(boolean, int, int, int,
- * int)} behavior and requests a layout each time a new page is added to the model.
- *
- * <p>Subclasses must implement {@link #layoutChild(int)} in order to position their actual views.
- * Subclasses can override {@link #onViewAreaChanged()} if they need to perform updates when this
- * happens.
- *
- * <p>Padding will not be acknowledged. If views must implement padding they need to measure
- * themselves but should be aware they will diverge from the coordinates of other views using the
- * {@link PaginationModel}.
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-public abstract class AbstractPaginatedView extends ViewGroup implements PaginationModelObserver {
-
- @Nullable
- private PaginationModel mModel;
-
- public AbstractPaginatedView(@NonNull Context context) {
- super(context);
- }
-
- public AbstractPaginatedView(@NonNull Context context, @NonNull AttributeSet attrs) {
- super(context, attrs);
- }
-
- public AbstractPaginatedView(@NonNull Context context, @Nullable AttributeSet attrs,
- int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
-
- public void setModel(@Nullable PaginationModel model) {
- this.mModel = model;
- }
-
- // This class does not produce a model but rather renders a model generated elsewhere to a view.
- // Any classes wishing to obtain the model should do so from the owner/manager.
- @Nullable
- public PaginationModel getModel() {
- return mModel;
- }
-
- protected boolean isInitialized() {
- return mModel != null;
- }
-
- /**
- * Measures this view in relation to the {@link #mModel} then asks all child views to measure
- * themselves.
- *
- * <p>If the {@link #mModel} is not initialized, this view has nothing to display and will
- * measure (0, 0). Otherwise, view will measure ({@link #mModel}'s width, {@link #mModel}'s
- * estimated height).
- */
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int width = 0;
- int estimatedHeight = 0;
-
- if (isInitialized()) {
- width = mModel.getWidth();
- estimatedHeight = mModel.getEstimatedFullHeight();
- }
-
- setMeasuredDimension(width, estimatedHeight);
- measureChildren(widthMeasureSpec, heightMeasureSpec);
- }
-
- /**
- * Provides consistent layout behavior for subclasses.
- *
- * <p>Does not perform a layout if there aren't any child views. Otherwise asks the
- * subclasses to
- * layout each child by index.
- */
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- int count = getChildCount();
- if (count == 0) {
- return;
- }
-
- for (int i = 0; i < count; i++) {
- layoutChild(i);
- }
- }
-
- /**
- * Lays out the child at {@code index}.
- *
- * <p>Subclasses should use the {@link #mModel} to determine top and bottom values.
- */
- protected abstract void layoutChild(int index);
-
- /** Requests a layout because this view has to grow now to accommodate the new page(s). */
- @Override
- public void onPageAdded() {
- requestLayout();
- }
-
- /**
- * Implementation of PaginationModelObserver, is no-op at this level.
- *
- * <p>Will be called each time the viewArea of the model is changed. Should be overridden by any
- * subclasses that wish to perform actions when this occurs.
- */
- @Override
- public void onViewAreaChanged() {
- }
-}
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PageRangeHandler.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PageRangeHandler.java
index 8936f4e..ea3ece28 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PageRangeHandler.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PageRangeHandler.java
@@ -56,11 +56,6 @@
mMaxPage = maxPage;
}
- @NonNull
- public PaginationModel getPaginationModel() {
- return mPaginationModel;
- }
-
/**
* Returns the page currently roughly centered in the view.
*/
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PaginatedView.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PaginatedView.java
index f24d3da..3cdd35d 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PaginatedView.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PaginatedView.java
@@ -21,10 +21,12 @@
import android.util.AttributeSet;
import android.util.SparseArray;
import android.view.View;
+import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
+import androidx.annotation.VisibleForTesting;
import androidx.pdf.ViewState;
import androidx.pdf.data.Range;
import androidx.pdf.util.PaginationUtils;
@@ -45,14 +47,11 @@
*/
@RestrictTo(RestrictTo.Scope.LIBRARY)
@SuppressWarnings("WrongCall")
-public class PaginatedView extends AbstractPaginatedView {
-
- private static final String TAG = PaginatedView.class.getSimpleName();
-
+public class PaginatedView extends ViewGroup implements PaginationModelObserver {
/** Maps the current child views to pages. */
private final SparseArray<PageView> mPageViews = new SparseArray<>();
- private PaginationModel mPaginationModel;
+ private PaginationModel mModel;
private PageRangeHandler mPageRangeHandler;
@@ -68,6 +67,9 @@
private boolean mIsConfigurationChanged = false;
+ /** The current viewport in content coordinates */
+ private final Rect mViewArea = new Rect();
+
public PaginatedView(@NonNull Context context) {
this(context, null);
}
@@ -78,20 +80,112 @@
public PaginatedView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
-
+ mModel = new PaginationModel(context);
+ mPageRangeHandler = new PageRangeHandler(mModel);
}
- /** Instantiate PaginationModel and PageRangeHandler */
- @NonNull
- public PaginationModel initPaginationModelAndPageRangeHandler(@NonNull Context context) {
- mPaginationModel = new PaginationModel(context);
- mPageRangeHandler = new PageRangeHandler(mPaginationModel);
- return mPaginationModel;
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mModel.addObserver(this);
+ }
+
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ if (mPageRangeHandler != null) {
+ mPageRangeHandler.setVisiblePages(null);
+ }
+ mModel.removeObserver(this);
+ }
+
+ @VisibleForTesting
+ public void setModel(@NonNull PaginationModel model) {
+ mModel = model;
}
@NonNull
- public PaginationModel getPaginationModel() {
- return mPaginationModel;
+ public PaginationModel getModel() {
+ return mModel;
+ }
+
+ @NonNull
+ public PaginationModel resetModels() {
+ mModel = new PaginationModel(getContext());
+ mPageRangeHandler = new PageRangeHandler(mModel);
+ return mModel;
+ }
+
+ /** Requests a layout because this view has to grow now to accommodate the new page(s). */
+ @Override
+ public void onPageAdded() {
+ requestLayout();
+ }
+
+ protected boolean isInitialized() {
+ return mModel != null;
+ }
+
+ /**
+ * Measures this view in relation to the {@link #mModel} then asks all child views to measure
+ * themselves.
+ *
+ * <p>If the {@link #mModel} is not initialized, this view has nothing to display and will
+ * measure (0, 0). Otherwise, view will measure ({@link #mModel}'s width, {@link #mModel}'s
+ * estimated height).
+ */
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int width = 0;
+ int estimatedHeight = 0;
+
+ if (isInitialized()) {
+ width = mModel.getWidth();
+ estimatedHeight = mModel.getEstimatedFullHeight();
+ }
+
+ setMeasuredDimension(width, estimatedHeight);
+ measureChildren(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ /**
+ * Provides consistent layout behavior for subclasses.
+ *
+ * <p>Does not perform a layout if there aren't any child views. Otherwise asks the
+ * subclasses to
+ * layout each child by index.
+ */
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ int count = getChildCount();
+ if (count == 0) {
+ return;
+ }
+
+ for (int i = 0; i < count; i++) {
+ layoutChild(i);
+ }
+ }
+
+ /**
+ * Returns the current viewport in content coordinates
+ */
+ @NonNull
+ public Rect getViewArea() {
+ return mViewArea;
+ }
+
+ /**
+ * Updates the current viewport
+ *
+ * @param viewArea the viewport in content coordinates
+ */
+ public void setViewArea(@NonNull Rect viewArea) {
+ if (!viewArea.equals(this.mViewArea)) {
+ this.mViewArea.set(viewArea);
+ onViewAreaChanged();
+ }
}
@NonNull
@@ -120,7 +214,7 @@
@NonNull
public PdfSelectionHandles getSelectionHandles() {
- return mSelectionHandles;
+ return mSelectionHandles;
}
public void setSelectionHandles(@NonNull PdfSelectionHandles selectionHandles) {
@@ -243,10 +337,10 @@
*
* @param index the index of the child view in this ViewGroup
*/
- @Override
- protected void layoutChild(int index) {
+ private void layoutChild(int index) {
int pageNum = mPageViews.keyAt(index);
- Rect pageCoordinates = getModel().getPageLocation(pageNum);
+ Rect viewArea = getViewArea();
+ Rect pageCoordinates = getModel().getPageLocation(pageNum, viewArea);
PageView child = (PageView) getChildAt(index);
child
@@ -257,7 +351,6 @@
pageCoordinates.right,
pageCoordinates.bottom);
- Rect viewArea = getModel().getViewArea();
child
.getPageView()
.setViewArea(
@@ -275,22 +368,13 @@
}
/** Perform a layout when the viewArea of the {@code model} has changed. */
- @Override
- public void onViewAreaChanged() {
+ private void onViewAreaChanged() {
// We can't wait for the next layout pass, the pages will be drawn before.
// We could still optimize to skip the next layoutChild() calls for the pages that have been
// laid out already for this viewArea.
onLayout(false, getLeft(), getTop(), getRight(), getBottom());
}
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- if (mPageRangeHandler != null) {
- mPageRangeHandler.setVisiblePages(null);
- }
- }
-
/**
* Refreshes the page range for the visible area.
*/
@@ -334,7 +418,7 @@
if (getViewAt(pageNum) == null) {
mPageViewFactory.getOrCreatePageView(pageNum,
PaginationUtils.getPageElevationInPixels(getContext()),
- mPaginationModel.getPageSize(pageNum));
+ mModel.getPageSize(pageNum));
requiresLayoutPass = true;
}
}
@@ -384,7 +468,7 @@
PageMosaicView pageView = mPageViewFactory.getOrCreatePageView(
page,
PaginationUtils.getPageElevationInPixels(getContext()),
- mPaginationModel.getPageSize(page));
+ mModel.getPageSize(page));
pageView.clearTiles();
pageView.requestFastDrawAtZoom(stableZoom);
pageView.refreshPageContentAndOverlays();
@@ -396,7 +480,7 @@
PageMosaicView pageView = mPageViewFactory.getOrCreatePageView(
page,
PaginationUtils.getPageElevationInPixels(getContext()),
- mPaginationModel.getPageSize(page));
+ mModel.getPageSize(page));
pageView.requestDrawAtZoom(stableZoom);
pageView.refreshPageContentAndOverlays();
}
@@ -417,7 +501,7 @@
PageMosaicView pageView = mPageViewFactory.getOrCreatePageView(
page,
PaginationUtils.getPageElevationInPixels(getContext()),
- mPaginationModel.getPageSize(page));
+ mModel.getPageSize(page));
pageView.requestTiles();
}
}
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PaginationModel.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PaginationModel.java
index f11e659..8407a743 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PaginationModel.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PaginationModel.java
@@ -50,19 +50,14 @@
* <ol>
* <li>{@link #initialize(int)} with the number of pages it will contain.
* <li>{@link #addPage(int, Dimensions)} to set the dimensions for each page.
- * <li>{@link #setViewArea(Rect)} to report current visible area so pages can be moved
- * horizontally for maximum visibility.
* </ol>
*
* <p>This model is observable. Any classes implementing {@link PaginationModelObserver} can
* register themselves via {@link #addObserver(PaginationModelObserver)} and will be notified when
- * pages are added or the {@link #mViewArea} is changed.
+ * pages are added
*/
@RestrictTo(RestrictTo.Scope.LIBRARY)
public class PaginationModel {
-
- private static final String TAG = PaginationModel.class.getSimpleName();
-
/**
* The spacing added before and after each page (the actual space between 2 consecutive pages is
* twice this distance), in pixels.
@@ -86,22 +81,6 @@
private int mAccumulatedPageSize = 0;
- /**
- * The portion of this model that is currently (or last we knew) exposed on the screen.
- *
- * <p>In the co-ordinates of this model - so if this entire model is within the visible area,
- * then
- * {@code viewArea} will contain the rect Rect(0, 0, getWidth, getHeight). Current visible area
- * should be reported to this model via {@link #setViewArea(Rect)}.
- */
- private final Rect mViewArea = new Rect();
-
- /**
- * A temp working instance for computing {@link #mViewArea} to avoid excessive object
- * creation.
- */
- private final Rect mTempViewArea = new Rect();
-
private final Set<PaginationModelObserver> mObservers = new HashSet<>();
public PaginationModel(@NonNull Context context) {
@@ -284,21 +263,7 @@
mMaxPages - mSize + 1));
}
- /**
- * Updates the portion of this model that is visible on the screen, in this model's
- * coordinates -
- * so relative to (0, 0)-(getWidth(), getHeight()).
- */
- public void setViewArea(@NonNull Rect viewArea) {
- mTempViewArea.set(viewArea);
- if (!mTempViewArea.intersect(
- 0, 0, getWidth(), getEstimatedFullHeight())) { // Modifies tempViewArea.
- }
- if (!mTempViewArea.equals(this.mViewArea)) {
- this.mViewArea.set(mTempViewArea);
- notifyViewAreaChanged();
- }
- }
+
/**
* Returns the location of the page in the model.
@@ -313,10 +278,11 @@
* </ul>
*
* @param pageNum - index of requested page
+ * @param viewArea - the current viewport in content coordinates
* @return - coordinates of the page within this model
*/
@NonNull
- public Rect getPageLocation(int pageNum) {
+ public Rect getPageLocation(int pageNum, @NonNull Rect viewArea) {
int left = 0;
int right = getWidth();
int top = mPageStops[pageNum];
@@ -324,15 +290,15 @@
int width = mPages[pageNum].getWidth();
if (width < right - left) {
// this page is smaller than the view's width, it may slide left or right.
- if (width < mViewArea.width()) {
+ if (width < viewArea.width()) {
// page is smaller than the view: center (but respect min left margin)
- left = Math.max(left, mViewArea.left + (mViewArea.width() - width) / 2);
+ left = Math.max(left, viewArea.left + (viewArea.width() - width) / 2);
} else {
// page is larger than view: scroll proportionally between the margins.
- if (mViewArea.right > right) {
+ if (viewArea.right > right) {
left = right - width;
- } else if (mViewArea.left > left) {
- left = mViewArea.left * (right - width) / (right - mViewArea.width());
+ } else if (viewArea.left > left) {
+ left = viewArea.left * (right - width) / (right - viewArea.width());
}
}
right = left + width;
@@ -371,23 +337,6 @@
return mMaxPages;
}
- /**
- * Returns the intersection of this model and the last viewArea that was reported to this model
- * via {@link #setViewArea(Rect)}.
- */
- @NonNull
- public Rect getViewArea() {
- return mViewArea;
- }
-
- /** Notify all observers that the {@code viewArea} has changed. */
- private void notifyViewAreaChanged() {
- Iterator<PaginationModelObserver> iterator = iterator();
- while (iterator.hasNext()) {
- iterator.next().onViewAreaChanged();
- }
- }
-
/** Notify all observers that a page has been added to the model. */
private void notifyPageAdded() {
Iterator<PaginationModelObserver> iterator = iterator();
@@ -432,7 +381,7 @@
@NonNull
public Iterator<PaginationModelObserver> iterator() {
synchronized (mObservers) {
- return new ArrayList<PaginationModelObserver>(mObservers).iterator();
+ return new ArrayList<>(mObservers).iterator();
}
}
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PaginationModelObserver.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PaginationModelObserver.java
index 9619bfe..c404f42 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PaginationModelObserver.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PaginationModelObserver.java
@@ -35,13 +35,4 @@
* Implementations are free to use this information as desired.
*/
default void onPageAdded() {}
-
- /**
- * Notifies the implementation that the {@code viewArea} of the {@link PaginationModel} has
- * changed.
- *
- * <p>The {@link PaginationModel} does not enforce any implementation expectations.
- * Implementations are free to use this information as desired.
- */
- default void onViewAreaChanged() {}
}
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PdfViewer.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PdfViewer.java
index 0fe8777..02df1c8 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PdfViewer.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PdfViewer.java
@@ -229,7 +229,7 @@
mFindInFileView = mPdfViewer.findViewById(R.id.search);
mFastScrollView = mPdfViewer.findViewById(R.id.fast_scroll_view);
mPaginatedView = mPdfViewer.findViewById(R.id.pdf_view);
- mPaginationModel = mPaginatedView.getPaginationModel();
+ mPaginationModel = mPaginatedView.getModel();
mZoomView = mPdfViewer.findViewById(R.id.zoom_view);
mLoadingSpinner = mPdfViewer.findViewById(R.id.progress_indicator);
setUpEditFab();
@@ -279,7 +279,7 @@
new SearchQueryObserver(mPaginatedView);
mSearchModel.query().addObserver(mSearchQueryObserver);
- mSingleTapHandler = new SingleTapHandler(getContext(), mAnnotationButton,
+ mSingleTapHandler = new SingleTapHandler(getContext(), mAnnotationButton, mPaginatedView,
mFindInFileView, mZoomView, mSelectionModel, mPaginationModel, mLayoutHandler);
mPageViewFactory = new PageViewFactory(requireContext(), mPdfLoader,
mPaginatedView, mZoomView, mSingleTapHandler, mFindInFileView);
@@ -378,7 +378,6 @@
if (mPaginatedView != null) {
mPaginatedView.removeAllViews();
- mPaginationModel.removeObserver(mPaginatedView);
mPaginatedView = null;
}
@@ -627,8 +626,6 @@
mPaginatedView.getPageRangeHandler().setMaxPage(1);
if (viewState().get() != ViewState.NO_VIEW) {
mPaginationModel.initialize(numPages);
- mPaginatedView.setModel(mPaginationModel);
- mPaginationModel.addObserver(mPaginatedView);
mFastScrollView.setPaginationModel(mPaginationModel);
dismissPasswordDialog();
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/SingleTapHandler.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/SingleTapHandler.java
index 74f02a6..67cfb2d 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/SingleTapHandler.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/SingleTapHandler.java
@@ -38,6 +38,7 @@
public class SingleTapHandler {
private final Context mContext;
private final FloatingActionButton mFloatingActionButton;
+ private final PaginatedView mPaginatedView;
private final FindInFileView mFindInFileView;
private final ZoomView mZoomView;
private final PdfSelectionModel mPdfSelectionModel;
@@ -47,6 +48,7 @@
public SingleTapHandler(@NonNull Context context,
@NonNull FloatingActionButton floatingActionButton,
+ @NonNull PaginatedView paginatedView,
@NonNull FindInFileView findInFileView,
@NonNull ZoomView zoomView,
@NonNull PdfSelectionModel pdfSelectionModel,
@@ -54,6 +56,7 @@
@NonNull LayoutHandler layoutHandler) {
mContext = context;
mFloatingActionButton = floatingActionButton;
+ mPaginatedView = paginatedView;
mFindInFileView = findInFileView;
mZoomView = zoomView;
mPdfSelectionModel = pdfSelectionModel;
@@ -65,15 +68,14 @@
mIsAnnotationIntentResolvable = annotationIntentResolvable;
}
- /** */
public void handleSingleTapConfirmedEventOnPage(@NonNull MotionEvent event,
@NonNull PageMosaicView pageMosaicView) {
if (mIsAnnotationIntentResolvable) {
if (mFloatingActionButton.getVisibility() == View.GONE
&& mFindInFileView.getVisibility() == GONE) {
- mFloatingActionButton.setVisibility(View.VISIBLE);
+ mFloatingActionButton.show();
} else {
- mFloatingActionButton.setVisibility(View.GONE);
+ mFloatingActionButton.hide();
}
}
@@ -123,7 +125,8 @@
if (destination.getYCoordinate() != null) {
int pageY = (int) destination.getYCoordinate().floatValue();
- Rect pageRect = mPaginationModel.getPageLocation(destination.getPageNumber());
+ Rect pageRect = mPaginationModel.getPageLocation(destination.getPageNumber(),
+ mPaginatedView.getViewArea());
int x = pageRect.left + (pageRect.width() / 2);
int y = mPaginationModel.getLookAtY(destination.getPageNumber(), pageY);
// Zoom should match the width of the page.
@@ -155,7 +158,7 @@
return;
}
- Rect pageRect = mPaginationModel.getPageLocation(pageNum);
+ Rect pageRect = mPaginationModel.getPageLocation(pageNum, mPaginatedView.getViewArea());
int x = pageRect.left + (pageRect.width() / 2);
int y = pageRect.top + (pageRect.height() / 2);
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/ZoomScrollValueObserver.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/ZoomScrollValueObserver.java
index 4d83cc3..b266f91 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/ZoomScrollValueObserver.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/ZoomScrollValueObserver.java
@@ -16,7 +16,6 @@
package androidx.pdf.viewer;
-import android.animation.ValueAnimator;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
@@ -45,7 +44,6 @@
private final SelectionActionMode mSelectionActionMode;
private final ObservableValue<ViewState> mViewState;
- private static final int FAB_ANIMATION_DURATION = 200;
private boolean mIsPageScrollingUp;
public ZoomScrollValueObserver(@Nullable ZoomView zoomView,
@@ -69,13 +67,11 @@
@Override
public void onChange(@Nullable ZoomView.ZoomScroll oldPosition,
@Nullable ZoomView.ZoomScroll position) {
- if (mPaginatedView == null || !mPaginatedView.getPaginationModel().isInitialized()
- || position == null || mPaginatedView.getPaginationModel().getSize() == 0) {
+ if (mPaginatedView == null || !mPaginatedView.getModel().isInitialized()
+ || position == null || mPaginatedView.getModel().getSize() == 0) {
return;
}
- // Stop showing context menu if there is any change in zoom or scroll, resume only when
- // the new position is stable
- mSelectionActionMode.stopActionMode();
+
mZoomView.loadPageAssets(mLayoutHandler, mViewState);
if (oldPosition.scrollY > position.scrollY) {
@@ -84,11 +80,20 @@
mIsPageScrollingUp = false;
}
+ // Stop showing context menu if there is any change in zoom or scroll, resume only when
+ // the new position is stable
+ if (mPaginatedView.getSelectionModel().selection().get() != null) {
+ mSelectionActionMode.stopActionMode();
+ if (position.stable) {
+ setUpContextMenu();
+ }
+ }
+
if (mIsAnnotationIntentResolvable && !mPaginatedView.isConfigurationChanged()) {
if (!isAnnotationButtonVisible() && position.scrollY == 0
&& mFindInFileView.getVisibility() == View.GONE) {
- editFabExpandAnimation();
+ mAnnotationButton.show();
} else if (isAnnotationButtonVisible() && mIsPageScrollingUp) {
clearAnnotationHandler();
return;
@@ -98,16 +103,7 @@
@Override
public void run() {
if (position.scrollY != 0) {
- mAnnotationButton.animate()
- .alpha(0.0f)
- .setDuration(FAB_ANIMATION_DURATION)
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- mAnnotationButton.setVisibility(View.GONE);
- mAnnotationButton.setAlpha(1.0f);
- }
- });
+ mAnnotationButton.hide();
}
}
});
@@ -116,35 +112,12 @@
&& position.scrollY != oldPosition.scrollY) {
mPaginatedView.setConfigurationChanged(false);
}
-
- if (mPaginatedView.getSelectionModel().selection().get() != null && position.stable) {
- setUpContextMenu();
- }
}
private boolean isAnnotationButtonVisible() {
return mAnnotationButton.getVisibility() == View.VISIBLE;
}
- private void editFabExpandAnimation() {
- mAnnotationButton.setScaleX(0.0f);
- mAnnotationButton.setScaleY(0.0f);
- ValueAnimator scaleAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
- scaleAnimator.setDuration(FAB_ANIMATION_DURATION);
- scaleAnimator.addUpdateListener(
- new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(@NonNull ValueAnimator animation) {
- float scale = (float) animation.getAnimatedValue();
- mAnnotationButton.setScaleX(scale);
- mAnnotationButton.setScaleY(scale);
- mAnnotationButton.setAlpha(scale);
- }
- });
- scaleAnimator.start();
- mAnnotationButton.setVisibility(View.VISIBLE);
- }
-
/** Exposing a function to clear the handler when PDFViewer Fragment is destroyed. */
public void clearAnnotationHandler() {
mAnnotationButtonHandler.removeCallbacksAndMessages(null);
@@ -167,16 +140,16 @@
if (selectionPage >= firstPageInVisibleRange
&& selectionPage <= lastPageInVisisbleRange) {
// Start and stop coordinates in a page wrt pagination model
- int startX = mPaginatedView.getPaginationModel().getLookAtX(selectionPage,
+ int startX = mPaginatedView.getModel().getLookAtX(selectionPage,
mPaginatedView.getSelectionModel().selection().get().getStart().getX());
- int startY = mPaginatedView.getPaginationModel().getLookAtY(selectionPage,
+ int startY = mPaginatedView.getModel().getLookAtY(selectionPage,
mPaginatedView.getSelectionModel().selection().get().getStart().getY());
- int stopX = mPaginatedView.getPaginationModel().getLookAtX(selectionPage,
+ int stopX = mPaginatedView.getModel().getLookAtX(selectionPage,
mPaginatedView.getSelectionModel().selection().get().getStop().getX());
- int stopY = mPaginatedView.getPaginationModel().getLookAtY(selectionPage,
+ int stopY = mPaginatedView.getModel().getLookAtY(selectionPage,
mPaginatedView.getSelectionModel().selection().get().getStop().getY());
- Rect currentViewArea = mPaginatedView.getPaginationModel().getViewArea();
+ Rect currentViewArea = mPaginatedView.getViewArea();
if (currentViewArea.intersect(startX, startY, stopX, stopY)) {
mSelectionActionMode.resume();
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfLoaderCallbacksImpl.kt b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfLoaderCallbacksImpl.kt
index f614d30..b5fbe27 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfLoaderCallbacksImpl.kt
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfLoaderCallbacksImpl.kt
@@ -117,28 +117,27 @@
return
}
- if (selection.page >= paginatedView.paginationModel.size) {
+ if (selection.page >= paginatedView.model.size) {
layoutHandler!!.layoutPages(selection.page + 1)
return
}
val rect = selection.pageMatches.getFirstRect(selection.selected)
- val x: Int = paginatedView.paginationModel.getLookAtX(selection.page, rect.centerX())
- val y: Int = paginatedView.paginationModel.getLookAtY(selection.page, rect.centerY())
+ val x: Int = paginatedView.model.getLookAtX(selection.page, rect.centerX())
+ val y: Int = paginatedView.model.getLookAtY(selection.page, rect.centerY())
zoomView.centerAt(x.toFloat(), y.toFloat())
pageViewFactory!!
.getOrCreatePageView(
selection.page,
pageElevationInPixels,
- paginatedView.paginationModel.getPageSize(selection.page)
+ paginatedView.model.getPageSize(selection.page)
)
.setOverlay(selection.overlay)
}
private fun isPageCreated(pageNum: Int): Boolean {
- return pageNum < paginatedView.paginationModel.size &&
- paginatedView.getViewAt(pageNum) != null
+ return pageNum < paginatedView.model.size && paginatedView.getViewAt(pageNum) != null
}
private fun getPage(pageNum: Int): PageViewFactory.PageView? {
@@ -198,16 +197,12 @@
paginatedView.pageRangeHandler.maxPage = 1
if (viewState.get() != ViewState.NO_VIEW) {
if (uri != null && data.uri == uri) {
- paginatedView.paginationModel.setMaxPages(-1)
+ paginatedView.model.setMaxPages(-1)
}
- paginatedView.paginationModel.initialize(numPages)
+ paginatedView.model.initialize(numPages)
- // Add pagination model to the view
- paginatedView.model = paginatedView.paginationModel
- paginatedView.let { paginatedView.paginationModel.addObserver(it) }
-
- fastScrollView.setPaginationModel(paginatedView.paginationModel)
+ fastScrollView.setPaginationModel(paginatedView.model)
dismissPasswordDialog()
@@ -249,12 +244,12 @@
override fun pageBroken(page: Int) {
if (viewState.get() != ViewState.NO_VIEW) {
- if (page < paginatedView.paginationModel.numPages) {
+ if (page < paginatedView.model.numPages) {
pageViewFactory!!
.getOrCreatePageView(
page,
pageElevationInPixels,
- paginatedView.paginationModel.getPageSize(page)
+ paginatedView.model.getPageSize(page)
)
.setFailure(context.resources.getString(R.string.error_on_page, page + 1))
// TODO: Track render error.
@@ -265,9 +260,9 @@
override fun setPageDimensions(pageNum: Int, dimensions: Dimensions) {
if (viewState.get() != ViewState.NO_VIEW) {
- paginatedView.paginationModel.addPage(pageNum, dimensions)
+ paginatedView.model.addPage(pageNum, dimensions)
- layoutHandler!!.pageLayoutReach = paginatedView.paginationModel.size
+ layoutHandler!!.pageLayoutReach = paginatedView.model.size
if (
searchModel!!.query().get() != null &&
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/password/PasswordDialog.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/password/PasswordDialog.java
index 92636fa..a994a5d 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/password/PasswordDialog.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/password/PasswordDialog.java
@@ -185,7 +185,7 @@
public void onStart() {
super.onStart();
mTextDefaultColor = getResources().getColor(R.color.pdf_viewer_color_on_surface);
- mTextErrorColor = getResources().getColor(R.color.pdf_viewer_color_on_error);
+ mTextErrorColor = getResources().getColor(R.color.pdf_viewer_color_error);
mBlueColor = getResources().getColor(R.color.pdf_viewer_color_primary);
EditText textField = (EditText) getDialog().findViewById(R.id.password);
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/widget/ZoomView.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/widget/ZoomView.java
index d7755ef..e5a021c 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/widget/ZoomView.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/widget/ZoomView.java
@@ -902,7 +902,7 @@
PaginatedView paginatedView = this.findViewById(R.id.pdf_view);
ZoomScroll position = this.zoomScroll().get();
- if (position == null || !paginatedView.getPaginationModel().isInitialized()) {
+ if (position == null || !paginatedView.getModel().isInitialized()) {
return;
}
@@ -911,7 +911,7 @@
this.setStableZoom(position.zoom);
}
- paginatedView.getPaginationModel().setViewArea(this.getVisibleAreaInContentCoords());
+ paginatedView.setViewArea(this.getVisibleAreaInContentCoords());
paginatedView.refreshPageRangeInVisibleArea(position, this.getHeight());
paginatedView.handleGonePages(false);
paginatedView.loadInvisibleNearPageRange(this.getStableZoom());
diff --git a/pdf/pdf-viewer/src/main/res/values-af/strings.xml b/pdf/pdf-viewer/src/main/res/values-af/strings.xml
index 269c461..3efa5d2 100644
--- a/pdf/pdf-viewer/src/main/res/values-af/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-af/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Kon nie die lêer oopmaak nie. Moontlike toestemmingkwessie?"</string>
<string name="page_broken" msgid="2968770793669433462">"Bladsy is vir die PDF-dokument gebreek"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Onvoldoende data om die PDF-dokument te verwerk"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-am/strings.xml b/pdf/pdf-viewer/src/main/res/values-am/strings.xml
index 517f867..a2679692 100644
--- a/pdf/pdf-viewer/src/main/res/values-am/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-am/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"ፋይሉን መክፈት አልተሳካም። የፈቃድ ችግር ሊሆን ይችላል?"</string>
<string name="page_broken" msgid="2968770793669433462">"ለPDF ሰነዱ ገፅ ተበላሽቷል"</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF ሰነዱን ለማሰናዳት በቂ ያልሆነ ውሂብ"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-ar/strings.xml b/pdf/pdf-viewer/src/main/res/values-ar/strings.xml
index 3b33c01..eae5948 100644
--- a/pdf/pdf-viewer/src/main/res/values-ar/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ar/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"تعذّر فتح الملف. هل توجد مشكلة محتملة في الأذونات؟"</string>
<string name="page_broken" msgid="2968770793669433462">"تعذّر تحميل صفحة من مستند PDF"</string>
<string name="needs_more_data" msgid="3520133467908240802">"البيانات غير كافية لمعالجة مستند PDF"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-as/strings.xml b/pdf/pdf-viewer/src/main/res/values-as/strings.xml
index 6aff8a9eb..f9c9ef7 100644
--- a/pdf/pdf-viewer/src/main/res/values-as/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-as/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"ফাইলটো খুলিব পৰা নগ’ল। সম্ভাব্য অনুমতি সম্পৰ্কীয় সমস্যা?"</string>
<string name="page_broken" msgid="2968770793669433462">"PDF নথিৰ বাবে পৃষ্ঠাখন বিসংগতিপূৰ্ণ"</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF নথিখন প্ৰক্ৰিয়াকৰণ কৰিবলৈ অপৰ্যাপ্ত ডেটা"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-az/strings.xml b/pdf/pdf-viewer/src/main/res/values-az/strings.xml
index 622b95f..6ef1751 100644
--- a/pdf/pdf-viewer/src/main/res/values-az/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-az/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Fayl açılmadı. İcazə problemi var?"</string>
<string name="page_broken" msgid="2968770793669433462">"PDF sənədi üçün səhifədə xəta var"</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF sənədini emal etmək üçün kifayət qədər data yoxdur"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-b+sr+Latn/strings.xml b/pdf/pdf-viewer/src/main/res/values-b+sr+Latn/strings.xml
index 0495044..f259654 100644
--- a/pdf/pdf-viewer/src/main/res/values-b+sr+Latn/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-b+sr+Latn/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Otvaranje fajla nije uspelo. Možda postoje problemi sa dozvolom?"</string>
<string name="page_broken" msgid="2968770793669433462">"Neispravna stranica za PDF dokument"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Nedovoljno podataka za obradu PDF dokumenta"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-be/strings.xml b/pdf/pdf-viewer/src/main/res/values-be/strings.xml
index 6d25879..c951b2c 100644
--- a/pdf/pdf-viewer/src/main/res/values-be/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-be/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Не ўдалося адкрыць файл. Магчыма, праблема з дазволам?"</string>
<string name="page_broken" msgid="2968770793669433462">"Старонка дакумента PDF пашкоджана"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Не хапае даных для апрацоўкі дакумента PDF"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-bg/strings.xml b/pdf/pdf-viewer/src/main/res/values-bg/strings.xml
index 37f6840..004be66 100644
--- a/pdf/pdf-viewer/src/main/res/values-bg/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-bg/strings.xml
@@ -53,4 +53,5 @@
<string name="file_error" msgid="4003885928556884091">"Файлът не бе отворен. Възможно е да има проблем с разрешенията."</string>
<string name="page_broken" msgid="2968770793669433462">"Невалидна страница в PDF документа"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Няма достатъчно данни за обработването на PDF документа"</string>
+ <string name="error_cannot_open_pdf" msgid="2361919778558145071">"PDF файлът не може да се отвори"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-bn/strings.xml b/pdf/pdf-viewer/src/main/res/values-bn/strings.xml
index a65a18c..937be09 100644
--- a/pdf/pdf-viewer/src/main/res/values-bn/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-bn/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"ফাইল খোলা যায়নি। অনুমতি সংক্রান্ত সমস্যার কারণে এটি হতে পারে?"</string>
<string name="page_broken" msgid="2968770793669433462">"পিডিএফ ডকুমেন্টের ক্ষেত্রে পৃষ্ঠা ভেঙে গেছে"</string>
<string name="needs_more_data" msgid="3520133467908240802">"পিডিএফ ডকুমেন্ট প্রসেস করার জন্য যথেষ্ট ডেটা নেই"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-bs/strings.xml b/pdf/pdf-viewer/src/main/res/values-bs/strings.xml
index 642b643..cd49b7a 100644
--- a/pdf/pdf-viewer/src/main/res/values-bs/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-bs/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Otvaranje fajla nije uspjelo. Možda postoji problem s odobrenjem?"</string>
<string name="page_broken" msgid="2968770793669433462">"Stranica je prelomljena za PDF dokument"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Nema dovoljno podataka za obradu PDF dokumenta"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-ca/strings.xml b/pdf/pdf-viewer/src/main/res/values-ca/strings.xml
index 9338452..4f3deac 100644
--- a/pdf/pdf-viewer/src/main/res/values-ca/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ca/strings.xml
@@ -53,4 +53,5 @@
<string name="file_error" msgid="4003885928556884091">"No s\'ha pogut obrir el fitxer. És possible que hi hagi un problema de permisos?"</string>
<string name="page_broken" msgid="2968770793669433462">"La pàgina no funciona per al document PDF"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Les dades són insuficients per processar el document PDF"</string>
+ <string name="error_cannot_open_pdf" msgid="2361919778558145071">"No es pot obrir el fitxer PDF"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-cs/strings.xml b/pdf/pdf-viewer/src/main/res/values-cs/strings.xml
index 07d4e4e..cdc4b65 100644
--- a/pdf/pdf-viewer/src/main/res/values-cs/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-cs/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Soubor se nepodařilo otevřít. Může se jednat o problém s oprávněním."</string>
<string name="page_broken" msgid="2968770793669433462">"Dokument PDF obsahuje poškozenou stránku"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Nedostatek dat ke zpracování dokumentu PDF"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-da/strings.xml b/pdf/pdf-viewer/src/main/res/values-da/strings.xml
index 9ad79da..5bcd02c 100644
--- a/pdf/pdf-viewer/src/main/res/values-da/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-da/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Filen kunne ikke åbnes Mon der er et problem med tilladelserne?"</string>
<string name="page_broken" msgid="2968770793669433462">"Siden er ødelagt for PDF-dokumentet"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Der er ikke nok data til at behandle PDF-dokumentet"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-de/strings.xml b/pdf/pdf-viewer/src/main/res/values-de/strings.xml
index 45bac2a..72b1096 100644
--- a/pdf/pdf-viewer/src/main/res/values-de/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-de/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Datei konnte nicht geöffnet werden. Möglicherweise ein Berechtigungsproblem?"</string>
<string name="page_broken" msgid="2968770793669433462">"Seite für PDF-Dokument ist fehlerhaft"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Keine ausreichenden Daten, um das PDF-Dokument zu verarbeiten"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-el/strings.xml b/pdf/pdf-viewer/src/main/res/values-el/strings.xml
index e87a5b8..ada33d8 100644
--- a/pdf/pdf-viewer/src/main/res/values-el/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-el/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Δεν ήταν δυνατό το άνοιγμα του αρχείου. Μήπως υπάρχει κάποιο πρόβλημα με την άδεια;"</string>
<string name="page_broken" msgid="2968770793669433462">"Δεν ήταν δυνατή η φόρτωση του εγγράφου PDF από τη σελίδα"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Μη επαρκή δεδομένα για την επεξεργασία του εγγράφου PDF"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-en-rAU/strings.xml b/pdf/pdf-viewer/src/main/res/values-en-rAU/strings.xml
index 62dcae9..2f9e726 100644
--- a/pdf/pdf-viewer/src/main/res/values-en-rAU/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-en-rAU/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Failed to open the file. Possible permission issue?"</string>
<string name="page_broken" msgid="2968770793669433462">"Page broken for the PDF document"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Insufficient data for processing the PDF document"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-en-rCA/strings.xml b/pdf/pdf-viewer/src/main/res/values-en-rCA/strings.xml
index 5a09605..66f8a08 100644
--- a/pdf/pdf-viewer/src/main/res/values-en-rCA/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-en-rCA/strings.xml
@@ -53,4 +53,5 @@
<string name="file_error" msgid="4003885928556884091">"Failed to open the file. Possible permission issue?"</string>
<string name="page_broken" msgid="2968770793669433462">"Page broken for the PDF document"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Insufficient data for processing the PDF document"</string>
+ <string name="error_cannot_open_pdf" msgid="2361919778558145071">"Can\'t open PDF file"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-en-rGB/strings.xml b/pdf/pdf-viewer/src/main/res/values-en-rGB/strings.xml
index 62dcae9..2f9e726 100644
--- a/pdf/pdf-viewer/src/main/res/values-en-rGB/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-en-rGB/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Failed to open the file. Possible permission issue?"</string>
<string name="page_broken" msgid="2968770793669433462">"Page broken for the PDF document"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Insufficient data for processing the PDF document"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-en-rIN/strings.xml b/pdf/pdf-viewer/src/main/res/values-en-rIN/strings.xml
index 62dcae9..2f9e726 100644
--- a/pdf/pdf-viewer/src/main/res/values-en-rIN/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-en-rIN/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Failed to open the file. Possible permission issue?"</string>
<string name="page_broken" msgid="2968770793669433462">"Page broken for the PDF document"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Insufficient data for processing the PDF document"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-en-rXC/strings.xml b/pdf/pdf-viewer/src/main/res/values-en-rXC/strings.xml
index d2bf4b8..f0cfb06 100644
--- a/pdf/pdf-viewer/src/main/res/values-en-rXC/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-en-rXC/strings.xml
@@ -53,4 +53,5 @@
<string name="file_error" msgid="4003885928556884091">"Failed to open the file. Possible permission issue?"</string>
<string name="page_broken" msgid="2968770793669433462">"Page broken for the PDF document"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Insufficient data for processing the PDF document"</string>
+ <string name="error_cannot_open_pdf" msgid="2361919778558145071">"Can\'t open PDF file"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-es-rUS/strings.xml b/pdf/pdf-viewer/src/main/res/values-es-rUS/strings.xml
index 4ad89b2..388c071 100644
--- a/pdf/pdf-viewer/src/main/res/values-es-rUS/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-es-rUS/strings.xml
@@ -42,12 +42,9 @@
<string name="desc_page_range" msgid="5286496438609641577">"páginas <xliff:g id="FIRST">%1$d</xliff:g> a <xliff:g id="LAST">%2$d</xliff:g> de <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_image_alt_text" msgid="7700601988820586333">"Imagen: <xliff:g id="ALT_TEXT">%1$s</xliff:g>"</string>
<string name="hint_find" msgid="5385388836603550565">"Buscar en el archivo"</string>
- <!-- no translation found for previous_button_description (1169511027880317546) -->
- <skip />
- <!-- no translation found for next_button_description (4702699322249103693) -->
- <skip />
- <!-- no translation found for close_button_description (7379823906921067675) -->
- <skip />
+ <string name="previous_button_description" msgid="1169511027880317546">"Anterior"</string>
+ <string name="next_button_description" msgid="4702699322249103693">"Siguiente"</string>
+ <string name="close_button_description" msgid="7379823906921067675">"Cerrar"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="action_edit" msgid="5882082700509010966">"Editar el archivo"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Ingresa la contraseña para desbloquear"</string>
@@ -56,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"No se pudo abrir el archivo. ¿Puede que se deba a un problema de permisos?"</string>
<string name="page_broken" msgid="2968770793669433462">"La página no funciona para el documento PDF"</string>
<string name="needs_more_data" msgid="3520133467908240802">"No hay datos suficientes para procesar el documento PDF"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-es/strings.xml b/pdf/pdf-viewer/src/main/res/values-es/strings.xml
index d08f531..b1aca5f 100644
--- a/pdf/pdf-viewer/src/main/res/values-es/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-es/strings.xml
@@ -42,12 +42,9 @@
<string name="desc_page_range" msgid="5286496438609641577">"páginas <xliff:g id="FIRST">%1$d</xliff:g> a <xliff:g id="LAST">%2$d</xliff:g> de <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
<string name="desc_image_alt_text" msgid="7700601988820586333">"Imagen: <xliff:g id="ALT_TEXT">%1$s</xliff:g>"</string>
<string name="hint_find" msgid="5385388836603550565">"Buscar en el archivo"</string>
- <!-- no translation found for previous_button_description (1169511027880317546) -->
- <skip />
- <!-- no translation found for next_button_description (4702699322249103693) -->
- <skip />
- <!-- no translation found for close_button_description (7379823906921067675) -->
- <skip />
+ <string name="previous_button_description" msgid="1169511027880317546">"Anterior"</string>
+ <string name="next_button_description" msgid="4702699322249103693">"Siguiente"</string>
+ <string name="close_button_description" msgid="7379823906921067675">"Cerrar"</string>
<string name="message_match_status" msgid="6288242289981639727">"<xliff:g id="POSITION">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="action_edit" msgid="5882082700509010966">"Editar archivo"</string>
<string name="password_not_entered" msgid="8875370870743585303">"Introduce la contraseña para desbloquear"</string>
@@ -56,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"No se ha podido abrir el archivo. ¿Puede que se deba a un problema de permisos?"</string>
<string name="page_broken" msgid="2968770793669433462">"La página no funciona para el documento PDF"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Datos insuficientes para procesar el documento PDF"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-et/strings.xml b/pdf/pdf-viewer/src/main/res/values-et/strings.xml
index 902cdd0..d640ef5 100644
--- a/pdf/pdf-viewer/src/main/res/values-et/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-et/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Faili avamine nurjus. Probleem võib olla seotud lubadega."</string>
<string name="page_broken" msgid="2968770793669433462">"Rikutud leht PDF-dokumendis"</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF-dokumendi töötlemiseks pole piisavalt andmeid"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-eu/strings.xml b/pdf/pdf-viewer/src/main/res/values-eu/strings.xml
index 239b72c..20770aa 100644
--- a/pdf/pdf-viewer/src/main/res/values-eu/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-eu/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Ezin izan da ireki fitxategia. Agian ez duzu horretarako baimenik?"</string>
<string name="page_broken" msgid="2968770793669433462">"PDF dokumentuaren orria hondatuta dago"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Ez dago behar adina daturik PDF dokumentua prozesatzeko"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-fa/strings.xml b/pdf/pdf-viewer/src/main/res/values-fa/strings.xml
index 42aa1cd..0669b17 100644
--- a/pdf/pdf-viewer/src/main/res/values-fa/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-fa/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"فایل باز نشد. احتمالاً مشکلی در اجازه وجود دارد؟"</string>
<string name="page_broken" msgid="2968770793669433462">"صفحه سند PDF خراب است"</string>
<string name="needs_more_data" msgid="3520133467908240802">"دادهها برای پردازش سند PDF کافی نیست"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-fi/strings.xml b/pdf/pdf-viewer/src/main/res/values-fi/strings.xml
index cdce79c..3518c19 100644
--- a/pdf/pdf-viewer/src/main/res/values-fi/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-fi/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Tiedoston avaaminen epäonnistui. Mahdollinen lupaan liittyvä ongelma?"</string>
<string name="page_broken" msgid="2968770793669433462">"PDF-dokumenttiin liittyvä sivu on rikki"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Riittämätön data PDF-dokumentin käsittelyyn"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-fr-rCA/strings.xml b/pdf/pdf-viewer/src/main/res/values-fr-rCA/strings.xml
index 453aacf..8a25992 100644
--- a/pdf/pdf-viewer/src/main/res/values-fr-rCA/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-fr-rCA/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Échec de l\'ouverture du fichier. Problème d\'autorisation éventuel?"</string>
<string name="page_broken" msgid="2968770793669433462">"Page brisée pour le document PDF"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Données insuffisantes pour le traitement du document PDF"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-fr/strings.xml b/pdf/pdf-viewer/src/main/res/values-fr/strings.xml
index cffc34d..dd5fcad 100644
--- a/pdf/pdf-viewer/src/main/res/values-fr/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-fr/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Échec de l\'ouverture du fichier. Problème d\'autorisation possible ?"</string>
<string name="page_broken" msgid="2968770793669433462">"Page non fonctionnelle pour le document PDF"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Données insuffisantes pour le traitement du document PDF"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-gl/strings.xml b/pdf/pdf-viewer/src/main/res/values-gl/strings.xml
index 67960e4..ebe0816 100644
--- a/pdf/pdf-viewer/src/main/res/values-gl/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-gl/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Produciuse un erro ao abrir o ficheiro. É posible que haxa problemas co permiso?"</string>
<string name="page_broken" msgid="2968770793669433462">"Non funciona a páxina para o documento PDF"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Os datos non son suficientes para procesar o documento PDF"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-gu/strings.xml b/pdf/pdf-viewer/src/main/res/values-gu/strings.xml
index f0fe5ae..ae95929 100644
--- a/pdf/pdf-viewer/src/main/res/values-gu/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-gu/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"ફાઇલ ખોલવામાં નિષ્ફળ રહ્યાં. શું તમારી પાસે આની પરવાનગી નથી?"</string>
<string name="page_broken" msgid="2968770793669433462">"PDF દસ્તાવેજ માટે પેજ લોડ થઈ રહ્યું નથી"</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF દસ્તાવેજ પર પ્રક્રિયા કરવા માટે પર્યાપ્ત ડેટા નથી"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-hi/strings.xml b/pdf/pdf-viewer/src/main/res/values-hi/strings.xml
index d1fe782..49f8c1d 100644
--- a/pdf/pdf-viewer/src/main/res/values-hi/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-hi/strings.xml
@@ -53,4 +53,5 @@
<string name="file_error" msgid="4003885928556884091">"फ़ाइल नहीं खोली जा सकी. क्या आपके पास इसकी अनुमति नहीं है?"</string>
<string name="page_broken" msgid="2968770793669433462">"PDF दस्तावेज़ के लिए पेज लोड नहीं हो रहा है"</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF दस्तावेज़ को प्रोसेस करने के लिए, ज़रूरत के मुताबिक डेटा नहीं है"</string>
+ <string name="error_cannot_open_pdf" msgid="2361919778558145071">"PDF फ़ाइल नहीं खोली जा सकी"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-hr/strings.xml b/pdf/pdf-viewer/src/main/res/values-hr/strings.xml
index 4605373..7248c6ee 100644
--- a/pdf/pdf-viewer/src/main/res/values-hr/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-hr/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Otvaranje datoteke nije uspjelo. Možda postoji problem s dopuštenjem?"</string>
<string name="page_broken" msgid="2968770793669433462">"Stranica je raščlanjena za PDF dokument"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Nema dovoljno podataka za obradu PDF dokumenta"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-hu/strings.xml b/pdf/pdf-viewer/src/main/res/values-hu/strings.xml
index a733547..f63a921 100644
--- a/pdf/pdf-viewer/src/main/res/values-hu/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-hu/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Nem sikerült megnyitni a fájlt. Engedéllyel kapcsolatos problémáról lehet szó?"</string>
<string name="page_broken" msgid="2968770793669433462">"Az oldal nem tölt be a PDF-dokumentumban"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Nem áll rendelkezésre elegendő adat a PDF-dokumentum feldolgozásához"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-hy/strings.xml b/pdf/pdf-viewer/src/main/res/values-hy/strings.xml
index 901715c..71d24c2 100644
--- a/pdf/pdf-viewer/src/main/res/values-hy/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-hy/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Չհաջողվեց բացել ֆայլը։ Գուցե թույլտվության հետ կապված խնդի՞ր է։"</string>
<string name="page_broken" msgid="2968770793669433462">"PDF փաստաթղթի էջը վնասված է"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Ոչ բավարար տվյալներ PDF փաստաթղթի մշակման համար"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-in/strings.xml b/pdf/pdf-viewer/src/main/res/values-in/strings.xml
index fa9c752..3c9e624 100644
--- a/pdf/pdf-viewer/src/main/res/values-in/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-in/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Gagal membuka file. Kemungkinan masalah izin?"</string>
<string name="page_broken" msgid="2968770793669433462">"Halaman dokumen PDF rusak"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Data tidak cukup untuk memproses dokumen PDF"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-is/strings.xml b/pdf/pdf-viewer/src/main/res/values-is/strings.xml
index 9716ab1..9378e05 100644
--- a/pdf/pdf-viewer/src/main/res/values-is/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-is/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Ekki tókst að opna skrána. Hugsanlega vandamál tengt heimildum?"</string>
<string name="page_broken" msgid="2968770793669433462">"Síða í PDF-skjali er gölluð"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Ekki næg gögn fyrir úrvinnslu á PDF-skjali"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-it/strings.xml b/pdf/pdf-viewer/src/main/res/values-it/strings.xml
index 564eecd..89a8d17 100644
--- a/pdf/pdf-viewer/src/main/res/values-it/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-it/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Impossibile aprire il file. Possibile problema di autorizzazione?"</string>
<string name="page_broken" msgid="2968770793669433462">"Pagina inaccessibile per il documento PDF"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Dati insufficienti per l\'elaborazione del documento PDF"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-iw/strings.xml b/pdf/pdf-viewer/src/main/res/values-iw/strings.xml
index 6ce9431..3dfd510 100644
--- a/pdf/pdf-viewer/src/main/res/values-iw/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-iw/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"לא ניתן לפתוח את הקובץ. יכול להיות שיש בעיה בהרשאה."</string>
<string name="page_broken" msgid="2968770793669433462">"קישור מנותק בדף למסמך ה-PDF"</string>
<string name="needs_more_data" msgid="3520133467908240802">"אין מספיק נתונים כדי לעבד את מסמך ה-PDF"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-ja/strings.xml b/pdf/pdf-viewer/src/main/res/values-ja/strings.xml
index f03d62a..a48cc46 100644
--- a/pdf/pdf-viewer/src/main/res/values-ja/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ja/strings.xml
@@ -53,4 +53,5 @@
<string name="file_error" msgid="4003885928556884091">"ファイルを開けませんでした。権限に問題がある可能性はありませんか?"</string>
<string name="page_broken" msgid="2968770793669433462">"PDF ドキュメントのページが壊れています"</string>
<string name="needs_more_data" msgid="3520133467908240802">"データ不足のため PDF ドキュメントを処理できません"</string>
+ <string name="error_cannot_open_pdf" msgid="2361919778558145071">"PDF ファイルを開けません"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-ka/strings.xml b/pdf/pdf-viewer/src/main/res/values-ka/strings.xml
index 864fd74..3fd5c9b 100644
--- a/pdf/pdf-viewer/src/main/res/values-ka/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ka/strings.xml
@@ -53,4 +53,5 @@
<string name="file_error" msgid="4003885928556884091">"ფაილის გახსნა ვერ მოხერხდა. შესაძლოა ნებართვის პრობლემა იყოს?"</string>
<string name="page_broken" msgid="2968770793669433462">"PDF დოკუმენტის გვერდი დაზიანებულია"</string>
<string name="needs_more_data" msgid="3520133467908240802">"მონაცემები არ არის საკმარისი PDF დოკუმენტის დასამუშავებლად"</string>
+ <string name="error_cannot_open_pdf" msgid="2361919778558145071">"PDF ფაილის გახსნა ვერ ხერხდება"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-kk/strings.xml b/pdf/pdf-viewer/src/main/res/values-kk/strings.xml
index b94ea34..5846202 100644
--- a/pdf/pdf-viewer/src/main/res/values-kk/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-kk/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Файл ашылмады. Бәлкім, рұқсатқа қатысты бір мәселе бар?"</string>
<string name="page_broken" msgid="2968770793669433462">"PDF құжатының беті бұзылған."</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF құжатын өңдеу үшін деректер жеткіліксіз."</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-km/strings.xml b/pdf/pdf-viewer/src/main/res/values-km/strings.xml
index 03a0ebd..931c43f 100644
--- a/pdf/pdf-viewer/src/main/res/values-km/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-km/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"មិនអាចបើកឯកសារនេះបានទេ។ អាចមានបញ្ហានៃការអនុញ្ញាតឬ?"</string>
<string name="page_broken" msgid="2968770793669433462">"ទំព័រមិនដំណើរការសម្រាប់ឯកសារ PDF"</string>
<string name="needs_more_data" msgid="3520133467908240802">"មានទិន្នន័យមិនគ្រប់គ្រាន់សម្រាប់ដំណើរការឯកសារ PDF"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-kn/strings.xml b/pdf/pdf-viewer/src/main/res/values-kn/strings.xml
index 4c114f7..fb8b009 100644
--- a/pdf/pdf-viewer/src/main/res/values-kn/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-kn/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"ಫೈಲ್ ತೆರೆಯಲು ವಿಫಲವಾಗಿದೆ. ಸಂಭವನೀಯ ಅನುಮತಿ ಸಮಸ್ಯೆ?"</string>
<string name="page_broken" msgid="2968770793669433462">"PDF ಡಾಕ್ಯುಮೆಂಟ್ಗೆ ಸಂಬಂಧಿಸಿದ ಪುಟ ಮುರಿದಿದೆ"</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF ಡಾಕ್ಯುಮೆಂಟ್ ಅನ್ನು ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲು ಸಾಕಷ್ಟು ಡೇಟಾ ಇಲ್ಲ"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-ko/strings.xml b/pdf/pdf-viewer/src/main/res/values-ko/strings.xml
index 54655ec..9530691 100644
--- a/pdf/pdf-viewer/src/main/res/values-ko/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ko/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"파일을 열 수 없습니다. 권한 문제가 있을 수 있나요?"</string>
<string name="page_broken" msgid="2968770793669433462">"PDF 문서의 페이지가 손상되었습니다."</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF 문서 처리를 위한 데이터가 부족합니다."</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-ky/strings.xml b/pdf/pdf-viewer/src/main/res/values-ky/strings.xml
index 90e6450..92e5119 100644
--- a/pdf/pdf-viewer/src/main/res/values-ky/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ky/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Файл ачылган жок. Керектүү уруксаттар жок окшойт."</string>
<string name="page_broken" msgid="2968770793669433462">"PDF документинин барагы бузук"</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF документин иштетүү үчүн маалымат жетишсиз"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-lo/strings.xml b/pdf/pdf-viewer/src/main/res/values-lo/strings.xml
index 895280e..baa1c28 100644
--- a/pdf/pdf-viewer/src/main/res/values-lo/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-lo/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"ເປີດໄຟລ໌ບໍ່ສຳເລັດ. ອາດເປັນຍ້ອນບັນຫາທາງການອະນຸຍາດບໍ?"</string>
<string name="page_broken" msgid="2968770793669433462">"ໜ້າເສຍຫາຍສໍາລັບເອກະສານ PDF"</string>
<string name="needs_more_data" msgid="3520133467908240802">"ຂໍ້ມູນບໍ່ພຽງພໍສໍາລັບການປະມວນຜົນເອກະສານ PDF"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-lt/strings.xml b/pdf/pdf-viewer/src/main/res/values-lt/strings.xml
index 8b14e98..588ff87 100644
--- a/pdf/pdf-viewer/src/main/res/values-lt/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-lt/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Nepavyko atidaryti failo. Galima su leidimais susijusi problema?"</string>
<string name="page_broken" msgid="2968770793669433462">"Sugadintas PDF dokumento puslapis"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Nepakanka duomenų PDF dokumentui apdoroti"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-lv/strings.xml b/pdf/pdf-viewer/src/main/res/values-lv/strings.xml
index 22e20c9..738615d 100644
--- a/pdf/pdf-viewer/src/main/res/values-lv/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-lv/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Neizdevās atvērt failu. Iespējams, ir radusies problēma ar atļaujām."</string>
<string name="page_broken" msgid="2968770793669433462">"PDF dokumenta lapa ir bojāta"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Nepietiekams datu apjoms, lai apstrādātu PDF dokumentu"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-mk/strings.xml b/pdf/pdf-viewer/src/main/res/values-mk/strings.xml
index cce9ac3..e6b85c2 100644
--- a/pdf/pdf-viewer/src/main/res/values-mk/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-mk/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Не можеше да се отвори датотеката. Можеби има проблем со дозволата?"</string>
<string name="page_broken" msgid="2968770793669433462">"Страницата не може да го вчита PDF-документот"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Недоволно податоци за обработка на PDF-документот"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-ml/strings.xml b/pdf/pdf-viewer/src/main/res/values-ml/strings.xml
index b07acaa..760a696 100644
--- a/pdf/pdf-viewer/src/main/res/values-ml/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ml/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"ഫയൽ തുറക്കാനായില്ല. അനുമതി സംബന്ധിച്ച പ്രശ്നമാകാൻ സാധ്യതയുണ്ടോ?"</string>
<string name="page_broken" msgid="2968770793669433462">"PDF ഡോക്യുമെന്റിനായി പേജ് ലോഡ് ചെയ്യാനായില്ല"</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF ഡോക്യുമെന്റ് പ്രോസസ് ചെയ്യാൻ മതിയായ ഡാറ്റയില്ല"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-mn/strings.xml b/pdf/pdf-viewer/src/main/res/values-mn/strings.xml
index 756390b..9c8f052 100644
--- a/pdf/pdf-viewer/src/main/res/values-mn/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-mn/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Файлыг нээж чадсангүй. Зөвшөөрөлтэй холбоотой асуудал байж болох уу?"</string>
<string name="page_broken" msgid="2968770793669433462">"PDF баримт бичгийн хуудас эвдэрсэн"</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF баримт бичгийг боловсруулахад өгөгдөл хангалтгүй байна"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-mr/strings.xml b/pdf/pdf-viewer/src/main/res/values-mr/strings.xml
index a6d1c6e..40ee1a8 100644
--- a/pdf/pdf-viewer/src/main/res/values-mr/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-mr/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"फाइल उघडता आली नाही. परवानगीशी संबंधित संभाव्य समस्या?"</string>
<string name="page_broken" msgid="2968770793669433462">"पीडीएफ दस्तऐवजासाठी पेज खंडित झाले आहे"</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF दस्तऐवजावर प्रक्रिया करण्यासाठी डेटा पुरेसा नाही"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-ms/strings.xml b/pdf/pdf-viewer/src/main/res/values-ms/strings.xml
index acf0ee8..f905244 100644
--- a/pdf/pdf-viewer/src/main/res/values-ms/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ms/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Gagal membuka fail. Kemungkinan terdapat masalah berkaitan dengan kebenaran?"</string>
<string name="page_broken" msgid="2968770793669433462">"Halaman rosak untuk dokumen PDF"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Data tidak mencukupi untuk memproses dokumen PDF"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-my/strings.xml b/pdf/pdf-viewer/src/main/res/values-my/strings.xml
index bd311a7..c288df3 100644
--- a/pdf/pdf-viewer/src/main/res/values-my/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-my/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"ဖိုင်ကို ဖွင့်၍မရလိုက်ပါ။ ခွင့်ပြုချက် ပြဿနာ ဖြစ်နိုင်လား။"</string>
<string name="page_broken" msgid="2968770793669433462">"PDF မှတ်တမ်းအတွက် စာမျက်နှာ ပျက်နေသည်"</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF မှတ်တမ်း လုပ်ဆောင်ရန်အတွက် ဒေတာ မလုံလောက်ပါ"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-nb/strings.xml b/pdf/pdf-viewer/src/main/res/values-nb/strings.xml
index 23107ac..06e5547 100644
--- a/pdf/pdf-viewer/src/main/res/values-nb/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-nb/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Kunne ikke åpne filen. Kan det være et problem med tillatelser?"</string>
<string name="page_broken" msgid="2968770793669433462">"Siden er ødelagt for PDF-dokumentet"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Det er utilstrekkelige data for behandling av PDF-dokumentet"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-ne/strings.xml b/pdf/pdf-viewer/src/main/res/values-ne/strings.xml
index da53207..d847ed0c 100644
--- a/pdf/pdf-viewer/src/main/res/values-ne/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ne/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"फाइल खोल्न सकिएन। तपाईंसँग यो फाइल खोल्ने अनुमति छैन?"</string>
<string name="page_broken" msgid="2968770793669433462">"PDF डकुमेन्टको पेज लोड गर्न सकिएन"</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF डकुमेन्ट प्रोसेस गर्न पर्याप्त जानकारी छैन"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-night/colors.xml b/pdf/pdf-viewer/src/main/res/values-night/colors.xml
index ce96df2..ea1e693 100644
--- a/pdf/pdf-viewer/src/main/res/values-night/colors.xml
+++ b/pdf/pdf-viewer/src/main/res/values-night/colors.xml
@@ -17,5 +17,5 @@
<resources>
<color name="pdf_viewer_color_primary">@color/m3_sys_color_dark_primary</color>
<color name="pdf_viewer_color_on_surface">@color/m3_sys_color_dark_on_surface</color>
- <color name="pdf_viewer_color_on_error">@color/m3_sys_color_dark_on_error</color>
+ <color name="pdf_viewer_color_error">@color/m3_sys_color_dark_error</color>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-nl/strings.xml b/pdf/pdf-viewer/src/main/res/values-nl/strings.xml
index 9ad4b40..5ceea0c 100644
--- a/pdf/pdf-viewer/src/main/res/values-nl/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-nl/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Kan het bestand niet openen. Mogelijk rechtenprobleem?"</string>
<string name="page_broken" msgid="2968770793669433462">"Pagina van het pdf-document kan niet worden geladen"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Onvoldoende gegevens om het pdf-document te verwerken"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-or/strings.xml b/pdf/pdf-viewer/src/main/res/values-or/strings.xml
index cf3e282..66b7f68 100644
--- a/pdf/pdf-viewer/src/main/res/values-or/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-or/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"ଫାଇଲ ଖୋଲିବାରେ ବିଫଳ ହୋଇଛି। ସମ୍ଭାବ୍ୟ ଅନୁମତି ସମସ୍ୟା ଅଛି?"</string>
<string name="page_broken" msgid="2968770793669433462">"PDF ଡକ୍ୟୁମେଣ୍ଟ ପାଇଁ ପୃଷ୍ଠା ବିଭାଜିତ ହୋଇଛି"</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF ଡକ୍ୟୁମେଣ୍ଟ ପ୍ରକ୍ରିୟାକରଣ ପାଇଁ ପର୍ଯ୍ୟାପ୍ତ ଡାଟା ନାହିଁ"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-pa/strings.xml b/pdf/pdf-viewer/src/main/res/values-pa/strings.xml
index 86a73eb..8d4c105 100644
--- a/pdf/pdf-viewer/src/main/res/values-pa/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-pa/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"ਫ਼ਾਈਲ ਨੂੰ ਖੋਲ੍ਹਣਾ ਅਸਫਲ ਰਿਹਾ। ਕੀ ਸੰਭਵ ਇਜਾਜ਼ਤ ਸੰਬੰਧੀ ਸਮੱਸਿਆ ਹੈ?"</string>
<string name="page_broken" msgid="2968770793669433462">"PDF ਦਸਤਾਵੇਜ਼ ਲਈ ਪੰਨਾ ਲੋਡ ਨਹੀਂ ਹੋ ਰਿਹਾ"</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF ਦਸਤਾਵੇਜ਼ \'ਤੇ ਪ੍ਰਕਿਰਿਆ ਕਰਨ ਲਈ ਲੋੜੀਂਦਾ ਡਾਟਾ ਨਹੀਂ ਹੈ"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-pl/strings.xml b/pdf/pdf-viewer/src/main/res/values-pl/strings.xml
index ce974f6..6583cb5 100644
--- a/pdf/pdf-viewer/src/main/res/values-pl/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-pl/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Nie udało się otworzyć pliku. Może to przez problem z uprawnieniami?"</string>
<string name="page_broken" msgid="2968770793669433462">"Strona w dokumencie PDF jest uszkodzona"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Brak wystarczającej ilości danych do przetworzenia dokumentu PDF"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-pt-rBR/strings.xml b/pdf/pdf-viewer/src/main/res/values-pt-rBR/strings.xml
index f3fdf6c..e8beb95 100644
--- a/pdf/pdf-viewer/src/main/res/values-pt-rBR/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-pt-rBR/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Falha ao abrir o arquivo. Possível problema de permissão?"</string>
<string name="page_broken" msgid="2968770793669433462">"Página do documento PDF corrompida"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Dados insuficientes para processamento do documento PDF"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-pt-rPT/strings.xml b/pdf/pdf-viewer/src/main/res/values-pt-rPT/strings.xml
index ff10bc1..2a1a80a 100644
--- a/pdf/pdf-viewer/src/main/res/values-pt-rPT/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-pt-rPT/strings.xml
@@ -53,4 +53,5 @@
<string name="file_error" msgid="4003885928556884091">"Falha ao abrir o ficheiro. Possível problema de autorização?"</string>
<string name="page_broken" msgid="2968770793669433462">"Página danificada para o documento PDF"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Dados insuficientes para processar o documento PDF"</string>
+ <string name="error_cannot_open_pdf" msgid="2361919778558145071">"Não é possível abrir o ficheiro PDF"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-pt/strings.xml b/pdf/pdf-viewer/src/main/res/values-pt/strings.xml
index f3fdf6c..e8beb95 100644
--- a/pdf/pdf-viewer/src/main/res/values-pt/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-pt/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Falha ao abrir o arquivo. Possível problema de permissão?"</string>
<string name="page_broken" msgid="2968770793669433462">"Página do documento PDF corrompida"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Dados insuficientes para processamento do documento PDF"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-ro/strings.xml b/pdf/pdf-viewer/src/main/res/values-ro/strings.xml
index 669086d..ef6b73d 100644
--- a/pdf/pdf-viewer/src/main/res/values-ro/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ro/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Nu s-a putut deschide fișierul. Există vreo problemă cu permisiunile?"</string>
<string name="page_broken" msgid="2968770793669433462">"Pagină deteriorată pentru documentul PDF"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Date insuficiente pentru procesarea documentului PDF"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-ru/strings.xml b/pdf/pdf-viewer/src/main/res/values-ru/strings.xml
index d7163f2..665e7cb 100644
--- a/pdf/pdf-viewer/src/main/res/values-ru/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ru/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Не удалось открыть файл. Возможно, нет необходимых разрешений."</string>
<string name="page_broken" msgid="2968770793669433462">"Страница документа PDF повреждена"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Недостаточно данных для обработки документа PDF"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-si/strings.xml b/pdf/pdf-viewer/src/main/res/values-si/strings.xml
index bba16c5..dc5d075 100644
--- a/pdf/pdf-viewer/src/main/res/values-si/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-si/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"ගොනුව විවෘත කිරීමට අසමත් විය. අවසර ගැටලුවක් විය හැකි ද?"</string>
<string name="page_broken" msgid="2968770793669433462">"PDF ලේඛනය සඳහා පිටුව හානි වී ඇත"</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF ලේඛනය සැකසීම සඳහා ප්රමාණවත් දත්ත නොමැත"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-sk/strings.xml b/pdf/pdf-viewer/src/main/res/values-sk/strings.xml
index f8f3997..d44c7b6 100644
--- a/pdf/pdf-viewer/src/main/res/values-sk/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-sk/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Súbor sa nepodarilo otvoriť. Možno sa vyskytol problém s povolením."</string>
<string name="page_broken" msgid="2968770793669433462">"Stránka sa v dokumente vo formáte PDF nedá načítať"</string>
<string name="needs_more_data" msgid="3520133467908240802">"V dokumente vo formáte PDF nie je dostatok údajov na spracovanie"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-sl/strings.xml b/pdf/pdf-viewer/src/main/res/values-sl/strings.xml
index ad7b4cd..afef99a 100644
--- a/pdf/pdf-viewer/src/main/res/values-sl/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-sl/strings.xml
@@ -53,4 +53,5 @@
<string name="file_error" msgid="4003885928556884091">"Odpiranje datoteke ni uspelo. Ali morda gre za težavo z dovoljenjem?"</string>
<string name="page_broken" msgid="2968770793669433462">"Strani iz dokumenta PDF ni mogoče prikazati"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Nezadostni podatki za obdelavo dokumenta PDF"</string>
+ <string name="error_cannot_open_pdf" msgid="2361919778558145071">"Datoteke PDF ni mogoče odpreti"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-sq/strings.xml b/pdf/pdf-viewer/src/main/res/values-sq/strings.xml
index 0e775ba..37347d3 100644
--- a/pdf/pdf-viewer/src/main/res/values-sq/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-sq/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Hapja e skedarit dështoi. Problem i mundshëm me lejet?"</string>
<string name="page_broken" msgid="2968770793669433462">"Faqe e dëmtuar për dokumentin PDF"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Të dhëna të pamjaftueshme për përpunimin e dokumentit PDF"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-sr/strings.xml b/pdf/pdf-viewer/src/main/res/values-sr/strings.xml
index d70749c..bcab510 100644
--- a/pdf/pdf-viewer/src/main/res/values-sr/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-sr/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Отварање фајла није успело. Можда постоје проблеми са дозволом?"</string>
<string name="page_broken" msgid="2968770793669433462">"Неисправна страница за PDF документ"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Недовољно података за обраду PDF документа"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-sv/strings.xml b/pdf/pdf-viewer/src/main/res/values-sv/strings.xml
index eae0c6a..3fa349f 100644
--- a/pdf/pdf-viewer/src/main/res/values-sv/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-sv/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Det gick inte att öppna filen. Detta kan bero på ett behörighetsproblem."</string>
<string name="page_broken" msgid="2968770793669433462">"Det gick inte att läsa in en sida i PDF-dokumentet"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Otillräcklig data för att behandla PDF-dokumentet"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-sw/strings.xml b/pdf/pdf-viewer/src/main/res/values-sw/strings.xml
index e976caf..abf9c4b5 100644
--- a/pdf/pdf-viewer/src/main/res/values-sw/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-sw/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Imeshindwa kufungua faili. Je, linaweza kuwa tatizo la ruhusa?"</string>
<string name="page_broken" msgid="2968770793669433462">"Ukurasa wa hati ya PDF una tatizo"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Hamna data ya kutosha kuchakata hati ya PDF"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-ta/strings.xml b/pdf/pdf-viewer/src/main/res/values-ta/strings.xml
index 8a51fa4..2d1d291 100644
--- a/pdf/pdf-viewer/src/main/res/values-ta/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ta/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"ஃபைலைத் திறக்க முடியவில்லை. அனுமதி தொடர்பான சிக்கல் உள்ளதா?"</string>
<string name="page_broken" msgid="2968770793669433462">"PDF ஆவணத்தை ஏற்ற முடியவில்லை"</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF ஆவணத்தைச் செயலாக்குவதற்குப் போதுமான தரவு இல்லை"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-te/strings.xml b/pdf/pdf-viewer/src/main/res/values-te/strings.xml
index e5ff775..ffcee7a 100644
--- a/pdf/pdf-viewer/src/main/res/values-te/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-te/strings.xml
@@ -53,4 +53,5 @@
<string name="file_error" msgid="4003885928556884091">"ఫైల్ను తెరవడం విఫలమైంది. అనుమతికి సంబంధించిన సమస్య కావచ్చా?"</string>
<string name="page_broken" msgid="2968770793669433462">"PDF డాక్యుమెంట్కు సంబంధించి పేజీ బ్రేక్ అయింది"</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF డాక్యుమెంట్ను ప్రాసెస్ చేయడానికి డేటా తగినంత లేదు"</string>
+ <string name="error_cannot_open_pdf" msgid="2361919778558145071">"PDF ఫైల్ను తెరవడం సాధ్యపడదు"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-th/strings.xml b/pdf/pdf-viewer/src/main/res/values-th/strings.xml
index 971e3e2..be31750 100644
--- a/pdf/pdf-viewer/src/main/res/values-th/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-th/strings.xml
@@ -53,4 +53,5 @@
<string name="file_error" msgid="4003885928556884091">"เปิดไฟล์ไม่สำเร็จ อาจเกิดจากปัญหาด้านสิทธิ์"</string>
<string name="page_broken" msgid="2968770793669433462">"หน้าในเอกสาร PDF เสียหาย"</string>
<string name="needs_more_data" msgid="3520133467908240802">"ข้อมูลไม่เพียงพอสำหรับการประมวลผลเอกสาร PDF"</string>
+ <string name="error_cannot_open_pdf" msgid="2361919778558145071">"เปิดไฟล์ PDF ไม่ได้"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-tl/strings.xml b/pdf/pdf-viewer/src/main/res/values-tl/strings.xml
index be6d695..5b20647 100644
--- a/pdf/pdf-viewer/src/main/res/values-tl/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-tl/strings.xml
@@ -53,4 +53,5 @@
<string name="file_error" msgid="4003885928556884091">"Hindi nabuksan ang file. Baka may isyu sa pahintulot?"</string>
<string name="page_broken" msgid="2968770793669433462">"Sira ang page para sa PDF na dokumento"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Kulang ang data para maproseso ang PDF na dokumento"</string>
+ <string name="error_cannot_open_pdf" msgid="2361919778558145071">"Hindi mabuksan ang PDF file"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-tr/strings.xml b/pdf/pdf-viewer/src/main/res/values-tr/strings.xml
index 09803ed..8ad6bf2 100644
--- a/pdf/pdf-viewer/src/main/res/values-tr/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-tr/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Dosya açılamadı. İzin sorunundan kaynaklanıyor olabilir mi?"</string>
<string name="page_broken" msgid="2968770793669433462">"PDF dokümanının sayfası bozuk"</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF dokümanını işleyecek kadar yeterli veri yok"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-uk/strings.xml b/pdf/pdf-viewer/src/main/res/values-uk/strings.xml
index fc81a4b..c381f20 100644
--- a/pdf/pdf-viewer/src/main/res/values-uk/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-uk/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Не вдалося відкрити файл. Можливо, виникла проблема з дозволом."</string>
<string name="page_broken" msgid="2968770793669433462">"Сторінку документа PDF пошкоджено"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Недостатньо даних для обробки документа PDF"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-ur/strings.xml b/pdf/pdf-viewer/src/main/res/values-ur/strings.xml
index e733c19..6027e36 100644
--- a/pdf/pdf-viewer/src/main/res/values-ur/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ur/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"فائل کھولنے میں ناکام۔ کیا یہ اجازت کا مسئلہ ہو سکتا ہے؟"</string>
<string name="page_broken" msgid="2968770793669433462">"PDF دستاویز کیلئے شکستہ صفحہ"</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF دستاویز پر کارروائی کرنے کیلئے ڈیٹا ناکافی ہے"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-uz/strings.xml b/pdf/pdf-viewer/src/main/res/values-uz/strings.xml
index 54c3148..41b89f8 100644
--- a/pdf/pdf-viewer/src/main/res/values-uz/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-uz/strings.xml
@@ -53,4 +53,5 @@
<string name="file_error" msgid="4003885928556884091">"Fayl ochilmadi. Ruxsat bilan muammo bormi?"</string>
<string name="page_broken" msgid="2968770793669433462">"PDF hujjat sahifasi yaroqsiz"</string>
<string name="needs_more_data" msgid="3520133467908240802">"PDF hujjatni qayta ishlash uchun kerakli axborotlar yetarli emas"</string>
+ <string name="error_cannot_open_pdf" msgid="2361919778558145071">"PDF fayk ochilmadi"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-vi/strings.xml b/pdf/pdf-viewer/src/main/res/values-vi/strings.xml
index c2015bc..5f50634 100644
--- a/pdf/pdf-viewer/src/main/res/values-vi/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-vi/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Không mở được tệp này. Có thể là do vấn đề về quyền?"</string>
<string name="page_broken" msgid="2968770793669433462">"Tài liệu PDF này bị lỗi trang"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Không đủ dữ liệu để xử lý tài liệu PDF này"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-zh-rCN/strings.xml b/pdf/pdf-viewer/src/main/res/values-zh-rCN/strings.xml
index 4abd99c..c6dbf7e 100644
--- a/pdf/pdf-viewer/src/main/res/values-zh-rCN/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-zh-rCN/strings.xml
@@ -53,4 +53,5 @@
<string name="file_error" msgid="4003885928556884091">"无法打开文件。可能是由于权限问题导致?"</string>
<string name="page_broken" msgid="2968770793669433462">"PDF 文档的页面已损坏"</string>
<string name="needs_more_data" msgid="3520133467908240802">"数据不足,无法处理 PDF 文档"</string>
+ <string name="error_cannot_open_pdf" msgid="2361919778558145071">"无法打开 PDF 文件"</string>
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-zh-rHK/strings.xml b/pdf/pdf-viewer/src/main/res/values-zh-rHK/strings.xml
index 3510530..691bdfe 100644
--- a/pdf/pdf-viewer/src/main/res/values-zh-rHK/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-zh-rHK/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"無法開啟檔案。可能有權限問題?"</string>
<string name="page_broken" msgid="2968770793669433462">"PDF 文件頁面已損毀"</string>
<string name="needs_more_data" msgid="3520133467908240802">"沒有足夠資料處理 PDF 文件"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-zh-rTW/strings.xml b/pdf/pdf-viewer/src/main/res/values-zh-rTW/strings.xml
index 10fd054..b2be3f4 100644
--- a/pdf/pdf-viewer/src/main/res/values-zh-rTW/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-zh-rTW/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"無法開啟檔案。有可能是權限問題?"</string>
<string name="page_broken" msgid="2968770793669433462">"PDF 文件的頁面損毀"</string>
<string name="needs_more_data" msgid="3520133467908240802">"資料不足,無法處理 PDF 文件"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values-zu/strings.xml b/pdf/pdf-viewer/src/main/res/values-zu/strings.xml
index 4065bd2..ba456e5 100644
--- a/pdf/pdf-viewer/src/main/res/values-zu/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-zu/strings.xml
@@ -53,4 +53,6 @@
<string name="file_error" msgid="4003885928556884091">"Yehlulekile ukuvula ifayela. Inkinga yemvume engaba khona?"</string>
<string name="page_broken" msgid="2968770793669433462">"Ikhasi eliphuliwe ledokhumenti ye-PDF"</string>
<string name="needs_more_data" msgid="3520133467908240802">"Idatha enganele yokucubungula idokhumenti ye-PDF"</string>
+ <!-- no translation found for error_cannot_open_pdf (2361919778558145071) -->
+ <skip />
</resources>
diff --git a/pdf/pdf-viewer/src/main/res/values/colors.xml b/pdf/pdf-viewer/src/main/res/values/colors.xml
index 63ae166..8730c12 100644
--- a/pdf/pdf-viewer/src/main/res/values/colors.xml
+++ b/pdf/pdf-viewer/src/main/res/values/colors.xml
@@ -17,7 +17,7 @@
<resources>
<color name="pdf_viewer_color_primary">@color/m3_sys_color_light_primary</color>
<color name="pdf_viewer_color_on_surface">@color/m3_sys_color_light_on_surface</color>
- <color name="pdf_viewer_color_on_error">@color/m3_sys_color_light_on_error</color>
+ <color name="pdf_viewer_color_error">@color/m3_sys_color_light_error</color>
<!-- m3_sys_color_primary_fixed_dim doesn't have a day/night version, therefore defined only once -->
<color name="pdf_viewer_selection_handles">@color/m3_sys_color_primary_fixed_dim</color>
</resources>
diff --git a/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/PaginationModelTest.java b/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/PaginationModelTest.java
index eecca89..a568c6f 100644
--- a/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/PaginationModelTest.java
+++ b/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/PaginationModelTest.java
@@ -178,25 +178,25 @@
mPaginationModel.addPage(1, mdDimensions);
mPaginationModel.addPage(2, lgDimensions);
// Setting viewArea large enough to accommodate the entire model.
- mPaginationModel.setViewArea(new Rect(0, 0, 800, 800));
+ Rect viewArea = new Rect(0, 0, 800, 800);
Rect expectedSmLocation = new Rect(300, 0, 500, 100);
- assertThat(mPaginationModel.getPageLocation(0)).isEqualTo(expectedSmLocation);
+ assertThat(mPaginationModel.getPageLocation(0, viewArea)).isEqualTo(expectedSmLocation);
Rect expectedMdLocation =
new Rect(200, 100 + getSpacingAbovePage(1), 600, 300 + getSpacingAbovePage(1));
- assertThat(mPaginationModel.getPageLocation(1)).isEqualTo(expectedMdLocation);
+ assertThat(mPaginationModel.getPageLocation(1, viewArea)).isEqualTo(expectedMdLocation);
Rect expectedLgLocation =
new Rect(0, 300 + getSpacingAbovePage(2), 800, 700 + getSpacingAbovePage(2));
- Assert.assertEquals(expectedLgLocation, mPaginationModel.getPageLocation(2));
- assertThat(mPaginationModel.getPageLocation(2)).isEqualTo(expectedLgLocation);
+ assertThat(mPaginationModel.getPageLocation(2, viewArea)).isEqualTo(expectedLgLocation);
}
/**
- * {@link PaginationModel#getPageLocation(int)} should try to fit as much of each page into the
- * viewable area as possible. Dimensions do not change vertically but pages that are smaller
- * than {@link PaginationModel#getWidth()} can be moved horizontally to make this happen.
+ * {@link PaginationModel#getPageLocation(int, Rect)} should try to fit as much of each page
+ * into the viewable area as possible. Dimensions do not change vertically but pages that are
+ * smaller than {@link PaginationModel#getWidth()} can be moved horizontally to make this
+ * happen.
*
* <p>Page 1's width is smaller than {@link PaginationModel#getWidth()} so it will be placed in
* the middle when the visible area covers the whole model {@see #testGetPageLocation}. When the
@@ -216,11 +216,11 @@
mPaginationModel.addPage(2, lgDimensions);
// Setting viewArea to a 300x200 section in the bottom-left corner of the model.
- mPaginationModel.setViewArea(new Rect(0, 500, 200, 800));
+ Rect viewArea = new Rect(0, 500, 200, 800);
Rect expectedMdLocation =
new Rect(0, 100 + getSpacingAbovePage(1), 400, 300 + getSpacingAbovePage(1));
- assertThat(mPaginationModel.getPageLocation(1)).isEqualTo(expectedMdLocation);
+ assertThat(mPaginationModel.getPageLocation(1, viewArea)).isEqualTo(expectedMdLocation);
}
/**
diff --git a/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/ZoomScrollValueObserverTest.java b/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/ZoomScrollValueObserverTest.java
index b19e190..93e2b09 100644
--- a/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/ZoomScrollValueObserverTest.java
+++ b/pdf/pdf-viewer/src/test/java/androidx/pdf/viewer/ZoomScrollValueObserverTest.java
@@ -80,7 +80,7 @@
mNewPosition = new ZoomView.ZoomScroll(1.0f, 0, 0, false);
when(mMockPaginatedView.getPageRangeHandler()).thenReturn(mPageRangeHandler);
- when(mMockPaginatedView.getPaginationModel()).thenReturn(mMockPaginationModel);
+ when(mMockPaginatedView.getModel()).thenReturn(mMockPaginationModel);
when(mMockPaginationModel.isInitialized()).thenReturn(true);
when(mMockZoomView.getHeight()).thenReturn(100);
when(mPageRangeHandler.computeVisibleRange(0, 1.0f, 100, false)).thenReturn(PAGE_RANGE);
@@ -103,7 +103,7 @@
zoomScrollValueObserver.onChange(OLD_POSITION, mNewPosition);
verify(mMockZoomView).setStableZoom(1.0f);
- verify(mMockPaginationModel).setViewArea(RECT);
+ verify(mMockPaginatedView).setViewArea(RECT);
verify(mMockPaginatedView).refreshPageRangeInVisibleArea(mNewPosition, 100);
verify(mMockPaginatedView).handleGonePages(false);
verify(mMockPaginatedView).loadInvisibleNearPageRange(1.0f);
diff --git a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/VersionCompatUtil.kt b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/VersionCompatUtil.kt
index e685b4c..ef1a4b4 100644
--- a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/VersionCompatUtil.kt
+++ b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/VersionCompatUtil.kt
@@ -32,25 +32,9 @@
SdkExtensions.getExtensionVersion(Build.VERSION_CODES.S) >= minVersion
}
- fun isRWithMinExtServicesVersion(minVersion: Int): Boolean {
- return Build.VERSION.SDK_INT == 30 &&
- SdkExtensions.getExtensionVersion(Build.VERSION_CODES.R) >= minVersion
- }
-
// Helper method to determine if version is testable, for APIs that are S+ only
fun isTestableVersion(minAdServicesVersion: Int, minExtServicesVersion: Int): Boolean {
return isTPlusWithMinAdServicesVersion(minAdServicesVersion) ||
isSWithMinExtServicesVersion(minExtServicesVersion)
}
-
- // Helper method to determine if version is testable, for APIs that are available on R
- fun isTestableVersion(
- minAdServicesVersion: Int,
- minExtServicesVersionS: Int,
- minExtServicesVersionR: Int
- ): Boolean {
- return isTPlusWithMinAdServicesVersion(minAdServicesVersion) ||
- isSWithMinExtServicesVersion(minExtServicesVersionS) ||
- isRWithMinExtServicesVersion(minExtServicesVersionR)
- }
}
diff --git a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/adid/AdIdManagerFuturesTest.kt b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/adid/AdIdManagerFuturesTest.kt
index dcf44e7d..3b2c545 100644
--- a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/adid/AdIdManagerFuturesTest.kt
+++ b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/adid/AdIdManagerFuturesTest.kt
@@ -17,7 +17,6 @@
package androidx.privacysandbox.ads.adservices.java.adid
import android.adservices.adid.AdIdManager
-import android.adservices.common.AdServicesOutcomeReceiver
import android.content.Context
import android.os.Looper
import android.os.OutcomeReceiver
@@ -54,14 +53,12 @@
private var mSession: StaticMockitoSession? = null
private val mValidAdExtServicesSdkExtVersionS =
VersionCompatUtil.isSWithMinExtServicesVersion(9)
- private val mValidAdExtServicesSdkExtVersionR =
- VersionCompatUtil.isRWithMinExtServicesVersion(11)
@Before
fun setUp() {
mContext = spy(ApplicationProvider.getApplicationContext<Context>())
- if (mValidAdExtServicesSdkExtVersionS || mValidAdExtServicesSdkExtVersionR) {
+ if (mValidAdExtServicesSdkExtVersionS) {
// setup a mockitoSession to return the mocked manager
// when the static method .get() is called
mSession =
@@ -78,11 +75,10 @@
@SdkSuppress(maxSdkVersion = 33, minSdkVersion = 30)
fun testAdIdOlderVersions() {
Assume.assumeFalse(
- "maxSdkVersion = API 33 ext 3 or API 31/32 ext 8 or API 30 ext 10",
+ "maxSdkVersion = API 33 ext 3 or API 31/32 ext 8",
VersionCompatUtil.isTestableVersion(
/* minAdServicesVersion=*/ 4,
/* minExtServicesVersionS=*/ 9,
- /* minExtServicesVersionR=*/ 11
)
)
Truth.assertThat(AdIdManagerFutures.from(mContext)).isEqualTo(null)
@@ -91,24 +87,15 @@
@Test
fun testAdIdAsync() {
Assume.assumeTrue(
- "minSdkVersion = API 33 ext 4 or API 31/32 ext 9 or API 30 ext 11",
+ "minSdkVersion = API 33 ext 4 or API 31/32 ext 9",
VersionCompatUtil.isTestableVersion(
/* minAdServicesVersion= */ 4,
/* minExtServicesVersionS=*/ 9,
- /* minExtServicesVersionR=*/ 11
)
)
- val adIdManager =
- mockAdIdManager(
- mContext,
- mValidAdExtServicesSdkExtVersionS || mValidAdExtServicesSdkExtVersionR
- )
-
- when (mValidAdExtServicesSdkExtVersionR) {
- true -> setupResponseR(adIdManager)
- false -> setupResponseSPlus(adIdManager)
- }
+ val adIdManager = mockAdIdManager(mContext, mValidAdExtServicesSdkExtVersionS)
+ setupResponseSPlus(adIdManager)
val managerCompat = AdIdManagerFutures.from(mContext)
@@ -117,11 +104,7 @@
// Verify that the result of the compat call is correct.
verifyResponse(result.get())
-
- when (mValidAdExtServicesSdkExtVersionR) {
- true -> verifyOnR(adIdManager)
- false -> verifyOnSPlus(adIdManager)
- }
+ verifyOnSPlus(adIdManager)
}
@SdkSuppress(minSdkVersion = 30)
@@ -157,37 +140,6 @@
)
}
- private fun setupResponseR(adIdManager: AdIdManager) {
- // Set up the response that AdIdManager will return when the compat code calls it.
- val adId = android.adservices.adid.AdId("1234", false)
- val answer = { args: InvocationOnMock ->
- assertNotEquals(Looper.getMainLooper(), Looper.myLooper())
- val receiver =
- args.getArgument<
- AdServicesOutcomeReceiver<android.adservices.adid.AdId, Exception>
- >(
- 1
- )
- receiver.onResult(adId)
- null
- }
- Mockito.doAnswer(answer)
- .`when`(adIdManager)
- .getAdId(
- any<Executor>(),
- any<AdServicesOutcomeReceiver<android.adservices.adid.AdId, Exception>>()
- )
- }
-
- private fun verifyOnR(adIdManager: AdIdManager) {
- // Verify that the compat code was invoked correctly.
- Mockito.verify(adIdManager)
- .getAdId(
- any<Executor>(),
- any<AdServicesOutcomeReceiver<android.adservices.adid.AdId, Exception>>()
- )
- }
-
private fun verifyOnSPlus(adIdManager: AdIdManager) {
// Verify that the compat code was invoked correctly.
Mockito.verify(adIdManager)
diff --git a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/TestUtil.java b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/TestUtil.java
index 8eea312..2df8a7a 100644
--- a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/TestUtil.java
+++ b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/TestUtil.java
@@ -115,17 +115,6 @@
}
}
- public void enableBackCompatOnR() {
- runShellCommand("device_config put adservices adservice_enabled true");
- runShellCommand("device_config put adservices enable_back_compat true");
- }
-
-
- public void disableBackCompatOnR() {
- runShellCommand("device_config put adservices adservice_enabled false");
- runShellCommand("device_config put adservices enable_back_compat false");
- }
-
public void enableBackCompatOnS() {
runShellCommand("device_config put adservices enable_back_compat true");
runShellCommand("device_config put adservices consent_source_of_truth 3");
diff --git a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/adid/AdIdManagerTest.java b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/adid/AdIdManagerTest.java
index dd54c93..dc17deb 100644
--- a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/adid/AdIdManagerTest.java
+++ b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/adid/AdIdManagerTest.java
@@ -80,8 +80,7 @@
Assume.assumeTrue(
VersionCompatUtil.INSTANCE.isTestableVersion(
/* minAdServicesVersion= */ 4,
- /* minExtServicesVersionS= */ 9,
- /* minExtServicesVersionR= */ 11));
+ /* minExtServicesVersionS= */ 9));
AdIdManagerFutures adIdManager =
AdIdManagerFutures.from(ApplicationProvider.getApplicationContext());
diff --git a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/measurement/MeasurementManagerTest.java b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/measurement/MeasurementManagerTest.java
index fc16065..7f45668 100644
--- a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/measurement/MeasurementManagerTest.java
+++ b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/endtoend/measurement/MeasurementManagerTest.java
@@ -96,8 +96,6 @@
MeasurementManagerFutures.from(ApplicationProvider.getApplicationContext());
if (VersionCompatUtil.INSTANCE.isSWithMinExtServicesVersion(9)) {
mTestUtil.enableBackCompatOnS();
- } else if (VersionCompatUtil.INSTANCE.isRWithMinExtServicesVersion(11)) {
- mTestUtil.enableBackCompatOnR();
}
// Put in a short sleep to make sure the updated config propagates
@@ -115,8 +113,6 @@
mTestUtil.overrideDisableMeasurementEnrollmentCheck("0");
if (VersionCompatUtil.INSTANCE.isSWithMinExtServicesVersion(9)) {
mTestUtil.disableBackCompatOnS();
- } else if (VersionCompatUtil.INSTANCE.isRWithMinExtServicesVersion(11)) {
- mTestUtil.disableBackCompatOnR();
}
// Cool-off rate limiter
@@ -129,8 +125,7 @@
Assume.assumeTrue(
VersionCompatUtil.INSTANCE.isTestableVersion(
/* minAdServicesVersion= */ 5,
- /* minExtServicesVersionS= */ 9,
- /* minExtServicesVersionR= */ 11));
+ /* minExtServicesVersionS= */ 9));
assertThat(
mMeasurementManager
@@ -146,8 +141,7 @@
Assume.assumeTrue(
VersionCompatUtil.INSTANCE.isTestableVersion(
/* minAdServicesVersion= */ 5,
- /* minExtServicesVersionS= */ 9,
- /* minExtServicesVersionR= */ 11));
+ /* minExtServicesVersionS= */ 9));
SourceRegistrationRequest request =
new SourceRegistrationRequest.Builder(
@@ -162,8 +156,7 @@
Assume.assumeTrue(
VersionCompatUtil.INSTANCE.isTestableVersion(
/* minAdServicesVersion= */ 5,
- /* minExtServicesVersionS= */ 9,
- /* minExtServicesVersionR= */ 11));
+ /* minExtServicesVersionS= */ 9));
assertThat(mMeasurementManager.registerTriggerAsync(TRIGGER_REGISTRATION_URI).get())
.isNotNull();
@@ -175,8 +168,7 @@
Assume.assumeTrue(
VersionCompatUtil.INSTANCE.isTestableVersion(
/* minAdServicesVersion= */ 5,
- /* minExtServicesVersionS= */ 9,
- /* minExtServicesVersionR= */ 11));
+ /* minExtServicesVersionS= */ 9));
WebSourceParams webSourceParams = new WebSourceParams(SOURCE_REGISTRATION_URI, false);
@@ -199,8 +191,7 @@
Assume.assumeTrue(
VersionCompatUtil.INSTANCE.isTestableVersion(
/* minAdServicesVersion= */ 5,
- /* minExtServicesVersionS= */ 9,
- /* minExtServicesVersionR= */ 11));
+ /* minExtServicesVersionS= */ 9));
WebTriggerParams webTriggerParams = new WebTriggerParams(TRIGGER_REGISTRATION_URI, false);
WebTriggerRegistrationRequest webTriggerRegistrationRequest =
@@ -236,8 +227,7 @@
Assume.assumeTrue(
VersionCompatUtil.INSTANCE.isTestableVersion(
/* minAdServicesVersion= */ 5,
- /* minExtServicesVersionS= */ 9,
- /* minExtServicesVersionR= */ 11));
+ /* minExtServicesVersionS= */ 9));
DeletionRequest deletionRequest =
new DeletionRequest.Builder(
@@ -258,8 +248,7 @@
Assume.assumeTrue(
VersionCompatUtil.INSTANCE.isTestableVersion(
/* minAdServicesVersion= */ 5,
- /* minExtServicesVersionS= */ 9,
- /* minExtServicesVersionR= */ 11));
+ /* minExtServicesVersionS= */ 9));
DeletionRequest deletionRequest =
new DeletionRequest.Builder(
@@ -283,8 +272,7 @@
Assume.assumeTrue(
VersionCompatUtil.INSTANCE.isTestableVersion(
/* minAdServicesVersion= */ 5,
- /* minExtServicesVersionS= */ 9,
- /* minExtServicesVersionR= */ 11));
+ /* minExtServicesVersionS= */ 9));
int result = mMeasurementManager.getMeasurementApiStatusAsync().get();
assertThat(result).isEqualTo(1);
diff --git a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/measurement/MeasurementManagerFuturesTest.kt b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/measurement/MeasurementManagerFuturesTest.kt
index 363191c..eeae81f 100644
--- a/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/measurement/MeasurementManagerFuturesTest.kt
+++ b/privacysandbox/ads/ads-adservices-java/src/androidTest/java/androidx/privacysandbox/ads/adservices/java/measurement/MeasurementManagerFuturesTest.kt
@@ -16,7 +16,6 @@
package androidx.privacysandbox.ads.adservices.java.measurement
-import android.adservices.common.AdServicesOutcomeReceiver
import android.adservices.measurement.MeasurementManager
import android.content.Context
import android.net.Uri
@@ -75,16 +74,12 @@
private var mSession: StaticMockitoSession? = null
private val mValidAdExtServicesSdkExtVersionS =
VersionCompatUtil.isSWithMinExtServicesVersion(9)
- private val mValidAdExtServicesSdkExtVersionR =
- VersionCompatUtil.isRWithMinExtServicesVersion(11)
- private val mValidExtServicesVersion =
- mValidAdExtServicesSdkExtVersionS || mValidAdExtServicesSdkExtVersionR
@Before
fun setUp() {
mContext = spy(ApplicationProvider.getApplicationContext<Context>())
- if (mValidExtServicesVersion) {
+ if (mValidAdExtServicesSdkExtVersionS) {
// setup a mockitoSession to return the mocked manager
// when the static method .get() is called
mSession =
@@ -104,11 +99,10 @@
@SdkSuppress(maxSdkVersion = 33, minSdkVersion = 30)
fun testMeasurementOlderVersions() {
Assume.assumeFalse(
- "maxSdkVersion = API 33 ext 4 or API 31/32 ext 8 or API 30 ext 10",
+ "maxSdkVersion = API 33 ext 4 or API 31/32 ext 8",
VersionCompatUtil.isTestableVersion(
/* minAdServicesVersion=*/ 5,
/* minExtServicesVersionS=*/ 9,
- /* minExtServicesVersionR=*/ 11
)
)
assertThat(from(mContext)).isEqualTo(null)
@@ -556,410 +550,6 @@
)
}
- @Test
- @SdkSuppress(maxSdkVersion = 30, minSdkVersion = 30)
- fun testDeleteRegistrationsAsyncOnR() {
- Assume.assumeTrue("minSdkVersion = API 30 ext 11", mValidAdExtServicesSdkExtVersionR)
-
- val mMeasurementManager =
- mockMeasurementManager(mContext, mValidAdExtServicesSdkExtVersionR)
- val managerCompat = from(mContext)
-
- // Set up the request.
- val answer = { args: InvocationOnMock ->
- val receiver = args.getArgument<AdServicesOutcomeReceiver<Any, Exception>>(2)
- receiver.onResult(Object())
- assertNotEquals(Looper.myLooper(), Looper.getMainLooper())
- null
- }
- doAnswer(answer)
- .`when`(mMeasurementManager)
- .deleteRegistrations(
- any<android.adservices.measurement.DeletionRequest>(),
- any<Executor>(),
- any<AdServicesOutcomeReceiver<Any, java.lang.Exception>>()
- )
-
- // Actually invoke the compat code.
- val request =
- DeletionRequest(
- DeletionRequest.DELETION_MODE_ALL,
- DeletionRequest.MATCH_BEHAVIOR_DELETE,
- Instant.now(),
- Instant.now(),
- listOf(uri1),
- listOf(uri1)
- )
-
- managerCompat!!.deleteRegistrationsAsync(request).get()
-
- // Verify that the compat code was invoked correctly.
- val captor =
- ArgumentCaptor.forClass(android.adservices.measurement.DeletionRequest::class.java)
- verify(mMeasurementManager)
- .deleteRegistrations(
- captor.capture(),
- any<Executor>(),
- any<AdServicesOutcomeReceiver<Any, java.lang.Exception>>()
- )
-
- // Verify that the request that the compat code makes to the platform is correct.
- verifyDeletionRequest(captor.value)
- }
-
- @Test
- @SdkSuppress(maxSdkVersion = 30, minSdkVersion = 30)
- fun testRegisterSourceAsyncOnR() {
- Assume.assumeTrue("minSdkVersion = API 30 ext 11", mValidAdExtServicesSdkExtVersionR)
-
- val mMeasurementManager =
- mockMeasurementManager(mContext, mValidAdExtServicesSdkExtVersionR)
- val inputEvent = mock(InputEvent::class.java)
- val managerCompat = from(mContext)
-
- val answer = { args: InvocationOnMock ->
- assertNotEquals(Looper.myLooper(), Looper.getMainLooper())
- val receiver = args.getArgument<AdServicesOutcomeReceiver<Any, Exception>>(3)
- receiver.onResult(Object())
- null
- }
- doAnswer(answer)
- .`when`(mMeasurementManager)
- .registerSource(
- any<Uri>(),
- any<InputEvent>(),
- any<Executor>(),
- any<AdServicesOutcomeReceiver<Any, Exception>>()
- )
-
- // Actually invoke the compat code.
- managerCompat!!.registerSourceAsync(uri1, inputEvent).get()
-
- // Verify that the compat code was invoked correctly.
- val captor1 = ArgumentCaptor.forClass(Uri::class.java)
- val captor2 = ArgumentCaptor.forClass(InputEvent::class.java)
- verify(mMeasurementManager)
- .registerSource(
- captor1.capture(),
- captor2.capture(),
- any<Executor>(),
- any<AdServicesOutcomeReceiver<Any, Exception>>()
- )
-
- // Verify that the request that the compat code makes to the platform is correct.
- assertThat(captor1.value == uri1)
- assertThat(captor2.value == inputEvent)
- }
-
- @Test
- @SdkSuppress(maxSdkVersion = 30, minSdkVersion = 30)
- fun testRegisterTriggerAsyncOnR() {
- Assume.assumeTrue("minSdkVersion = API 30 ext 11", mValidAdExtServicesSdkExtVersionR)
-
- val mMeasurementManager =
- mockMeasurementManager(mContext, mValidAdExtServicesSdkExtVersionR)
- val managerCompat = from(mContext)
-
- val answer = { args: InvocationOnMock ->
- assertNotEquals(Looper.myLooper(), Looper.getMainLooper())
- val receiver = args.getArgument<AdServicesOutcomeReceiver<Any, Exception>>(2)
- receiver.onResult(Object())
- null
- }
- doAnswer(answer)
- .`when`(mMeasurementManager)
- .registerTrigger(
- any<Uri>(),
- any<Executor>(),
- any<AdServicesOutcomeReceiver<Any, Exception>>()
- )
-
- // Actually invoke the compat code.
- managerCompat!!.registerTriggerAsync(uri1).get()
-
- // Verify that the compat code was invoked correctly.
- val captor1 = ArgumentCaptor.forClass(Uri::class.java)
- verify(mMeasurementManager)
- .registerTrigger(
- captor1.capture(),
- any<Executor>(),
- any<AdServicesOutcomeReceiver<Any, Exception>>()
- )
-
- // Verify that the request that the compat code makes to the platform is correct.
- assertThat(captor1.value == uri1)
- }
-
- @Test
- @SdkSuppress(maxSdkVersion = 30, minSdkVersion = 30)
- fun testRegisterWebSourceAsyncOnR() {
- Assume.assumeTrue("minSdkVersion = API 30 ext 11", mValidAdExtServicesSdkExtVersionR)
-
- val mMeasurementManager =
- mockMeasurementManager(mContext, mValidAdExtServicesSdkExtVersionR)
- val managerCompat = from(mContext)
-
- val answer = { args: InvocationOnMock ->
- assertNotEquals(Looper.myLooper(), Looper.getMainLooper())
- val receiver = args.getArgument<AdServicesOutcomeReceiver<Any, Exception>>(2)
- receiver.onResult(Object())
- null
- }
- doAnswer(answer)
- .`when`(mMeasurementManager)
- .registerWebSource(
- any<android.adservices.measurement.WebSourceRegistrationRequest>(),
- any<Executor>(),
- any<AdServicesOutcomeReceiver<Any, Exception>>()
- )
-
- val request =
- WebSourceRegistrationRequest.Builder(listOf(WebSourceParams(uri2, false)), uri1)
- .setAppDestination(appDestination)
- .build()
-
- // Actually invoke the compat code.
- managerCompat!!.registerWebSourceAsync(request).get()
-
- // Verify that the compat code was invoked correctly.
- val captor1 =
- ArgumentCaptor.forClass(
- android.adservices.measurement.WebSourceRegistrationRequest::class.java
- )
- verify(mMeasurementManager)
- .registerWebSource(
- captor1.capture(),
- any<Executor>(),
- any<AdServicesOutcomeReceiver<Any, Exception>>()
- )
-
- // Verify that the request that the compat code makes to the platform is correct.
- val actualRequest = captor1.value
- assertThat(actualRequest.topOriginUri == uri1)
- assertThat(actualRequest.sourceParams.size == 1)
- assertThat(actualRequest.appDestination == appDestination)
- assertThat(actualRequest.sourceParams[0].registrationUri == uri2)
- assertThat(!actualRequest.sourceParams[0].isDebugKeyAllowed)
- }
-
- @Test
- @SdkSuppress(maxSdkVersion = 30, minSdkVersion = 30)
- fun testRegisterWebTriggerAsyncOnR() {
- Assume.assumeTrue("minSdkVersion = API 30 ext 11", mValidAdExtServicesSdkExtVersionR)
-
- val mMeasurementManager =
- mockMeasurementManager(mContext, mValidAdExtServicesSdkExtVersionR)
- val managerCompat = from(mContext)
-
- val answer = { args: InvocationOnMock ->
- assertNotEquals(Looper.myLooper(), Looper.getMainLooper())
- val receiver = args.getArgument<AdServicesOutcomeReceiver<Any, Exception>>(2)
- receiver.onResult(Object())
- null
- }
- doAnswer(answer)
- .`when`(mMeasurementManager)
- .registerWebTrigger(
- any<android.adservices.measurement.WebTriggerRegistrationRequest>(),
- any<Executor>(),
- any<AdServicesOutcomeReceiver<Any, Exception>>()
- )
-
- val request = WebTriggerRegistrationRequest(listOf(WebTriggerParams(uri1, false)), uri2)
-
- // Actually invoke the compat code.
- managerCompat!!.registerWebTriggerAsync(request).get()
-
- // Verify that the compat code was invoked correctly.
- val captor1 =
- ArgumentCaptor.forClass(
- android.adservices.measurement.WebTriggerRegistrationRequest::class.java
- )
- verify(mMeasurementManager)
- .registerWebTrigger(
- captor1.capture(),
- any<Executor>(),
- any<AdServicesOutcomeReceiver<Any, Exception>>()
- )
-
- // Verify that the request that the compat code makes to the platform is correct.
- val actualRequest = captor1.value
- assertThat(actualRequest.destination == uri2)
- assertThat(actualRequest.triggerParams.size == 1)
- assertThat(actualRequest.triggerParams[0].registrationUri == uri1)
- assertThat(!actualRequest.triggerParams[0].isDebugKeyAllowed)
- }
-
- @Test
- @SdkSuppress(maxSdkVersion = 30, minSdkVersion = 30)
- fun testMeasurementApiStatusAsyncOnR() {
- Assume.assumeTrue("minSdkVersion = API 30 ext 11", mValidAdExtServicesSdkExtVersionR)
-
- val mMeasurementManager =
- mockMeasurementManager(mContext, mValidAdExtServicesSdkExtVersionR)
- val managerCompat = from(mContext)
-
- val state = MeasurementManager.MEASUREMENT_API_STATE_DISABLED
- val answer = { args: InvocationOnMock ->
- assertNotEquals(Looper.myLooper(), Looper.getMainLooper())
- val receiver = args.getArgument<AdServicesOutcomeReceiver<Int, Exception>>(1)
- receiver.onResult(state)
- null
- }
- doAnswer(answer)
- .`when`(mMeasurementManager)
- .getMeasurementApiStatus(
- any<Executor>(),
- any<AdServicesOutcomeReceiver<Int, Exception>>()
- )
-
- // Actually invoke the compat code.
- val result = managerCompat!!.getMeasurementApiStatusAsync()
- result.get()
-
- // Verify that the compat code was invoked correctly.
- verify(mMeasurementManager)
- .getMeasurementApiStatus(
- any<Executor>(),
- any<AdServicesOutcomeReceiver<Int, Exception>>()
- )
-
- // Verify that the result.
- assertThat(result.get() == state)
- }
-
- @ExperimentalFeatures.RegisterSourceOptIn
- @Test
- @SdkSuppress(maxSdkVersion = 30, minSdkVersion = 30)
- fun testRegisterSourceAsync_allSuccessOnR() {
- Assume.assumeTrue("minSdkVersion = API 30 ext 11", mValidAdExtServicesSdkExtVersionR)
-
- val mMeasurementManager =
- mockMeasurementManager(mContext, mValidAdExtServicesSdkExtVersionR)
- val inputEvent = mock(InputEvent::class.java)
- val managerCompat = from(mContext)
-
- val successCallback = { args: InvocationOnMock ->
- assertNotEquals(Looper.myLooper(), Looper.getMainLooper())
- val receiver = args.getArgument<AdServicesOutcomeReceiver<Any, Exception>>(3)
- receiver.onResult(Object())
- null
- }
- doAnswer(successCallback)
- .`when`(mMeasurementManager)
- .registerSource(
- any<Uri>(),
- any<InputEvent>(),
- any<Executor>(),
- any<AdServicesOutcomeReceiver<Any, Exception>>()
- )
-
- // Actually invoke the compat code.
- val request =
- SourceRegistrationRequest.Builder(listOf(uri1, uri2)).setInputEvent(inputEvent).build()
- managerCompat!!.registerSourceAsync(request).get()
-
- // Verify that the compat code was invoked correctly.
- verify(mMeasurementManager)
- .registerSource(
- eq(uri1),
- eq(inputEvent),
- any<Executor>(),
- any<AdServicesOutcomeReceiver<Any, Exception>>()
- )
- verify(mMeasurementManager)
- .registerSource(
- eq(uri2),
- eq(inputEvent),
- any<Executor>(),
- any<AdServicesOutcomeReceiver<Any, Exception>>()
- )
- }
-
- @ExperimentalFeatures.RegisterSourceOptIn
- @Test
- @SdkSuppress(maxSdkVersion = 30, minSdkVersion = 30)
- fun testRegisterSource_15thOf20Fails_atLeast15thExecutesOnR() {
- Assume.assumeTrue("minSdkVersion = API 30 ext 11", mValidAdExtServicesSdkExtVersionR)
-
- val mMeasurementManager =
- mockMeasurementManager(mContext, mValidAdExtServicesSdkExtVersionR)
- val mockInputEvent = mock(InputEvent::class.java)
- val managerCompat = from(mContext)
-
- val successCallback = { args: InvocationOnMock ->
- val receiver = args.getArgument<AdServicesOutcomeReceiver<Any, Exception>>(3)
- receiver.onResult(Object())
- null
- }
-
- val errorMessage = "some error occurred"
- val errorCallback = { args: InvocationOnMock ->
- val receiver = args.getArgument<AdServicesOutcomeReceiver<Any, Exception>>(3)
- receiver.onError(IllegalArgumentException(errorMessage))
- null
- }
-
- val uris =
- (1..20)
- .map { i ->
- val uri = Uri.parse("www.uri$i.com")
- if (i == 15) {
- doAnswer(errorCallback)
- .`when`(mMeasurementManager)
- .registerSource(
- eq(uri),
- any<InputEvent>(),
- any<Executor>(),
- any<AdServicesOutcomeReceiver<Any, Exception>>()
- )
- } else {
- doAnswer(successCallback)
- .`when`(mMeasurementManager)
- .registerSource(
- eq(uri),
- any<InputEvent>(),
- any<Executor>(),
- any<AdServicesOutcomeReceiver<Any, Exception>>()
- )
- }
- uri
- }
- .toList()
-
- val request = SourceRegistrationRequest(uris, mockInputEvent)
-
- // Actually invoke the compat code.
- runBlocking {
- try {
- withContext(Dispatchers.Main) { managerCompat!!.registerSourceAsync(request).get() }
- fail("Expected failure.")
- } catch (e: ExecutionException) {
- assertTrue(e.cause!! is IllegalArgumentException)
- assertThat(e.cause!!.message).isEqualTo(errorMessage)
- }
- }
-
- // Verify that the compat code was invoked correctly.
- // registerSource gets called 1-20 times. We cannot predict the exact number because
- // uri15 would crash asynchronously. Other uris may succeed and those threads on default
- // dispatcher won't crash.
- verify(mMeasurementManager, atLeastOnce())
- .registerSource(
- any<Uri>(),
- eq(mockInputEvent),
- any<Executor>(),
- any<AdServicesOutcomeReceiver<Any, Exception>>()
- )
- verify(mMeasurementManager, atMost(20))
- .registerSource(
- any<Uri>(),
- eq(mockInputEvent),
- any<Executor>(),
- any<AdServicesOutcomeReceiver<Any, Exception>>()
- )
- }
-
@SdkSuppress(minSdkVersion = 30)
companion object {
diff --git a/privacysandbox/ads/ads-adservices/build.gradle b/privacysandbox/ads/ads-adservices/build.gradle
index e35df7a..5610d1a 100644
--- a/privacysandbox/ads/ads-adservices/build.gradle
+++ b/privacysandbox/ads/ads-adservices/build.gradle
@@ -51,10 +51,6 @@
}
android {
- buildTypes.all {
- consumerProguardFiles "proguard-rules.pro"
- }
-
compileSdk = 34
compileSdkExtension = 12
namespace "androidx.privacysandbox.ads.adservices"
diff --git a/privacysandbox/ads/ads-adservices/proguard-rules.pro b/privacysandbox/ads/ads-adservices/proguard-rules.pro
deleted file mode 100644
index e092df1..0000000
--- a/privacysandbox/ads/ads-adservices/proguard-rules.pro
+++ /dev/null
@@ -1,19 +0,0 @@
-# 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.
-
-# A rule that will keep the internal ContinuationOutcomeReceiver class used to
-# work with AdServicesOutcomeReceiver on Android R
--keep class androidx.privacysandbox.ads.adservices.internal.ContinuationOutcomeReceiver {
- <methods>;
-}
diff --git a/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/adid/AdIdManagerTest.kt b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/adid/AdIdManagerTest.kt
index b1b53a6..b937f1d 100644
--- a/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/adid/AdIdManagerTest.kt
+++ b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/adid/AdIdManagerTest.kt
@@ -16,7 +16,6 @@
package androidx.privacysandbox.ads.adservices.adid
-import android.adservices.common.AdServicesOutcomeReceiver
import android.content.Context
import android.os.OutcomeReceiver
import android.os.ext.SdkExtensions
@@ -53,13 +52,12 @@
private var mSession: StaticMockitoSession? = null
private val mValidAdServicesSdkExtVersion = AdServicesInfo.adServicesVersion() >= 4
private val mValidAdExtServicesSdkExtVersionS = AdServicesInfo.extServicesVersionS() >= 9
- private val mValidAdExtServicesSdkExtVersionR = AdServicesInfo.extServicesVersionR() >= 11
@Before
fun setUp() {
mContext = spy(ApplicationProvider.getApplicationContext<Context>())
- if (mValidAdExtServicesSdkExtVersionS || mValidAdExtServicesSdkExtVersionR) {
+ if (mValidAdExtServicesSdkExtVersionS) {
// setup a mockitoSession to return the mocked manager
// when the static method .get() is called
mSession =
@@ -79,16 +77,12 @@
fun testAdIdOlderVersions() {
Assume.assumeTrue("maxSdkVersion = API 33 ext 3", !mValidAdServicesSdkExtVersion)
Assume.assumeTrue("maxSdkVersion = API 31/32 ext 8", !mValidAdExtServicesSdkExtVersionS)
- Assume.assumeTrue("maxSdkVersion = API 30 ext 10", !mValidAdExtServicesSdkExtVersionR)
assertThat(AdIdManager.obtain(mContext)).isNull()
}
@Test
fun testAdIdManagerNoClassDefFoundError() {
- Assume.assumeTrue(
- "minSdkVersion = API 31/32 ext 9 or API 30 ext 11",
- mValidAdExtServicesSdkExtVersionS || mValidAdExtServicesSdkExtVersionR
- )
+ Assume.assumeTrue("minSdkVersion = API 31/32 ext 9", mValidAdExtServicesSdkExtVersionS)
`when`(android.adservices.adid.AdIdManager.get(any())).thenThrow(NoClassDefFoundError())
assertThat(AdIdManager.obtain(mContext)).isNull()
@@ -96,19 +90,13 @@
@Test
fun testAdIdAsync() {
- val validExtServicesVersion =
- mValidAdExtServicesSdkExtVersionS || mValidAdExtServicesSdkExtVersionR
Assume.assumeTrue(
- "minSdkVersion = API 33 ext 4 or API 31/32 ext 9 or API 30 ext 11",
- mValidAdServicesSdkExtVersion || validExtServicesVersion
+ "minSdkVersion = API 33 ext 4 or API 31/32 ext 9",
+ mValidAdServicesSdkExtVersion || mValidAdExtServicesSdkExtVersionS
)
- val adIdManager = mockAdIdManager(mContext, validExtServicesVersion)
-
- when (mValidAdExtServicesSdkExtVersionR) {
- true -> setupResponseR(adIdManager)
- false -> setupResponseSPlus(adIdManager)
- }
+ val adIdManager = mockAdIdManager(mContext, mValidAdExtServicesSdkExtVersionS)
+ setupResponseSPlus(adIdManager)
val managerCompat = AdIdManager.obtain(mContext)
@@ -116,10 +104,7 @@
val result = runBlocking { managerCompat!!.getAdId() }
// Verify that the compat code was invoked correctly.
- when (mValidAdExtServicesSdkExtVersionR) {
- true -> verifyOnR(adIdManager)
- false -> verifyOnSPlus(adIdManager)
- }
+ verifyOnSPlus(adIdManager)
// Verify that the result of the compat call is correct.
verifyResponse(result)
@@ -162,35 +147,6 @@
)
}
- private fun setupResponseR(adIdManager: android.adservices.adid.AdIdManager) {
- // Set up the response that AdIdManager will return when the compat code calls it.
- val adId = android.adservices.adid.AdId("1234", false)
- val answer = { args: InvocationOnMock ->
- val receiver =
- args.getArgument<
- AdServicesOutcomeReceiver<android.adservices.adid.AdId, Exception>
- >(
- 1
- )
- receiver.onResult(adId)
- null
- }
- doAnswer(answer)
- .`when`(adIdManager)
- .getAdId(
- any<Executor>(),
- any<AdServicesOutcomeReceiver<android.adservices.adid.AdId, Exception>>()
- )
- }
-
- private fun verifyOnR(adIdManager: android.adservices.adid.AdIdManager) {
- verify(adIdManager)
- .getAdId(
- any<Executor>(),
- any<AdServicesOutcomeReceiver<android.adservices.adid.AdId, Exception>>()
- )
- }
-
private fun verifyOnSPlus(adIdManager: android.adservices.adid.AdIdManager) {
verify(adIdManager)
.getAdId(
diff --git a/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManagerTest.kt b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManagerTest.kt
index 77ef543..dd323de 100644
--- a/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManagerTest.kt
+++ b/privacysandbox/ads/ads-adservices/src/androidTest/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManagerTest.kt
@@ -16,7 +16,6 @@
package androidx.privacysandbox.ads.adservices.measurement
-import android.adservices.common.AdServicesOutcomeReceiver
import android.adservices.measurement.MeasurementManager
import android.content.Context
import android.net.Uri
@@ -63,15 +62,12 @@
private var mSession: StaticMockitoSession? = null
private val mValidAdServicesSdkExtVersion = AdServicesInfo.adServicesVersion() >= 5
private val mValidAdExtServicesSdkExtVersionS = AdServicesInfo.extServicesVersionS() >= 9
- private val mValidAdExtServicesSdkExtVersionR = AdServicesInfo.extServicesVersionR() >= 11
- private val mValidExtServicesVersion =
- mValidAdExtServicesSdkExtVersionS || mValidAdExtServicesSdkExtVersionR
@Before
fun setUp() {
mContext = spy(ApplicationProvider.getApplicationContext<Context>())
- if (mValidExtServicesVersion) {
+ if (mValidAdExtServicesSdkExtVersionS) {
// setup a mockitoSession to return the mocked manager
// when the static method .get() is called
mSession =
@@ -92,16 +88,12 @@
fun testMeasurementOlderVersions() {
Assume.assumeTrue("maxSdkVersion = API 33 ext 4", !mValidAdServicesSdkExtVersion)
Assume.assumeTrue("maxSdkVersion = API 31/32 ext 8", !mValidAdExtServicesSdkExtVersionS)
- Assume.assumeTrue("maxSdkVersion = API 30 ext 10", !mValidAdExtServicesSdkExtVersionR)
assertThat(obtain(mContext)).isNull()
}
@Test
fun testMeasurementManagerNoClassDefFoundError() {
- Assume.assumeTrue(
- "minSdkVersion = API 31/32 ext 9 or API 30 ext 11",
- mValidExtServicesVersion
- )
+ Assume.assumeTrue("minSdkVersion = API 31/32 ext 9", mValidAdExtServicesSdkExtVersionS)
`when`(MeasurementManager.get(any())).thenThrow(NoClassDefFoundError())
assertThat(obtain(mContext)).isNull()
@@ -503,379 +495,6 @@
)
}
- @Test
- @SdkSuppress(maxSdkVersion = 30, minSdkVersion = 30)
- fun testDeleteRegistrationsOnR() {
- Assume.assumeTrue("minSdkVersion = API 30 ext 11", mValidAdExtServicesSdkExtVersionR)
-
- val measurementManager = mockMeasurementManager(mContext, mValidAdExtServicesSdkExtVersionR)
- val managerCompat = obtain(mContext)
-
- // Set up the request.
- val answer = { args: InvocationOnMock ->
- val receiver = args.getArgument<AdServicesOutcomeReceiver<Any, Exception>>(2)
- receiver.onResult(Object())
- null
- }
- doAnswer(answer)
- .`when`(measurementManager)
- .deleteRegistrations(
- any<android.adservices.measurement.DeletionRequest>(),
- any<Executor>(),
- any<AdServicesOutcomeReceiver<Any, java.lang.Exception>>()
- )
-
- // Actually invoke the compat code.
- runBlocking {
- val request =
- DeletionRequest(
- DeletionRequest.DELETION_MODE_ALL,
- DeletionRequest.MATCH_BEHAVIOR_DELETE,
- Instant.now(),
- Instant.now(),
- listOf(uri1),
- listOf(uri1)
- )
-
- managerCompat!!.deleteRegistrations(request)
- }
-
- // Verify that the compat code was invoked correctly.
- val captor =
- ArgumentCaptor.forClass(android.adservices.measurement.DeletionRequest::class.java)
- verify(measurementManager)
- .deleteRegistrations(
- captor.capture(),
- any<Executor>(),
- any<AdServicesOutcomeReceiver<Any, java.lang.Exception>>()
- )
-
- // Verify that the request that the compat code makes to the platform is correct.
- verifyDeletionRequest(captor.value)
- }
-
- @Test
- @SdkSuppress(maxSdkVersion = 30, minSdkVersion = 30)
- fun testRegisterSourceOnR() {
- Assume.assumeTrue("minSdkVersion = API 30 ext 11", mValidAdExtServicesSdkExtVersionR)
-
- val measurementManager = mockMeasurementManager(mContext, mValidAdExtServicesSdkExtVersionR)
- val inputEvent = mock(InputEvent::class.java)
- val managerCompat = obtain(mContext)
-
- val answer = { args: InvocationOnMock ->
- val receiver = args.getArgument<AdServicesOutcomeReceiver<Any, Exception>>(3)
- receiver.onResult(Object())
- null
- }
- doAnswer(answer)
- .`when`(measurementManager)
- .registerSource(
- any<Uri>(),
- any<InputEvent>(),
- any<Executor>(),
- any<AdServicesOutcomeReceiver<Any, Exception>>()
- )
-
- // Actually invoke the compat code.
- runBlocking { managerCompat!!.registerSource(uri1, inputEvent) }
-
- // Verify that the compat code was invoked correctly.
- val captor1 = ArgumentCaptor.forClass(Uri::class.java)
- val captor2 = ArgumentCaptor.forClass(InputEvent::class.java)
- verify(measurementManager)
- .registerSource(
- captor1.capture(),
- captor2.capture(),
- any<Executor>(),
- any<AdServicesOutcomeReceiver<Any, Exception>>()
- )
-
- // Verify that the request that the compat code makes to the platform is correct.
- assertThat(captor1.value == uri1)
- assertThat(captor2.value == inputEvent)
- }
-
- @Test
- @SdkSuppress(maxSdkVersion = 30, minSdkVersion = 30)
- fun testRegisterTriggerOnR() {
- Assume.assumeTrue("minSdkVersion = API 30 ext 11", mValidAdExtServicesSdkExtVersionR)
-
- val measurementManager = mockMeasurementManager(mContext, mValidAdExtServicesSdkExtVersionR)
- val managerCompat = obtain(mContext)
- val answer = { args: InvocationOnMock ->
- val receiver = args.getArgument<AdServicesOutcomeReceiver<Any, Exception>>(2)
- receiver.onResult(Object())
- null
- }
- doAnswer(answer)
- .`when`(measurementManager)
- .registerTrigger(
- any<Uri>(),
- any<Executor>(),
- any<AdServicesOutcomeReceiver<Any, Exception>>()
- )
-
- // Actually invoke the compat code.
- runBlocking { managerCompat!!.registerTrigger(uri1) }
-
- // Verify that the compat code was invoked correctly.
- val captor1 = ArgumentCaptor.forClass(Uri::class.java)
- verify(measurementManager)
- .registerTrigger(
- captor1.capture(),
- any<Executor>(),
- any<AdServicesOutcomeReceiver<Any, Exception>>()
- )
-
- // Verify that the request that the compat code makes to the platform is correct.
- assertThat(captor1.value).isEqualTo(uri1)
- }
-
- @Test
- @SdkSuppress(maxSdkVersion = 30, minSdkVersion = 30)
- fun testRegisterWebSourceOnR() {
- Assume.assumeTrue("minSdkVersion = API 30 ext 11", mValidAdExtServicesSdkExtVersionR)
-
- val measurementManager = mockMeasurementManager(mContext, mValidAdExtServicesSdkExtVersionR)
- val managerCompat = obtain(mContext)
- val answer = { args: InvocationOnMock ->
- val receiver = args.getArgument<AdServicesOutcomeReceiver<Any, Exception>>(2)
- receiver.onResult(Object())
- null
- }
- doAnswer(answer)
- .`when`(measurementManager)
- .registerWebSource(
- any<android.adservices.measurement.WebSourceRegistrationRequest>(),
- any<Executor>(),
- any<AdServicesOutcomeReceiver<Any, Exception>>()
- )
-
- val request =
- WebSourceRegistrationRequest.Builder(listOf(WebSourceParams(uri1, false)), uri1)
- .setAppDestination(appDestination)
- .build()
-
- // Actually invoke the compat code.
- runBlocking { managerCompat!!.registerWebSource(request) }
-
- // Verify that the compat code was invoked correctly.
- val captor1 =
- ArgumentCaptor.forClass(
- android.adservices.measurement.WebSourceRegistrationRequest::class.java
- )
- verify(measurementManager)
- .registerWebSource(
- captor1.capture(),
- any<Executor>(),
- any<AdServicesOutcomeReceiver<Any, Exception>>()
- )
-
- // Verify that the request that the compat code makes to the platform is correct.
- val actualRequest = captor1.value
- assertThat(actualRequest.topOriginUri == uri1)
- assertThat(actualRequest.sourceParams.size == 1)
- assertThat(actualRequest.appDestination == appDestination)
- assertThat(actualRequest.sourceParams[0].registrationUri == uri1)
- assertThat(!actualRequest.sourceParams[0].isDebugKeyAllowed)
- }
-
- @ExperimentalFeatures.RegisterSourceOptIn
- @Test
- @SdkSuppress(maxSdkVersion = 30, minSdkVersion = 30)
- fun testRegisterSource_allSuccessOnR() {
- Assume.assumeTrue("minSdkVersion = API 30 ext 11", mValidAdExtServicesSdkExtVersionR)
-
- val measurementManager = mockMeasurementManager(mContext, mValidAdExtServicesSdkExtVersionR)
- val mockInputEvent = mock(InputEvent::class.java)
- val managerCompat = obtain(mContext)
-
- val successCallback = { args: InvocationOnMock ->
- val receiver = args.getArgument<AdServicesOutcomeReceiver<Any, Exception>>(3)
- receiver.onResult(Object())
- null
- }
- doAnswer(successCallback)
- .`when`(measurementManager)
- .registerSource(
- any<Uri>(),
- any<InputEvent>(),
- any<Executor>(),
- any<AdServicesOutcomeReceiver<Any, Exception>>()
- )
-
- val request = SourceRegistrationRequest(listOf(uri1, uri2), mockInputEvent)
-
- // Actually invoke the compat code.
- runBlocking { managerCompat!!.registerSource(request) }
-
- // Verify that the compat code was invoked correctly.
- verify(measurementManager, times(2))
- .registerSource(
- any(),
- eq(mockInputEvent),
- any<Executor>(),
- any<AdServicesOutcomeReceiver<Any, Exception>>()
- )
- }
-
- @ExperimentalFeatures.RegisterSourceOptIn
- @Test
- @SdkSuppress(maxSdkVersion = 30, minSdkVersion = 30)
- fun testRegisterSource_15thOf20Fails_remaining5DoNotExecuteOnR() {
- Assume.assumeTrue("minSdkVersion = API 30 ext 11", mValidAdExtServicesSdkExtVersionR)
-
- val measurementManager = mockMeasurementManager(mContext, mValidAdExtServicesSdkExtVersionR)
- val mockInputEvent = mock(InputEvent::class.java)
- val managerCompat = obtain(mContext)
-
- val successCallback = { args: InvocationOnMock ->
- val receiver = args.getArgument<AdServicesOutcomeReceiver<Any, Exception>>(3)
- receiver.onResult(Object())
- null
- }
-
- val errorMessage = "some error occurred"
- val errorCallback = { args: InvocationOnMock ->
- val receiver = args.getArgument<AdServicesOutcomeReceiver<Any, Exception>>(3)
- receiver.onError(IllegalArgumentException(errorMessage))
- null
- }
- val uris =
- (0..20)
- .map { i ->
- val uri = Uri.parse("www.uri$i.com")
- if (i == 15) {
- doAnswer(errorCallback)
- .`when`(measurementManager)
- .registerSource(
- eq(uri),
- any<InputEvent>(),
- any<Executor>(),
- any<AdServicesOutcomeReceiver<Any, Exception>>()
- )
- } else {
- doAnswer(successCallback)
- .`when`(measurementManager)
- .registerSource(
- eq(uri),
- any<InputEvent>(),
- any<Executor>(),
- any<AdServicesOutcomeReceiver<Any, Exception>>()
- )
- }
- uri
- }
- .toList()
-
- val request = SourceRegistrationRequest(uris, mockInputEvent)
-
- // Actually invoke the compat code.
- runBlocking {
- try {
- managerCompat!!.registerSource(request)
- fail("Expected failure.")
- } catch (e: IllegalArgumentException) {
- assertThat(e.message).isEqualTo(errorMessage)
- }
- }
-
- // Verify that the compat code was invoked correctly.
- (0..15).forEach { i ->
- verify(measurementManager)
- .registerSource(
- eq(Uri.parse("www.uri$i.com")),
- eq(mockInputEvent),
- any<Executor>(),
- any<AdServicesOutcomeReceiver<Any, Exception>>()
- )
- }
- (16..20).forEach { i ->
- verify(measurementManager, never())
- .registerSource(
- eq(Uri.parse("www.uri$i.com")),
- eq(mockInputEvent),
- any<Executor>(),
- any<AdServicesOutcomeReceiver<Any, Exception>>()
- )
- }
- }
-
- @Test
- @SdkSuppress(maxSdkVersion = 30, minSdkVersion = 30)
- fun testRegisterWebTriggerOnR() {
- Assume.assumeTrue("minSdkVersion = API 30 ext 11", mValidAdExtServicesSdkExtVersionR)
-
- val measurementManager = mockMeasurementManager(mContext, mValidAdExtServicesSdkExtVersionR)
- val managerCompat = obtain(mContext)
- val answer = { args: InvocationOnMock ->
- val receiver = args.getArgument<AdServicesOutcomeReceiver<Any, Exception>>(2)
- receiver.onResult(Object())
- null
- }
- doAnswer(answer)
- .`when`(measurementManager)
- .registerWebTrigger(
- any<android.adservices.measurement.WebTriggerRegistrationRequest>(),
- any<Executor>(),
- any<AdServicesOutcomeReceiver<Any, Exception>>()
- )
-
- val request = WebTriggerRegistrationRequest(listOf(WebTriggerParams(uri1, false)), uri2)
-
- // Actually invoke the compat code.
- runBlocking { managerCompat!!.registerWebTrigger(request) }
-
- // Verify that the compat code was invoked correctly.
- val captor1 =
- ArgumentCaptor.forClass(
- android.adservices.measurement.WebTriggerRegistrationRequest::class.java
- )
- verify(measurementManager)
- .registerWebTrigger(
- captor1.capture(),
- any<Executor>(),
- any<AdServicesOutcomeReceiver<Any, Exception>>()
- )
-
- // Verify that the request that the compat code makes to the platform is correct.
- val actualRequest = captor1.value
- assertThat(actualRequest.destination).isEqualTo(uri2)
- assertThat(actualRequest.triggerParams.size == 1)
- assertThat(actualRequest.triggerParams[0].registrationUri == uri1)
- assertThat(!actualRequest.triggerParams[0].isDebugKeyAllowed)
- }
-
- @Test
- @SdkSuppress(maxSdkVersion = 30, minSdkVersion = 30)
- fun testMeasurementApiStatusOnR() {
- Assume.assumeTrue("minSdkVersion = API 30 ext 11", mValidAdExtServicesSdkExtVersionR)
-
- val measurementManager = mockMeasurementManager(mContext, mValidAdExtServicesSdkExtVersionR)
- callAndVerifyGetMeasurementApiStatusOnR(
- measurementManager,
- /* state= */ MeasurementManager.MEASUREMENT_API_STATE_ENABLED,
- /* expectedResult= */ MeasurementManager.MEASUREMENT_API_STATE_ENABLED
- )
- }
-
- @Test
- @SdkSuppress(maxSdkVersion = 30, minSdkVersion = 30)
- fun testMeasurementApiStatusUnknownOnR() {
- Assume.assumeTrue("minSdkVersion = API 30 ext 11", mValidAdExtServicesSdkExtVersionR)
-
- val measurementManager = mockMeasurementManager(mContext, mValidAdExtServicesSdkExtVersionR)
-
- // Call with a value greater than values returned in SdkExtensions.AD_SERVICES = 5
- // Since the compat code does not know the returned state, it sets it to UNKNOWN.
- callAndVerifyGetMeasurementApiStatusOnR(
- measurementManager,
- /* state= */ 6,
- /* expectedResult= */ 5
- )
- }
-
@SdkSuppress(minSdkVersion = 30)
companion object {
@@ -925,38 +544,6 @@
assertThat(actualResult == expectedResult)
}
- private fun callAndVerifyGetMeasurementApiStatusOnR(
- measurementManager: android.adservices.measurement.MeasurementManager,
- state: Int,
- expectedResult: Int
- ) {
- val managerCompat = obtain(mContext)
- val answer = { args: InvocationOnMock ->
- val receiver = args.getArgument<AdServicesOutcomeReceiver<Int, Exception>>(1)
- receiver.onResult(state)
- null
- }
- doAnswer(answer)
- .`when`(measurementManager)
- .getMeasurementApiStatus(
- any<Executor>(),
- any<AdServicesOutcomeReceiver<Int, Exception>>()
- )
-
- // Actually invoke the compat code.
- val actualResult = runBlocking { managerCompat!!.getMeasurementApiStatus() }
-
- // Verify that the compat code was invoked correctly.
- verify(measurementManager)
- .getMeasurementApiStatus(
- any<Executor>(),
- any<AdServicesOutcomeReceiver<Int, Exception>>()
- )
-
- // Verify that the request that the compat code makes to the platform is correct.
- assertThat(actualResult == expectedResult)
- }
-
private fun verifyDeletionRequest(request: android.adservices.measurement.DeletionRequest) {
// Set up the request that we expect the compat code to invoke.
val expectedRequest =
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adid/AdIdManager.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adid/AdIdManager.kt
index a0eb9df..df5782a 100644
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adid/AdIdManager.kt
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adid/AdIdManager.kt
@@ -57,10 +57,6 @@
BackCompatManager.getManager(context, "AdIdManager") {
AdIdManagerApi31Ext9Impl(context)
}
- } else if (AdServicesInfo.extServicesVersionR() >= 11) {
- BackCompatManager.getManager(context, "AdIdManager") {
- AdIdManagerApi30Ext11Impl(context)
- }
} else {
null
}
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adid/AdIdManagerApi30Ext11Impl.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adid/AdIdManagerApi30Ext11Impl.kt
deleted file mode 100644
index 5cd2bf6..0000000
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adid/AdIdManagerApi30Ext11Impl.kt
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.privacysandbox.ads.adservices.adid
-
-import android.adservices.common.AdServicesPermissions
-import android.annotation.SuppressLint
-import android.content.Context
-import android.os.Build
-import androidx.annotation.DoNotInline
-import androidx.annotation.RequiresExtension
-import androidx.annotation.RequiresPermission
-import androidx.annotation.RestrictTo
-import androidx.privacysandbox.ads.adservices.internal.asAdServicesOutcomeReceiver
-import kotlinx.coroutines.suspendCancellableCoroutine
-
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-@SuppressLint("ClassVerificationFailure", "NewApi")
-@RequiresExtension(extension = Build.VERSION_CODES.R, version = 11)
-open class AdIdManagerApi30Ext11Impl(context: Context) : AdIdManager() {
- private val mAdIdManager: android.adservices.adid.AdIdManager =
- android.adservices.adid.AdIdManager.get(context)
-
- @DoNotInline
- @RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_AD_ID)
- override suspend fun getAdId(): AdId {
- return convertResponse(getAdIdAsyncInternal())
- }
-
- @RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_AD_ID)
- private suspend fun getAdIdAsyncInternal(): android.adservices.adid.AdId =
- suspendCancellableCoroutine { continuation ->
- mAdIdManager.getAdId(Runnable::run, continuation.asAdServicesOutcomeReceiver())
- }
-
- private fun convertResponse(response: android.adservices.adid.AdId): AdId {
- return AdId(response.adId, response.isLimitAdTrackingEnabled)
- }
-}
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/internal/AdServicesInfo.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/internal/AdServicesInfo.kt
index a981294..b28fef0 100644
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/internal/AdServicesInfo.kt
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/internal/AdServicesInfo.kt
@@ -38,14 +38,6 @@
}
}
- fun extServicesVersionR(): Int {
- return if (Build.VERSION.SDK_INT == 30) {
- Extensions30ExtImpl.getAdExtServicesVersionR()
- } else {
- 0
- }
- }
-
@RequiresApi(30)
private object Extensions30Impl {
@DoNotInline
@@ -55,12 +47,8 @@
@RequiresApi(30)
private object Extensions30ExtImpl {
// For ExtServices, there is no AD_SERVICES extension version, so we need to check
- // for the build version. Use S for now, but this can be changed to R when we add
- // support for R later.
+ // for the build version for S.
@DoNotInline
fun getAdExtServicesVersionS() = SdkExtensions.getExtensionVersion(Build.VERSION_CODES.S)
-
- @DoNotInline
- fun getAdExtServicesVersionR() = SdkExtensions.getExtensionVersion(Build.VERSION_CODES.R)
}
}
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/internal/AdServicesOutcomeReceiver.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/internal/AdServicesOutcomeReceiver.kt
deleted file mode 100644
index 6f87e40..0000000
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/internal/AdServicesOutcomeReceiver.kt
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.privacysandbox.ads.adservices.internal
-
-import android.adservices.common.AdServicesOutcomeReceiver
-import android.annotation.SuppressLint
-import android.os.Build
-import androidx.annotation.RequiresExtension
-import java.util.concurrent.atomic.AtomicBoolean
-import kotlin.coroutines.Continuation
-import kotlin.coroutines.resume
-import kotlin.coroutines.resumeWithException
-
-/*
- This file is a modified version OutcomeReceiver.kt in androidx.core.os, designed to provide the same
- functionality with the AdServicesOutcomeReceiver, to keep the implementation of the backward compatible
- classes as close to identical as possible.
-*/
-
-@RequiresExtension(extension = Build.VERSION_CODES.R, version = 11)
-fun <R, E : Throwable> Continuation<R>.asAdServicesOutcomeReceiver():
- AdServicesOutcomeReceiver<R, E> = ContinuationOutcomeReceiver(this)
-
-@SuppressLint("NewApi")
-@RequiresExtension(extension = Build.VERSION_CODES.R, version = 11)
-private class ContinuationOutcomeReceiver<R, E : Throwable>(
- private val continuation: Continuation<R>
-) : AdServicesOutcomeReceiver<R, E>, AtomicBoolean(false) {
- @Suppress("WRONG_NULLABILITY_FOR_JAVA_OVERRIDE")
- override fun onResult(result: R) {
- // Do not attempt to resume more than once, even if the caller of the returned
- // OutcomeReceiver is buggy and tries anyway.
- if (compareAndSet(false, true)) {
- continuation.resume(result)
- }
- }
-
- override fun onError(error: E) {
- // Do not attempt to resume more than once, even if the caller of the returned
- // OutcomeReceiver is buggy and tries anyway.
- if (compareAndSet(false, true)) {
- continuation.resumeWithException(error)
- }
- }
-
- override fun toString() = "ContinuationOutcomeReceiver(outcomeReceived = ${get()})"
-}
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/internal/BackCompatManager.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/internal/BackCompatManager.kt
index f0f6005..f796274 100644
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/internal/BackCompatManager.kt
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/internal/BackCompatManager.kt
@@ -29,7 +29,6 @@
Log.d(
tag,
"Unable to find adservices code, check manifest for uses-library tag, " +
- "versionR=${AdServicesInfo.extServicesVersionR()}, " +
"versionS=${AdServicesInfo.extServicesVersionS()}"
)
return null
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/DeletionRequest.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/DeletionRequest.kt
index a482855..14782a9 100644
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/DeletionRequest.kt
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/DeletionRequest.kt
@@ -100,7 +100,6 @@
@SuppressLint("ClassVerificationFailure", "NewApi")
@RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
@RequiresExtension(extension = Build.VERSION_CODES.S, version = 9)
- @RequiresExtension(extension = Build.VERSION_CODES.R, version = 11)
internal fun convertToAdServices(): android.adservices.measurement.DeletionRequest {
return android.adservices.measurement.DeletionRequest.Builder()
.setDeletionMode(deletionMode)
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManager.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManager.kt
index ebfdc16..ea8584b 100644
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManager.kt
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManager.kt
@@ -149,10 +149,6 @@
BackCompatManager.getManager(context, "MeasurementManager") {
MeasurementManagerApi31Ext9Impl(context)
}
- } else if (AdServicesInfo.extServicesVersionR() >= 11) {
- BackCompatManager.getManager(context, "MeasurementManager") {
- MeasurementManagerApi30Ext11Impl(context)
- }
} else {
null
}
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManagerApi30Ext11Impl.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManagerApi30Ext11Impl.kt
deleted file mode 100644
index 86ed67c..0000000
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/MeasurementManagerApi30Ext11Impl.kt
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.privacysandbox.ads.adservices.measurement
-
-import android.adservices.common.AdServicesPermissions
-import android.annotation.SuppressLint
-import android.content.Context
-import android.net.Uri
-import android.os.Build
-import android.view.InputEvent
-import androidx.annotation.DoNotInline
-import androidx.annotation.RequiresExtension
-import androidx.annotation.RequiresPermission
-import androidx.annotation.RestrictTo
-import androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures
-import androidx.privacysandbox.ads.adservices.internal.asAdServicesOutcomeReceiver
-import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.suspendCancellableCoroutine
-
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-@SuppressLint("ClassVerificationFailure", "NewApi")
-@RequiresExtension(extension = Build.VERSION_CODES.R, version = 11)
-class MeasurementManagerApi30Ext11Impl(context: Context) : MeasurementManager() {
- private val mMeasurementManager: android.adservices.measurement.MeasurementManager =
- android.adservices.measurement.MeasurementManager.get(context)
-
- @DoNotInline
- override suspend fun deleteRegistrations(deletionRequest: DeletionRequest) {
- suspendCancellableCoroutine<Any> { continuation ->
- mMeasurementManager.deleteRegistrations(
- deletionRequest.convertToAdServices(),
- Runnable::run,
- continuation.asAdServicesOutcomeReceiver()
- )
- }
- }
-
- @DoNotInline
- @RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION)
- override suspend fun registerSource(attributionSource: Uri, inputEvent: InputEvent?) {
- suspendCancellableCoroutine<Any> { continuation ->
- mMeasurementManager.registerSource(
- attributionSource,
- inputEvent,
- Runnable::run,
- continuation.asAdServicesOutcomeReceiver()
- )
- }
- }
-
- @DoNotInline
- @RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION)
- override suspend fun registerTrigger(trigger: Uri) {
- suspendCancellableCoroutine<Any> { continuation ->
- mMeasurementManager.registerTrigger(
- trigger,
- Runnable::run,
- continuation.asAdServicesOutcomeReceiver()
- )
- }
- }
-
- @DoNotInline
- @RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION)
- override suspend fun registerWebSource(request: WebSourceRegistrationRequest) {
- suspendCancellableCoroutine<Any> { continuation ->
- mMeasurementManager.registerWebSource(
- request.convertToAdServices(),
- Runnable::run,
- continuation.asAdServicesOutcomeReceiver()
- )
- }
- }
-
- @DoNotInline
- @ExperimentalFeatures.RegisterSourceOptIn
- @RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION)
- override suspend fun registerSource(request: SourceRegistrationRequest): Unit = coroutineScope {
- request.registrationUris.forEach { uri ->
- launch {
- suspendCancellableCoroutine<Any> { continuation ->
- mMeasurementManager.registerSource(
- uri,
- request.inputEvent,
- Runnable::run,
- continuation.asAdServicesOutcomeReceiver()
- )
- }
- }
- }
- }
-
- @DoNotInline
- @RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION)
- override suspend fun registerWebTrigger(request: WebTriggerRegistrationRequest) {
- suspendCancellableCoroutine<Any> { continuation ->
- mMeasurementManager.registerWebTrigger(
- request.convertToAdServices(),
- Runnable::run,
- continuation.asAdServicesOutcomeReceiver()
- )
- }
- }
-
- @DoNotInline
- @RequiresPermission(AdServicesPermissions.ACCESS_ADSERVICES_ATTRIBUTION)
- override suspend fun getMeasurementApiStatus(): Int =
- suspendCancellableCoroutine { continuation ->
- mMeasurementManager.getMeasurementApiStatus(
- Runnable::run,
- continuation.asAdServicesOutcomeReceiver()
- )
- }
-}
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/WebSourceParams.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/WebSourceParams.kt
index c7bc6fe..1d3218f 100644
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/WebSourceParams.kt
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/WebSourceParams.kt
@@ -54,7 +54,6 @@
@SuppressLint("ClassVerificationFailure", "NewApi")
@RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
@RequiresExtension(extension = Build.VERSION_CODES.S, version = 9)
- @RequiresExtension(extension = Build.VERSION_CODES.R, version = 11)
internal fun convertWebSourceParams(
request: List<WebSourceParams>
): List<android.adservices.measurement.WebSourceParams> {
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/WebSourceRegistrationRequest.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/WebSourceRegistrationRequest.kt
index 5d48214..6499e73 100644
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/WebSourceRegistrationRequest.kt
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/WebSourceRegistrationRequest.kt
@@ -95,7 +95,6 @@
@SuppressLint("ClassVerificationFailure", "NewApi")
@RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
@RequiresExtension(extension = Build.VERSION_CODES.S, version = 9)
- @RequiresExtension(extension = Build.VERSION_CODES.R, version = 11)
internal fun convertToAdServices():
android.adservices.measurement.WebSourceRegistrationRequest {
return android.adservices.measurement.WebSourceRegistrationRequest.Builder(
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/WebTriggerParams.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/WebTriggerParams.kt
index 2163701..bc3eefc 100644
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/WebTriggerParams.kt
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/WebTriggerParams.kt
@@ -54,7 +54,6 @@
@SuppressLint("ClassVerificationFailure", "NewApi")
@RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
@RequiresExtension(extension = Build.VERSION_CODES.S, version = 9)
- @RequiresExtension(extension = Build.VERSION_CODES.R, version = 11)
internal fun convertWebTriggerParams(
request: List<WebTriggerParams>
): List<android.adservices.measurement.WebTriggerParams> {
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/WebTriggerRegistrationRequest.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/WebTriggerRegistrationRequest.kt
index f521339..7384929 100644
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/WebTriggerRegistrationRequest.kt
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/measurement/WebTriggerRegistrationRequest.kt
@@ -52,7 +52,6 @@
@SuppressLint("ClassVerificationFailure", "NewApi")
@RequiresExtension(extension = SdkExtensions.AD_SERVICES, version = 4)
@RequiresExtension(extension = Build.VERSION_CODES.S, version = 9)
- @RequiresExtension(extension = Build.VERSION_CODES.R, version = 11)
internal fun convertToAdServices():
android.adservices.measurement.WebTriggerRegistrationRequest {
return android.adservices.measurement.WebTriggerRegistrationRequest.Builder(
diff --git a/privacysandbox/tools/tools-apicompiler/build.gradle b/privacysandbox/tools/tools-apicompiler/build.gradle
index ec701e3..27258e4 100644
--- a/privacysandbox/tools/tools-apicompiler/build.gradle
+++ b/privacysandbox/tools/tools-apicompiler/build.gradle
@@ -21,6 +21,8 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
+
+import androidx.build.KotlinTarget
import androidx.build.LibraryType
import androidx.build.SdkHelperKt
import androidx.build.AndroidXConfig
@@ -80,4 +82,5 @@
type = LibraryType.ANNOTATION_PROCESSOR
inceptionYear = "2022"
description = "Compiler for Privacy Sandbox API annotations."
+ kotlinTarget = KotlinTarget.KOTLIN_1_9
}
diff --git a/room/room-compiler/build.gradle b/room/room-compiler/build.gradle
index ff0e1d5..060f27d 100644
--- a/room/room-compiler/build.gradle
+++ b/room/room-compiler/build.gradle
@@ -23,6 +23,7 @@
*/
import androidx.build.BuildOnServerKt
+import androidx.build.KotlinTarget
import androidx.build.LibraryType
import androidx.build.SdkHelperKt
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
@@ -240,4 +241,5 @@
type = LibraryType.ANNOTATION_PROCESSOR
inceptionYear = "2017"
description = "Android Room annotation processor"
+ kotlinTarget = KotlinTarget.KOTLIN_1_9
}
diff --git a/room/room-migration/build.gradle b/room/room-migration/build.gradle
index b10beda..8051a15 100644
--- a/room/room-migration/build.gradle
+++ b/room/room-migration/build.gradle
@@ -22,6 +22,8 @@
* modifying its settings.
*/
+
+import androidx.build.KotlinTarget
import androidx.build.PlatformIdentifier
import androidx.build.LibraryType
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
@@ -88,4 +90,5 @@
description = "Android Room Migration"
legacyDisableKotlinStrictApiMode = true
metalavaK2UastEnabled = false
+ kotlinTarget = KotlinTarget.KOTLIN_1_9
}
diff --git a/room/room-testing/build.gradle b/room/room-testing/build.gradle
index 13685a4..14a4823 100644
--- a/room/room-testing/build.gradle
+++ b/room/room-testing/build.gradle
@@ -22,6 +22,8 @@
* modifying its settings.
*/
+
+import androidx.build.KotlinTarget
import androidx.build.PlatformIdentifier
import androidx.build.LibraryType
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
@@ -97,4 +99,5 @@
description = "Android Room Testing"
legacyDisableKotlinStrictApiMode = true
metalavaK2UastEnabled = false
+ kotlinTarget = KotlinTarget.KOTLIN_1_9
}
diff --git a/settings.gradle b/settings.gradle
index 257a374..ded0605 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -658,6 +658,7 @@
includeProject(":datastore:datastore-rxjava2", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
includeProject(":datastore:datastore-rxjava3", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
includeProject(":datastore:datastore-sampleapp", [BuildType.MAIN, BuildType.INFRAROGUE, BuildType.KMP])
+includeProject(":datastore:integration-tests:testapp", [BuildType.MAIN])
includeProject(":documentfile:documentfile", [BuildType.MAIN])
includeProject(":draganddrop:draganddrop", [BuildType.MAIN])
includeProject(":draganddrop:integration-tests:sampleapp", [BuildType.MAIN])
diff --git a/stableaidl/stableaidl-gradle-plugin/build.gradle b/stableaidl/stableaidl-gradle-plugin/build.gradle
index 4518da8..a0c775c 100644
--- a/stableaidl/stableaidl-gradle-plugin/build.gradle
+++ b/stableaidl/stableaidl-gradle-plugin/build.gradle
@@ -21,7 +21,8 @@
* Please use that script when creating a new project, rather than copying an existing project and
* modifying its settings.
*/
-import androidx.build.*
+import androidx.build.KotlinTarget
+import androidx.build.LibraryType
plugins {
id("AndroidXPlugin")
@@ -85,4 +86,5 @@
type = LibraryType.GRADLE_PLUGIN
inceptionYear = "2022"
description = "Stable AIDL Gradle Plugin"
+ kotlinTarget = KotlinTarget.KOTLIN_1_9
}
diff --git a/wear/compose/compose-foundation/build.gradle b/wear/compose/compose-foundation/build.gradle
index aa8f032..4844a72 100644
--- a/wear/compose/compose-foundation/build.gradle
+++ b/wear/compose/compose-foundation/build.gradle
@@ -32,14 +32,14 @@
}
dependencies {
- api("androidx.compose.foundation:foundation:1.7.0-rc01")
- api("androidx.compose.ui:ui:1.7.0-rc01")
- api("androidx.compose.ui:ui-text:1.7.0-rc01")
- api("androidx.compose.runtime:runtime:1.7.0-rc01")
+ api("androidx.compose.foundation:foundation:1.7.0")
+ api("androidx.compose.ui:ui:1.7.0")
+ api("androidx.compose.ui:ui-text:1.7.0")
+ api("androidx.compose.runtime:runtime:1.7.0")
implementation(libs.kotlinStdlib)
- implementation("androidx.compose.foundation:foundation-layout:1.7.0-rc01")
- implementation("androidx.compose.ui:ui-util:1.7.0-rc01")
+ implementation("androidx.compose.foundation:foundation-layout:1.7.0")
+ implementation("androidx.compose.ui:ui-util:1.7.0")
implementation("androidx.lifecycle:lifecycle-runtime-compose:2.7.0")
implementation("androidx.core:core:1.12.0")
implementation("androidx.profileinstaller:profileinstaller:1.3.1")
diff --git a/wear/compose/compose-material-core/build.gradle b/wear/compose/compose-material-core/build.gradle
index e380531..df1b6c6 100644
--- a/wear/compose/compose-material-core/build.gradle
+++ b/wear/compose/compose-material-core/build.gradle
@@ -33,16 +33,16 @@
}
dependencies {
- api("androidx.compose.foundation:foundation:1.7.0-rc01")
- api("androidx.compose.ui:ui:1.7.0-rc01")
- api("androidx.compose.ui:ui-text:1.7.0-rc01")
- api("androidx.compose.runtime:runtime:1.7.0-rc01")
+ api("androidx.compose.foundation:foundation:1.7.0")
+ api("androidx.compose.ui:ui:1.7.0")
+ api("androidx.compose.ui:ui-text:1.7.0")
+ api("androidx.compose.runtime:runtime:1.7.0")
implementation(libs.kotlinStdlib)
- implementation("androidx.compose.animation:animation:1.7.0-rc01")
- implementation("androidx.compose.material:material-icons-core:1.7.0-rc01")
- implementation("androidx.compose.material:material-ripple:1.7.0-rc01")
- implementation("androidx.compose.ui:ui-util:1.7.0-rc01")
+ implementation("androidx.compose.animation:animation:1.7.0")
+ implementation("androidx.compose.material:material-icons-core:1.7.0")
+ implementation("androidx.compose.material:material-ripple:1.7.0")
+ implementation("androidx.compose.ui:ui-util:1.7.0")
implementation(project(":wear:compose:compose-foundation"))
implementation("androidx.profileinstaller:profileinstaller:1.3.1")
diff --git a/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/Resources.kt b/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/Resources.kt
index 1a967f1..7b9faf1 100644
--- a/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/Resources.kt
+++ b/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/Resources.kt
@@ -62,11 +62,11 @@
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@Composable
-fun screenHeightDp() = LocalContext.current.resources.configuration.screenHeightDp
+fun screenHeightDp() = LocalConfiguration.current.screenHeightDp
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@Composable
-fun screenWidthDp() = LocalContext.current.resources.configuration.screenWidthDp
+fun screenWidthDp() = LocalConfiguration.current.screenWidthDp
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@Composable
diff --git a/wear/compose/compose-material/build.gradle b/wear/compose/compose-material/build.gradle
index 864af0d..1d2fc79 100644
--- a/wear/compose/compose-material/build.gradle
+++ b/wear/compose/compose-material/build.gradle
@@ -31,17 +31,17 @@
}
dependencies {
- api("androidx.compose.foundation:foundation:1.7.0-rc01")
- api("androidx.compose.ui:ui:1.7.0-rc01")
- api("androidx.compose.ui:ui-text:1.7.0-rc01")
- api("androidx.compose.runtime:runtime:1.7.0-rc01")
+ api("androidx.compose.foundation:foundation:1.7.0")
+ api("androidx.compose.ui:ui:1.7.0")
+ api("androidx.compose.ui:ui-text:1.7.0")
+ api("androidx.compose.runtime:runtime:1.7.0")
api(project(":wear:compose:compose-foundation"))
implementation(libs.kotlinStdlib)
- implementation("androidx.compose.animation:animation:1.7.0-rc01")
- implementation("androidx.compose.material:material-icons-core:1.7.0-rc01")
- implementation("androidx.compose.material:material-ripple:1.7.0-rc01")
- implementation("androidx.compose.ui:ui-util:1.7.0-rc01")
+ implementation("androidx.compose.animation:animation:1.7.0")
+ implementation("androidx.compose.material:material-icons-core:1.7.0")
+ implementation("androidx.compose.material:material-ripple:1.7.0")
+ implementation("androidx.compose.ui:ui-util:1.7.0")
implementation(project(":wear:compose:compose-material-core"))
implementation("androidx.profileinstaller:profileinstaller:1.3.1")
implementation("androidx.lifecycle:lifecycle-common:2.7.0")
diff --git a/wear/compose/compose-material3/api/current.txt b/wear/compose/compose-material3/api/current.txt
index 3b402a4..b0b63b9 100644
--- a/wear/compose/compose-material3/api/current.txt
+++ b/wear/compose/compose-material3/api/current.txt
@@ -1,6 +1,28 @@
// Signature format: 4.0
package androidx.wear.compose.material3 {
+ public final class AlertDialogDefaults {
+ method @androidx.compose.runtime.Composable public void BottomButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public void ConfirmButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public void DismissButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public void GroupSeparator();
+ method @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.PaddingValues contentPadding(boolean hasBottomButton);
+ method public kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.RowScope,kotlin.Unit> getConfirmIcon();
+ method public kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.RowScope,kotlin.Unit> getDismissIcon();
+ method public float getEdgeButtonExtraTopPadding();
+ method public androidx.compose.foundation.layout.Arrangement.Vertical getVerticalArrangement();
+ property public final kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.RowScope,kotlin.Unit> ConfirmIcon;
+ property public final kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.RowScope,kotlin.Unit> DismissIcon;
+ property public final androidx.compose.foundation.layout.Arrangement.Vertical VerticalArrangement;
+ property public final float edgeButtonExtraTopPadding;
+ field public static final androidx.wear.compose.material3.AlertDialogDefaults INSTANCE;
+ }
+
+ public final class AlertDialogKt {
+ method @androidx.compose.runtime.Composable public static void AlertDialog(boolean show, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> bottomButton, kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? text, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.ui.window.DialogProperties properties, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.lazy.ScalingLazyListScope,kotlin.Unit>? content);
+ method @androidx.compose.runtime.Composable public static void AlertDialog(boolean show, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> confirmButton, kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> dismissButton, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? text, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.ui.window.DialogProperties properties, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.lazy.ScalingLazyListScope,kotlin.Unit>? content);
+ }
+
@RequiresApi(31) public final class AnimatedTextDefaults {
field public static final int CacheSize = 5; // 0x5
field public static final androidx.wear.compose.material3.AnimatedTextDefaults INSTANCE;
@@ -298,6 +320,45 @@
method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public static long contentColorFor(long backgroundColor);
}
+ public final class ConfirmationColors {
+ ctor public ConfirmationColors(long iconColor, long iconContainerColor, long textColor);
+ method public long getIconColor();
+ method public long getIconContainerColor();
+ method public long getTextColor();
+ property public final long iconColor;
+ property public final long iconContainerColor;
+ property public final long textColor;
+ }
+
+ public final class ConfirmationDefaults {
+ method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ConfirmationColors confirmationColors();
+ method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ConfirmationColors confirmationColors(optional long iconColor, optional long iconContainerColor, optional long textColor);
+ method @androidx.compose.runtime.Composable public kotlin.jvm.functions.Function1<androidx.wear.compose.foundation.CurvedScope,kotlin.Unit> curvedText(String text, optional androidx.wear.compose.foundation.CurvedTextStyle style);
+ method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ConfirmationColors failureColors();
+ method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ConfirmationColors failureColors(optional long iconColor, optional long iconContainerColor, optional long textColor);
+ method @androidx.compose.runtime.Composable public kotlin.jvm.functions.Function1<androidx.wear.compose.foundation.CurvedScope,kotlin.Unit> failureText();
+ method public kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.BoxScope,kotlin.Unit> getFailureIcon();
+ method public float getIconSize();
+ method public float getSmallIconSize();
+ method public kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.BoxScope,kotlin.Unit> getSuccessIcon();
+ method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ConfirmationColors successColors();
+ method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ConfirmationColors successColors(optional long iconColor, optional long iconContainerColor, optional long textColor);
+ method @androidx.compose.runtime.Composable public kotlin.jvm.functions.Function1<androidx.wear.compose.foundation.CurvedScope,kotlin.Unit> successText();
+ property public final kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.BoxScope,kotlin.Unit> FailureIcon;
+ property public final float IconSize;
+ property public final float SmallIconSize;
+ property public final kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.BoxScope,kotlin.Unit> SuccessIcon;
+ field public static final long ConfirmationDurationMillis = 4000L; // 0xfa0L
+ field public static final androidx.wear.compose.material3.ConfirmationDefaults INSTANCE;
+ }
+
+ public final class ConfirmationKt {
+ method @androidx.compose.runtime.Composable public static void Confirmation(boolean show, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>? text, optional androidx.compose.ui.Modifier modifier, optional androidx.wear.compose.material3.ConfirmationColors colors, optional androidx.compose.ui.window.DialogProperties properties, optional long durationMillis, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void Confirmation(boolean show, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.CurvedScope,kotlin.Unit>? curvedText, optional androidx.compose.ui.Modifier modifier, optional androidx.wear.compose.material3.ConfirmationColors colors, optional androidx.compose.ui.window.DialogProperties properties, optional long durationMillis, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void FailureConfirmation(boolean show, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.CurvedScope,kotlin.Unit>? curvedText, optional androidx.wear.compose.material3.ConfirmationColors colors, optional androidx.compose.ui.window.DialogProperties properties, optional long durationMillis, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void SuccessConfirmation(boolean show, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.CurvedScope,kotlin.Unit>? curvedText, optional androidx.wear.compose.material3.ConfirmationColors colors, optional androidx.compose.ui.window.DialogProperties properties, optional long durationMillis, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+ }
+
public final class ContentColorKt {
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.graphics.Color> getLocalContentColor();
property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.graphics.Color> LocalContentColor;
@@ -401,8 +462,8 @@
method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconButtonColors iconButtonColors();
method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconButtonColors iconButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
method public float iconSizeFor(float size);
- method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ToggleButtonColors iconToggleButtonColors();
- method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ToggleButtonColors iconToggleButtonColors(optional long checkedContainerColor, optional long checkedContentColor, optional long uncheckedContainerColor, optional long uncheckedContentColor, optional long disabledCheckedContainerColor, optional long disabledCheckedContentColor, optional long disabledUncheckedContainerColor, optional long disabledUncheckedContentColor);
+ method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconToggleButtonColors iconToggleButtonColors();
+ method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconToggleButtonColors iconToggleButtonColors(optional long checkedContainerColor, optional long checkedContentColor, optional long uncheckedContainerColor, optional long uncheckedContentColor, optional long disabledCheckedContainerColor, optional long disabledCheckedContentColor, optional long disabledUncheckedContainerColor, optional long disabledUncheckedContentColor);
method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconButtonColors outlinedIconButtonColors();
method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconButtonColors outlinedIconButtonColors(optional long contentColor, optional long disabledContentColor);
property public final float DefaultButtonSize;
@@ -422,7 +483,7 @@
method @androidx.compose.runtime.Composable public static void FilledIconButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional String? onLongClickLabel, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.IconButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void FilledTonalIconButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional String? onLongClickLabel, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.IconButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void IconButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional String? onLongClickLabel, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.IconButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
- method @androidx.compose.runtime.Composable public static void IconToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material3.ToggleButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.foundation.BorderStroke? border, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void IconToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material3.IconToggleButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.foundation.BorderStroke? border, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void OutlinedIconButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional String? onLongClickLabel, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.IconButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
}
@@ -432,6 +493,26 @@
method @androidx.compose.runtime.Composable public static void Icon(androidx.compose.ui.graphics.vector.ImageVector imageVector, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
}
+ @androidx.compose.runtime.Immutable public final class IconToggleButtonColors {
+ ctor public IconToggleButtonColors(long checkedContainerColor, long checkedContentColor, long uncheckedContainerColor, long uncheckedContentColor, long disabledCheckedContainerColor, long disabledCheckedContentColor, long disabledUncheckedContainerColor, long disabledUncheckedContentColor);
+ method public long getCheckedContainerColor();
+ method public long getCheckedContentColor();
+ method public long getDisabledCheckedContainerColor();
+ method public long getDisabledCheckedContentColor();
+ method public long getDisabledUncheckedContainerColor();
+ method public long getDisabledUncheckedContentColor();
+ method public long getUncheckedContainerColor();
+ method public long getUncheckedContentColor();
+ property public final long checkedContainerColor;
+ property public final long checkedContentColor;
+ property public final long disabledCheckedContainerColor;
+ property public final long disabledCheckedContentColor;
+ property public final long disabledUncheckedContainerColor;
+ property public final long disabledUncheckedContentColor;
+ property public final long uncheckedContainerColor;
+ property public final long uncheckedContentColor;
+ }
+
@SuppressCompatibility @androidx.compose.runtime.Immutable @androidx.wear.compose.material3.ExperimentalWearMaterial3Api public final class InlineSliderColors {
ctor public InlineSliderColors(long containerColor, long buttonIconColor, long selectedBarColor, long unselectedBarColor, long barSeparatorColor, long disabledContainerColor, long disabledButtonIconColor, long disabledSelectedBarColor, long disabledUnselectedBarColor, long disabledBarSeparatorColor);
method public long getBarSeparatorColor();
@@ -548,6 +629,34 @@
method public static androidx.wear.compose.material3.MotionScheme standardMotionScheme();
}
+ public final class OpenOnPhoneDialogColors {
+ ctor public OpenOnPhoneDialogColors(long iconColor, long iconContainerColor, long progressIndicatorColor, long progressTrackColor, long textColor);
+ method public long getIconColor();
+ method public long getIconContainerColor();
+ method public long getProgressIndicatorColor();
+ method public long getProgressTrackColor();
+ method public long getTextColor();
+ property public final long iconColor;
+ property public final long iconContainerColor;
+ property public final long progressIndicatorColor;
+ property public final long progressTrackColor;
+ property public final long textColor;
+ }
+
+ public final class OpenOnPhoneDialogDefaults {
+ method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.OpenOnPhoneDialogColors colors();
+ method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.OpenOnPhoneDialogColors colors(optional long iconColor, optional long iconContainerColor, optional long progressIndicatorColor, optional long progressTrackColor, optional long textColor);
+ method @androidx.compose.runtime.Composable public kotlin.jvm.functions.Function1<androidx.wear.compose.foundation.CurvedScope,kotlin.Unit> curvedText(optional String text, optional androidx.wear.compose.foundation.CurvedTextStyle style);
+ method public kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.BoxScope,kotlin.Unit> getIcon();
+ property public final kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.BoxScope,kotlin.Unit> Icon;
+ field public static final long DurationMillis = 4000L; // 0xfa0L
+ field public static final androidx.wear.compose.material3.OpenOnPhoneDialogDefaults INSTANCE;
+ }
+
+ public final class OpenOnPhoneDialogKt {
+ method @androidx.compose.runtime.Composable public static void OpenOnPhoneDialog(boolean show, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.CurvedScope,kotlin.Unit>? curvedText, optional androidx.wear.compose.material3.OpenOnPhoneDialogColors colors, optional androidx.compose.ui.window.DialogProperties properties, optional long durationMillis, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+ }
+
public final class PickerDefaults {
method @androidx.compose.runtime.Composable public androidx.compose.foundation.gestures.FlingBehavior flingBehavior(androidx.wear.compose.material3.PickerState state, optional androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> decay);
method public float getGradientRatio();
@@ -1119,8 +1228,8 @@
method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.TextButtonColors outlinedTextButtonColors(optional long contentColor, optional long disabledContentColor);
method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.TextButtonColors textButtonColors();
method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.TextButtonColors textButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
- method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ToggleButtonColors textToggleButtonColors();
- method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ToggleButtonColors textToggleButtonColors(optional long checkedContainerColor, optional long checkedContentColor, optional long uncheckedContainerColor, optional long uncheckedContentColor, optional long disabledCheckedContainerColor, optional long disabledCheckedContentColor, optional long disabledUncheckedContainerColor, optional long disabledUncheckedContentColor);
+ method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.TextToggleButtonColors textToggleButtonColors();
+ method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.TextToggleButtonColors textToggleButtonColors(optional long checkedContainerColor, optional long checkedContentColor, optional long uncheckedContainerColor, optional long uncheckedContentColor, optional long disabledCheckedContainerColor, optional long disabledCheckedContentColor, optional long disabledUncheckedContainerColor, optional long disabledUncheckedContentColor);
property public final float DefaultButtonSize;
property public final float LargeButtonSize;
property public final float SmallButtonSize;
@@ -1134,7 +1243,7 @@
public final class TextButtonKt {
method @androidx.compose.runtime.Composable public static void TextButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional String? onLongClickLabel, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.TextButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
- method @androidx.compose.runtime.Composable public static void TextToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material3.ToggleButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.foundation.BorderStroke? border, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void TextToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material3.TextToggleButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.foundation.BorderStroke? border, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
}
public final class TextKt {
@@ -1151,6 +1260,26 @@
property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.TextStyle> LocalTextStyle;
}
+ @androidx.compose.runtime.Immutable public final class TextToggleButtonColors {
+ ctor public TextToggleButtonColors(long checkedContainerColor, long checkedContentColor, long uncheckedContainerColor, long uncheckedContentColor, long disabledCheckedContainerColor, long disabledCheckedContentColor, long disabledUncheckedContainerColor, long disabledUncheckedContentColor);
+ method public long getCheckedContainerColor();
+ method public long getCheckedContentColor();
+ method public long getDisabledCheckedContainerColor();
+ method public long getDisabledCheckedContentColor();
+ method public long getDisabledUncheckedContainerColor();
+ method public long getDisabledUncheckedContentColor();
+ method public long getUncheckedContainerColor();
+ method public long getUncheckedContentColor();
+ property public final long checkedContainerColor;
+ property public final long checkedContentColor;
+ property public final long disabledCheckedContainerColor;
+ property public final long disabledCheckedContentColor;
+ property public final long disabledUncheckedContainerColor;
+ property public final long disabledUncheckedContentColor;
+ property public final long uncheckedContainerColor;
+ property public final long uncheckedContentColor;
+ }
+
@androidx.compose.runtime.Immutable public final class TimePickerColors {
ctor public TimePickerColors(long selectedPickerContentColor, long unselectedPickerContentColor, long separatorColor, long pickerLabelColor, long confirmButtonContentColor, long confirmButtonContainerColor);
method public long getConfirmButtonContainerColor();
@@ -1221,26 +1350,6 @@
method public abstract void time();
}
- @androidx.compose.runtime.Immutable public final class ToggleButtonColors {
- ctor public ToggleButtonColors(long checkedContainerColor, long checkedContentColor, long uncheckedContainerColor, long uncheckedContentColor, long disabledCheckedContainerColor, long disabledCheckedContentColor, long disabledUncheckedContainerColor, long disabledUncheckedContentColor);
- method public long getCheckedContainerColor();
- method public long getCheckedContentColor();
- method public long getDisabledCheckedContainerColor();
- method public long getDisabledCheckedContentColor();
- method public long getDisabledUncheckedContainerColor();
- method public long getDisabledUncheckedContentColor();
- method public long getUncheckedContainerColor();
- method public long getUncheckedContentColor();
- property public final long checkedContainerColor;
- property public final long checkedContentColor;
- property public final long disabledCheckedContainerColor;
- property public final long disabledCheckedContentColor;
- property public final long disabledUncheckedContainerColor;
- property public final long disabledUncheckedContentColor;
- property public final long uncheckedContainerColor;
- property public final long uncheckedContentColor;
- }
-
public fun interface TouchExplorationStateProvider {
method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<java.lang.Boolean> touchExplorationState();
}
@@ -1296,32 +1405,6 @@
}
-package androidx.wear.compose.material3.dialog {
-
- public final class AlertDialogDefaults {
- method @androidx.compose.runtime.Composable public void BottomButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
- method @androidx.compose.runtime.Composable public void ConfirmButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
- method @androidx.compose.runtime.Composable public void DismissButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
- method @androidx.compose.runtime.Composable public void GroupSeparator();
- method @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.PaddingValues contentPadding(boolean hasBottomButton);
- method public kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.RowScope,kotlin.Unit> getConfirmIcon();
- method public kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.RowScope,kotlin.Unit> getDismissIcon();
- method public float getEdgeButtonExtraTopPadding();
- method public androidx.compose.foundation.layout.Arrangement.Vertical getVerticalArrangement();
- property public final kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.RowScope,kotlin.Unit> ConfirmIcon;
- property public final kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.RowScope,kotlin.Unit> DismissIcon;
- property public final androidx.compose.foundation.layout.Arrangement.Vertical VerticalArrangement;
- property public final float edgeButtonExtraTopPadding;
- field public static final androidx.wear.compose.material3.dialog.AlertDialogDefaults INSTANCE;
- }
-
- public final class AlertDialogKt {
- method @androidx.compose.runtime.Composable public static void AlertDialog(boolean show, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> bottomButton, kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? text, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.ui.window.DialogProperties properties, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.lazy.ScalingLazyListScope,kotlin.Unit>? content);
- method @androidx.compose.runtime.Composable public static void AlertDialog(boolean show, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> confirmButton, kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> dismissButton, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? text, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.ui.window.DialogProperties properties, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.lazy.ScalingLazyListScope,kotlin.Unit>? content);
- }
-
-}
-
package androidx.wear.compose.material3.lazy {
public final class LazyColumnScrollTransformModifiersKt {
diff --git a/wear/compose/compose-material3/api/restricted_current.txt b/wear/compose/compose-material3/api/restricted_current.txt
index 3b402a4..b0b63b9 100644
--- a/wear/compose/compose-material3/api/restricted_current.txt
+++ b/wear/compose/compose-material3/api/restricted_current.txt
@@ -1,6 +1,28 @@
// Signature format: 4.0
package androidx.wear.compose.material3 {
+ public final class AlertDialogDefaults {
+ method @androidx.compose.runtime.Composable public void BottomButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public void ConfirmButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public void DismissButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public void GroupSeparator();
+ method @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.PaddingValues contentPadding(boolean hasBottomButton);
+ method public kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.RowScope,kotlin.Unit> getConfirmIcon();
+ method public kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.RowScope,kotlin.Unit> getDismissIcon();
+ method public float getEdgeButtonExtraTopPadding();
+ method public androidx.compose.foundation.layout.Arrangement.Vertical getVerticalArrangement();
+ property public final kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.RowScope,kotlin.Unit> ConfirmIcon;
+ property public final kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.RowScope,kotlin.Unit> DismissIcon;
+ property public final androidx.compose.foundation.layout.Arrangement.Vertical VerticalArrangement;
+ property public final float edgeButtonExtraTopPadding;
+ field public static final androidx.wear.compose.material3.AlertDialogDefaults INSTANCE;
+ }
+
+ public final class AlertDialogKt {
+ method @androidx.compose.runtime.Composable public static void AlertDialog(boolean show, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> bottomButton, kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? text, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.ui.window.DialogProperties properties, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.lazy.ScalingLazyListScope,kotlin.Unit>? content);
+ method @androidx.compose.runtime.Composable public static void AlertDialog(boolean show, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> confirmButton, kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> dismissButton, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? text, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.ui.window.DialogProperties properties, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.lazy.ScalingLazyListScope,kotlin.Unit>? content);
+ }
+
@RequiresApi(31) public final class AnimatedTextDefaults {
field public static final int CacheSize = 5; // 0x5
field public static final androidx.wear.compose.material3.AnimatedTextDefaults INSTANCE;
@@ -298,6 +320,45 @@
method @androidx.compose.runtime.Composable @androidx.compose.runtime.ReadOnlyComposable public static long contentColorFor(long backgroundColor);
}
+ public final class ConfirmationColors {
+ ctor public ConfirmationColors(long iconColor, long iconContainerColor, long textColor);
+ method public long getIconColor();
+ method public long getIconContainerColor();
+ method public long getTextColor();
+ property public final long iconColor;
+ property public final long iconContainerColor;
+ property public final long textColor;
+ }
+
+ public final class ConfirmationDefaults {
+ method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ConfirmationColors confirmationColors();
+ method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ConfirmationColors confirmationColors(optional long iconColor, optional long iconContainerColor, optional long textColor);
+ method @androidx.compose.runtime.Composable public kotlin.jvm.functions.Function1<androidx.wear.compose.foundation.CurvedScope,kotlin.Unit> curvedText(String text, optional androidx.wear.compose.foundation.CurvedTextStyle style);
+ method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ConfirmationColors failureColors();
+ method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ConfirmationColors failureColors(optional long iconColor, optional long iconContainerColor, optional long textColor);
+ method @androidx.compose.runtime.Composable public kotlin.jvm.functions.Function1<androidx.wear.compose.foundation.CurvedScope,kotlin.Unit> failureText();
+ method public kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.BoxScope,kotlin.Unit> getFailureIcon();
+ method public float getIconSize();
+ method public float getSmallIconSize();
+ method public kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.BoxScope,kotlin.Unit> getSuccessIcon();
+ method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ConfirmationColors successColors();
+ method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ConfirmationColors successColors(optional long iconColor, optional long iconContainerColor, optional long textColor);
+ method @androidx.compose.runtime.Composable public kotlin.jvm.functions.Function1<androidx.wear.compose.foundation.CurvedScope,kotlin.Unit> successText();
+ property public final kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.BoxScope,kotlin.Unit> FailureIcon;
+ property public final float IconSize;
+ property public final float SmallIconSize;
+ property public final kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.BoxScope,kotlin.Unit> SuccessIcon;
+ field public static final long ConfirmationDurationMillis = 4000L; // 0xfa0L
+ field public static final androidx.wear.compose.material3.ConfirmationDefaults INSTANCE;
+ }
+
+ public final class ConfirmationKt {
+ method @androidx.compose.runtime.Composable public static void Confirmation(boolean show, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit>? text, optional androidx.compose.ui.Modifier modifier, optional androidx.wear.compose.material3.ConfirmationColors colors, optional androidx.compose.ui.window.DialogProperties properties, optional long durationMillis, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void Confirmation(boolean show, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.CurvedScope,kotlin.Unit>? curvedText, optional androidx.compose.ui.Modifier modifier, optional androidx.wear.compose.material3.ConfirmationColors colors, optional androidx.compose.ui.window.DialogProperties properties, optional long durationMillis, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void FailureConfirmation(boolean show, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.CurvedScope,kotlin.Unit>? curvedText, optional androidx.wear.compose.material3.ConfirmationColors colors, optional androidx.compose.ui.window.DialogProperties properties, optional long durationMillis, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void SuccessConfirmation(boolean show, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.CurvedScope,kotlin.Unit>? curvedText, optional androidx.wear.compose.material3.ConfirmationColors colors, optional androidx.compose.ui.window.DialogProperties properties, optional long durationMillis, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+ }
+
public final class ContentColorKt {
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.graphics.Color> getLocalContentColor();
property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.graphics.Color> LocalContentColor;
@@ -401,8 +462,8 @@
method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconButtonColors iconButtonColors();
method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconButtonColors iconButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
method public float iconSizeFor(float size);
- method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ToggleButtonColors iconToggleButtonColors();
- method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ToggleButtonColors iconToggleButtonColors(optional long checkedContainerColor, optional long checkedContentColor, optional long uncheckedContainerColor, optional long uncheckedContentColor, optional long disabledCheckedContainerColor, optional long disabledCheckedContentColor, optional long disabledUncheckedContainerColor, optional long disabledUncheckedContentColor);
+ method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconToggleButtonColors iconToggleButtonColors();
+ method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconToggleButtonColors iconToggleButtonColors(optional long checkedContainerColor, optional long checkedContentColor, optional long uncheckedContainerColor, optional long uncheckedContentColor, optional long disabledCheckedContainerColor, optional long disabledCheckedContentColor, optional long disabledUncheckedContainerColor, optional long disabledUncheckedContentColor);
method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconButtonColors outlinedIconButtonColors();
method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.IconButtonColors outlinedIconButtonColors(optional long contentColor, optional long disabledContentColor);
property public final float DefaultButtonSize;
@@ -422,7 +483,7 @@
method @androidx.compose.runtime.Composable public static void FilledIconButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional String? onLongClickLabel, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.IconButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void FilledTonalIconButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional String? onLongClickLabel, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.IconButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void IconButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional String? onLongClickLabel, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.IconButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
- method @androidx.compose.runtime.Composable public static void IconToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material3.ToggleButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.foundation.BorderStroke? border, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void IconToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material3.IconToggleButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.foundation.BorderStroke? border, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void OutlinedIconButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional String? onLongClickLabel, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.IconButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
}
@@ -432,6 +493,26 @@
method @androidx.compose.runtime.Composable public static void Icon(androidx.compose.ui.graphics.vector.ImageVector imageVector, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional long tint);
}
+ @androidx.compose.runtime.Immutable public final class IconToggleButtonColors {
+ ctor public IconToggleButtonColors(long checkedContainerColor, long checkedContentColor, long uncheckedContainerColor, long uncheckedContentColor, long disabledCheckedContainerColor, long disabledCheckedContentColor, long disabledUncheckedContainerColor, long disabledUncheckedContentColor);
+ method public long getCheckedContainerColor();
+ method public long getCheckedContentColor();
+ method public long getDisabledCheckedContainerColor();
+ method public long getDisabledCheckedContentColor();
+ method public long getDisabledUncheckedContainerColor();
+ method public long getDisabledUncheckedContentColor();
+ method public long getUncheckedContainerColor();
+ method public long getUncheckedContentColor();
+ property public final long checkedContainerColor;
+ property public final long checkedContentColor;
+ property public final long disabledCheckedContainerColor;
+ property public final long disabledCheckedContentColor;
+ property public final long disabledUncheckedContainerColor;
+ property public final long disabledUncheckedContentColor;
+ property public final long uncheckedContainerColor;
+ property public final long uncheckedContentColor;
+ }
+
@SuppressCompatibility @androidx.compose.runtime.Immutable @androidx.wear.compose.material3.ExperimentalWearMaterial3Api public final class InlineSliderColors {
ctor public InlineSliderColors(long containerColor, long buttonIconColor, long selectedBarColor, long unselectedBarColor, long barSeparatorColor, long disabledContainerColor, long disabledButtonIconColor, long disabledSelectedBarColor, long disabledUnselectedBarColor, long disabledBarSeparatorColor);
method public long getBarSeparatorColor();
@@ -548,6 +629,34 @@
method public static androidx.wear.compose.material3.MotionScheme standardMotionScheme();
}
+ public final class OpenOnPhoneDialogColors {
+ ctor public OpenOnPhoneDialogColors(long iconColor, long iconContainerColor, long progressIndicatorColor, long progressTrackColor, long textColor);
+ method public long getIconColor();
+ method public long getIconContainerColor();
+ method public long getProgressIndicatorColor();
+ method public long getProgressTrackColor();
+ method public long getTextColor();
+ property public final long iconColor;
+ property public final long iconContainerColor;
+ property public final long progressIndicatorColor;
+ property public final long progressTrackColor;
+ property public final long textColor;
+ }
+
+ public final class OpenOnPhoneDialogDefaults {
+ method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.OpenOnPhoneDialogColors colors();
+ method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.OpenOnPhoneDialogColors colors(optional long iconColor, optional long iconContainerColor, optional long progressIndicatorColor, optional long progressTrackColor, optional long textColor);
+ method @androidx.compose.runtime.Composable public kotlin.jvm.functions.Function1<androidx.wear.compose.foundation.CurvedScope,kotlin.Unit> curvedText(optional String text, optional androidx.wear.compose.foundation.CurvedTextStyle style);
+ method public kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.BoxScope,kotlin.Unit> getIcon();
+ property public final kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.BoxScope,kotlin.Unit> Icon;
+ field public static final long DurationMillis = 4000L; // 0xfa0L
+ field public static final androidx.wear.compose.material3.OpenOnPhoneDialogDefaults INSTANCE;
+ }
+
+ public final class OpenOnPhoneDialogKt {
+ method @androidx.compose.runtime.Composable public static void OpenOnPhoneDialog(boolean show, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.CurvedScope,kotlin.Unit>? curvedText, optional androidx.wear.compose.material3.OpenOnPhoneDialogColors colors, optional androidx.compose.ui.window.DialogProperties properties, optional long durationMillis, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+ }
+
public final class PickerDefaults {
method @androidx.compose.runtime.Composable public androidx.compose.foundation.gestures.FlingBehavior flingBehavior(androidx.wear.compose.material3.PickerState state, optional androidx.compose.animation.core.DecayAnimationSpec<java.lang.Float> decay);
method public float getGradientRatio();
@@ -1119,8 +1228,8 @@
method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.TextButtonColors outlinedTextButtonColors(optional long contentColor, optional long disabledContentColor);
method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.TextButtonColors textButtonColors();
method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.TextButtonColors textButtonColors(optional long containerColor, optional long contentColor, optional long disabledContainerColor, optional long disabledContentColor);
- method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ToggleButtonColors textToggleButtonColors();
- method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ToggleButtonColors textToggleButtonColors(optional long checkedContainerColor, optional long checkedContentColor, optional long uncheckedContainerColor, optional long uncheckedContentColor, optional long disabledCheckedContainerColor, optional long disabledCheckedContentColor, optional long disabledUncheckedContainerColor, optional long disabledUncheckedContentColor);
+ method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.TextToggleButtonColors textToggleButtonColors();
+ method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.TextToggleButtonColors textToggleButtonColors(optional long checkedContainerColor, optional long checkedContentColor, optional long uncheckedContainerColor, optional long uncheckedContentColor, optional long disabledCheckedContainerColor, optional long disabledCheckedContentColor, optional long disabledUncheckedContainerColor, optional long disabledUncheckedContentColor);
property public final float DefaultButtonSize;
property public final float LargeButtonSize;
property public final float SmallButtonSize;
@@ -1134,7 +1243,7 @@
public final class TextButtonKt {
method @androidx.compose.runtime.Composable public static void TextButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? onLongClick, optional String? onLongClickLabel, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.wear.compose.material3.TextButtonColors colors, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
- method @androidx.compose.runtime.Composable public static void TextToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material3.ToggleButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.foundation.BorderStroke? border, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void TextToggleButton(boolean checked, kotlin.jvm.functions.Function1<? super java.lang.Boolean,kotlin.Unit> onCheckedChange, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.wear.compose.material3.TextToggleButtonColors colors, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.foundation.BorderStroke? border, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> content);
}
public final class TextKt {
@@ -1151,6 +1260,26 @@
property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.text.TextStyle> LocalTextStyle;
}
+ @androidx.compose.runtime.Immutable public final class TextToggleButtonColors {
+ ctor public TextToggleButtonColors(long checkedContainerColor, long checkedContentColor, long uncheckedContainerColor, long uncheckedContentColor, long disabledCheckedContainerColor, long disabledCheckedContentColor, long disabledUncheckedContainerColor, long disabledUncheckedContentColor);
+ method public long getCheckedContainerColor();
+ method public long getCheckedContentColor();
+ method public long getDisabledCheckedContainerColor();
+ method public long getDisabledCheckedContentColor();
+ method public long getDisabledUncheckedContainerColor();
+ method public long getDisabledUncheckedContentColor();
+ method public long getUncheckedContainerColor();
+ method public long getUncheckedContentColor();
+ property public final long checkedContainerColor;
+ property public final long checkedContentColor;
+ property public final long disabledCheckedContainerColor;
+ property public final long disabledCheckedContentColor;
+ property public final long disabledUncheckedContainerColor;
+ property public final long disabledUncheckedContentColor;
+ property public final long uncheckedContainerColor;
+ property public final long uncheckedContentColor;
+ }
+
@androidx.compose.runtime.Immutable public final class TimePickerColors {
ctor public TimePickerColors(long selectedPickerContentColor, long unselectedPickerContentColor, long separatorColor, long pickerLabelColor, long confirmButtonContentColor, long confirmButtonContainerColor);
method public long getConfirmButtonContainerColor();
@@ -1221,26 +1350,6 @@
method public abstract void time();
}
- @androidx.compose.runtime.Immutable public final class ToggleButtonColors {
- ctor public ToggleButtonColors(long checkedContainerColor, long checkedContentColor, long uncheckedContainerColor, long uncheckedContentColor, long disabledCheckedContainerColor, long disabledCheckedContentColor, long disabledUncheckedContainerColor, long disabledUncheckedContentColor);
- method public long getCheckedContainerColor();
- method public long getCheckedContentColor();
- method public long getDisabledCheckedContainerColor();
- method public long getDisabledCheckedContentColor();
- method public long getDisabledUncheckedContainerColor();
- method public long getDisabledUncheckedContentColor();
- method public long getUncheckedContainerColor();
- method public long getUncheckedContentColor();
- property public final long checkedContainerColor;
- property public final long checkedContentColor;
- property public final long disabledCheckedContainerColor;
- property public final long disabledCheckedContentColor;
- property public final long disabledUncheckedContainerColor;
- property public final long disabledUncheckedContentColor;
- property public final long uncheckedContainerColor;
- property public final long uncheckedContentColor;
- }
-
public fun interface TouchExplorationStateProvider {
method @androidx.compose.runtime.Composable public androidx.compose.runtime.State<java.lang.Boolean> touchExplorationState();
}
@@ -1296,32 +1405,6 @@
}
-package androidx.wear.compose.material3.dialog {
-
- public final class AlertDialogDefaults {
- method @androidx.compose.runtime.Composable public void BottomButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
- method @androidx.compose.runtime.Composable public void ConfirmButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
- method @androidx.compose.runtime.Composable public void DismissButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
- method @androidx.compose.runtime.Composable public void GroupSeparator();
- method @androidx.compose.runtime.Composable public androidx.compose.foundation.layout.PaddingValues contentPadding(boolean hasBottomButton);
- method public kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.RowScope,kotlin.Unit> getConfirmIcon();
- method public kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.RowScope,kotlin.Unit> getDismissIcon();
- method public float getEdgeButtonExtraTopPadding();
- method public androidx.compose.foundation.layout.Arrangement.Vertical getVerticalArrangement();
- property public final kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.RowScope,kotlin.Unit> ConfirmIcon;
- property public final kotlin.jvm.functions.Function1<androidx.compose.foundation.layout.RowScope,kotlin.Unit> DismissIcon;
- property public final androidx.compose.foundation.layout.Arrangement.Vertical VerticalArrangement;
- property public final float edgeButtonExtraTopPadding;
- field public static final androidx.wear.compose.material3.dialog.AlertDialogDefaults INSTANCE;
- }
-
- public final class AlertDialogKt {
- method @androidx.compose.runtime.Composable public static void AlertDialog(boolean show, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit> bottomButton, kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? text, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.ui.window.DialogProperties properties, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.lazy.ScalingLazyListScope,kotlin.Unit>? content);
- method @androidx.compose.runtime.Composable public static void AlertDialog(boolean show, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> confirmButton, kotlin.jvm.functions.Function0<kotlin.Unit> title, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> dismissButton, optional kotlin.jvm.functions.Function0<kotlin.Unit>? icon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? text, optional androidx.compose.foundation.layout.Arrangement.Vertical verticalArrangement, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.ui.window.DialogProperties properties, optional kotlin.jvm.functions.Function1<? super androidx.wear.compose.foundation.lazy.ScalingLazyListScope,kotlin.Unit>? content);
- }
-
-}
-
package androidx.wear.compose.material3.lazy {
public final class LazyColumnScrollTransformModifiersKt {
diff --git a/wear/compose/compose-material3/build.gradle b/wear/compose/compose-material3/build.gradle
index f103f85..43171ec 100644
--- a/wear/compose/compose-material3/build.gradle
+++ b/wear/compose/compose-material3/build.gradle
@@ -32,21 +32,22 @@
}
dependencies {
- api("androidx.compose.foundation:foundation:1.7.0-rc01")
- api("androidx.compose.ui:ui:1.7.0-rc01")
- api("androidx.compose.ui:ui-text:1.7.0-rc01")
- api("androidx.compose.runtime:runtime:1.7.0-rc01")
+ api("androidx.compose.foundation:foundation:1.7.0")
+ api("androidx.compose.ui:ui:1.7.0")
+ api("androidx.compose.ui:ui-text:1.7.0")
+ api("androidx.compose.runtime:runtime:1.7.0")
api(project(":wear:compose:compose-foundation"))
implementation(libs.kotlinStdlib)
implementation(libs.kotlinCoroutinesCore)
- implementation("androidx.compose.animation:animation:1.7.0-rc01")
- implementation("androidx.compose.material:material-icons-core:1.7.0-rc01")
- implementation("androidx.compose.material:material-ripple:1.7.0-rc01")
- implementation("androidx.compose.ui:ui-util:1.7.0-rc01")
+ implementation("androidx.compose.animation:animation:1.7.0")
+ implementation("androidx.compose.material:material-icons-core:1.7.0")
+ implementation("androidx.compose.material:material-ripple:1.7.0")
+ implementation("androidx.compose.ui:ui-util:1.7.0")
implementation(project(":wear:compose:compose-material-core"))
implementation("androidx.profileinstaller:profileinstaller:1.3.1")
implementation("androidx.graphics:graphics-shapes:1.0.0-beta01")
+ implementation project(':compose:animation:animation-graphics')
androidTestImplementation(project(":compose:ui:ui-test"))
androidTestImplementation(project(":compose:ui:ui-test-junit4"))
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/dialogs/AlertDialogs.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/AlertDialogs.kt
similarity index 95%
rename from wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/dialogs/AlertDialogs.kt
rename to wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/AlertDialogs.kt
index c71f190..4440425 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/dialogs/AlertDialogs.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/AlertDialogs.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.wear.compose.material3.demos.dialogs
+package androidx.wear.compose.material3.demos
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
@@ -38,6 +38,8 @@
import androidx.wear.compose.foundation.lazy.ScalingLazyListScope
import androidx.wear.compose.foundation.lazy.rememberScalingLazyListState
import androidx.wear.compose.integration.demos.common.ComposableDemo
+import androidx.wear.compose.material3.AlertDialog
+import androidx.wear.compose.material3.AlertDialogDefaults
import androidx.wear.compose.material3.Button
import androidx.wear.compose.material3.ListHeader
import androidx.wear.compose.material3.MaterialTheme
@@ -45,11 +47,9 @@
import androidx.wear.compose.material3.ScreenScaffold
import androidx.wear.compose.material3.SwitchButton
import androidx.wear.compose.material3.Text
-import androidx.wear.compose.material3.dialog.AlertDialog
-import androidx.wear.compose.material3.dialog.AlertDialogDefaults
-import androidx.wear.compose.material3.samples.dialog.AlertDialogWithBottomButtonSample
-import androidx.wear.compose.material3.samples.dialog.AlertDialogWithConfirmAndDismissSample
-import androidx.wear.compose.material3.samples.dialog.AlertDialogWithContentGroupsSample
+import androidx.wear.compose.material3.samples.AlertDialogWithBottomButtonSample
+import androidx.wear.compose.material3.samples.AlertDialogWithConfirmAndDismissSample
+import androidx.wear.compose.material3.samples.AlertDialogWithContentGroupsSample
val AlertDialogs =
listOf(
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/Confirmations.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/Confirmations.kt
new file mode 100644
index 0000000..9233b25
--- /dev/null
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/Confirmations.kt
@@ -0,0 +1,108 @@
+/*
+ * 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.wear.compose.material3.demos
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.size
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Add
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.wear.compose.integration.demos.common.ComposableDemo
+import androidx.wear.compose.material3.Confirmation
+import androidx.wear.compose.material3.ConfirmationDefaults
+import androidx.wear.compose.material3.FilledTonalButton
+import androidx.wear.compose.material3.Icon
+import androidx.wear.compose.material3.MaterialTheme
+import androidx.wear.compose.material3.Text
+import androidx.wear.compose.material3.samples.ConfirmationSample
+import androidx.wear.compose.material3.samples.FailureConfirmationSample
+import androidx.wear.compose.material3.samples.LongTextConfirmationSample
+import androidx.wear.compose.material3.samples.SuccessConfirmationSample
+
+val Comfirmations =
+ listOf(
+ ComposableDemo("Generic confirmation") { ConfirmationSample() },
+ ComposableDemo("Long content confirmation") { LongTextConfirmationSample() },
+ ComposableDemo("Success confirmation") { SuccessConfirmationSample() },
+ ComposableDemo("Failure confirmation") { FailureConfirmationSample() },
+ ComposableDemo("Confirmation without text") { ConfirmationWithoutText() },
+ ComposableDemo("Confirmation with custom colors") { ConfirmationWithCustomColors() },
+ )
+
+@Composable
+fun ConfirmationWithoutText() {
+ var showConfirmation by remember { mutableStateOf(false) }
+
+ Box(Modifier.fillMaxSize()) {
+ FilledTonalButton(
+ modifier = Modifier.align(Alignment.Center),
+ onClick = { showConfirmation = true },
+ label = { Text("Show Confirmation") }
+ )
+ }
+
+ Confirmation(
+ show = showConfirmation,
+ onDismissRequest = { showConfirmation = false },
+ curvedText = null
+ ) {
+ Icon(
+ imageVector = Icons.Filled.Add,
+ contentDescription = null,
+ modifier = Modifier.size(ConfirmationDefaults.IconSize).align(Alignment.Center),
+ tint = MaterialTheme.colorScheme.primary
+ )
+ }
+}
+
+@Composable
+fun ConfirmationWithCustomColors() {
+ var showConfirmation by remember { mutableStateOf(false) }
+
+ Box(Modifier.fillMaxSize()) {
+ FilledTonalButton(
+ modifier = Modifier.align(Alignment.Center),
+ onClick = { showConfirmation = true },
+ label = { Text("Show Confirmation") }
+ )
+ }
+
+ Confirmation(
+ show = showConfirmation,
+ onDismissRequest = { showConfirmation = false },
+ colors =
+ ConfirmationDefaults.confirmationColors(
+ iconColor = MaterialTheme.colorScheme.tertiary,
+ iconContainerColor = MaterialTheme.colorScheme.onTertiary,
+ textColor = MaterialTheme.colorScheme.onSurfaceVariant
+ ),
+ curvedText = ConfirmationDefaults.curvedText("Custom confirmation")
+ ) {
+ Icon(
+ imageVector = Icons.Filled.Add,
+ contentDescription = null,
+ modifier = Modifier.size(ConfirmationDefaults.IconSize).align(Alignment.Center),
+ )
+ }
+}
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/OpenOnPhoneDialogDemo.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/OpenOnPhoneDialogDemo.kt
new file mode 100644
index 0000000..ab628e5
--- /dev/null
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/OpenOnPhoneDialogDemo.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.wear.compose.material3.demos
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.wear.compose.integration.demos.common.ComposableDemo
+import androidx.wear.compose.material3.FilledTonalButton
+import androidx.wear.compose.material3.MaterialTheme
+import androidx.wear.compose.material3.OpenOnPhoneDialog
+import androidx.wear.compose.material3.OpenOnPhoneDialogDefaults
+import androidx.wear.compose.material3.Text
+import androidx.wear.compose.material3.samples.OpenOnPhoneDialogSample
+
+val OpenOnPhoneDialogDemos =
+ listOf(
+ ComposableDemo("Default OpenOnPhone Dialog") { OpenOnPhoneDialogSample() },
+ ComposableDemo("With custom text") { OpenOnPhoneDialogWithCustomText() },
+ ComposableDemo("With custom colors") { OpenOnPhoneDialogWithCustomColors() },
+ )
+
+@Composable
+fun OpenOnPhoneDialogWithCustomText() {
+ var showConfirmation by remember { mutableStateOf(false) }
+
+ Box(Modifier.fillMaxSize()) {
+ FilledTonalButton(
+ modifier = Modifier.align(Alignment.Center),
+ onClick = { showConfirmation = true },
+ label = { Text("Open on phone") }
+ )
+ }
+
+ OpenOnPhoneDialog(
+ show = showConfirmation,
+ onDismissRequest = { showConfirmation = false },
+ curvedText = OpenOnPhoneDialogDefaults.curvedText("Custom text")
+ )
+}
+
+@Composable
+fun OpenOnPhoneDialogWithCustomColors() {
+ var showConfirmation by remember { mutableStateOf(false) }
+
+ Box(Modifier.fillMaxSize()) {
+ FilledTonalButton(
+ modifier = Modifier.align(Alignment.Center),
+ onClick = { showConfirmation = true },
+ label = { Text("Open on phone") }
+ )
+ }
+
+ OpenOnPhoneDialog(
+ show = showConfirmation,
+ onDismissRequest = { showConfirmation = false },
+ colors =
+ OpenOnPhoneDialogDefaults.colors(
+ iconColor = MaterialTheme.colorScheme.tertiary,
+ iconContainerColor = MaterialTheme.colorScheme.tertiaryContainer,
+ progressIndicatorColor = MaterialTheme.colorScheme.tertiary,
+ progressTrackColor = MaterialTheme.colorScheme.onTertiary,
+ textColor = MaterialTheme.colorScheme.onSurfaceVariant
+ )
+ )
+}
diff --git a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/WearMaterial3Demos.kt b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/WearMaterial3Demos.kt
index 082ed2d..2f9ce14 100644
--- a/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/WearMaterial3Demos.kt
+++ b/wear/compose/compose-material3/integration-tests/src/main/java/androidx/wear/compose/material3/demos/WearMaterial3Demos.kt
@@ -22,7 +22,6 @@
import androidx.wear.compose.integration.demos.common.Centralize
import androidx.wear.compose.integration.demos.common.ComposableDemo
import androidx.wear.compose.integration.demos.common.Material3DemoCategory
-import androidx.wear.compose.material3.demos.dialogs.AlertDialogs
import androidx.wear.compose.material3.samples.AnimatedTextSample
import androidx.wear.compose.material3.samples.AnimatedTextSampleButtonResponse
import androidx.wear.compose.material3.samples.AnimatedTextSampleSharedFontRegistry
@@ -46,12 +45,9 @@
listOf(
ComposableDemo("Color Scheme") { ColorSchemeDemos() },
Material3DemoCategory("Curved Text", CurvedTextDemos),
- Material3DemoCategory(
- "Dialogs",
- listOf(
- Material3DemoCategory("AlertDialog", AlertDialogs),
- )
- ),
+ Material3DemoCategory("Alert Dialog", AlertDialogs),
+ Material3DemoCategory("Confirmation", Comfirmations),
+ Material3DemoCategory("Open on phone Dialog", OpenOnPhoneDialogDemos),
ComposableDemo("Scaffold") { ScaffoldSample() },
Material3DemoCategory("ScrollAway", ScrollAwayDemos),
ComposableDemo("Haptics") { Centralize { HapticsDemos() } },
diff --git a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/dialog/AlertDialogSample.kt b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/AlertDialogSample.kt
similarity index 96%
rename from wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/dialog/AlertDialogSample.kt
rename to wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/AlertDialogSample.kt
index e20161b..dae4eef 100644
--- a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/dialog/AlertDialogSample.kt
+++ b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/AlertDialogSample.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.wear.compose.material3.samples.dialog
+package androidx.wear.compose.material3.samples
import androidx.annotation.Sampled
import androidx.compose.foundation.layout.Box
@@ -31,12 +31,12 @@
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
+import androidx.wear.compose.material3.AlertDialog
+import androidx.wear.compose.material3.AlertDialogDefaults
import androidx.wear.compose.material3.FilledTonalButton
import androidx.wear.compose.material3.Icon
import androidx.wear.compose.material3.MaterialTheme
import androidx.wear.compose.material3.Text
-import androidx.wear.compose.material3.dialog.AlertDialog
-import androidx.wear.compose.material3.dialog.AlertDialogDefaults
@Sampled
@Composable
diff --git a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/ConfirmationSample.kt b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/ConfirmationSample.kt
new file mode 100644
index 0000000..46bde32
--- /dev/null
+++ b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/ConfirmationSample.kt
@@ -0,0 +1,126 @@
+/*
+ * 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.wear.compose.material3.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.size
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Add
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.wear.compose.material3.Confirmation
+import androidx.wear.compose.material3.ConfirmationDefaults
+import androidx.wear.compose.material3.FailureConfirmation
+import androidx.wear.compose.material3.FilledTonalButton
+import androidx.wear.compose.material3.Icon
+import androidx.wear.compose.material3.SuccessConfirmation
+import androidx.wear.compose.material3.Text
+
+@Sampled
+@Composable
+fun ConfirmationSample() {
+ var showConfirmation by remember { mutableStateOf(false) }
+
+ Box(Modifier.fillMaxSize()) {
+ FilledTonalButton(
+ modifier = Modifier.align(Alignment.Center),
+ onClick = { showConfirmation = true },
+ label = { Text("Show Confirmation") }
+ )
+ }
+
+ // Has an icon and a short curved text content, which will be displayed along the bottom edge of
+ // the screen.
+ Confirmation(
+ show = showConfirmation,
+ onDismissRequest = { showConfirmation = false },
+ curvedText = ConfirmationDefaults.curvedText("Confirmed")
+ ) {
+ Icon(
+ imageVector = Icons.Filled.Add,
+ contentDescription = null,
+ modifier = Modifier.size(ConfirmationDefaults.IconSize),
+ )
+ }
+}
+
+@Sampled
+@Composable
+fun LongTextConfirmationSample() {
+ var showConfirmation by remember { mutableStateOf(false) }
+
+ Box(Modifier.fillMaxSize()) {
+ FilledTonalButton(
+ modifier = Modifier.align(Alignment.Center),
+ onClick = { showConfirmation = true },
+ label = { Text("Show Confirmation") }
+ )
+ }
+
+ // Has an icon and a text content. Text will be displayed in the center of the screen below the
+ // icon.
+ Confirmation(
+ show = showConfirmation,
+ onDismissRequest = { showConfirmation = false },
+ text = { Text(text = "Your message has been sent") },
+ ) {
+ Icon(
+ imageVector = Icons.Filled.Add,
+ contentDescription = null,
+ modifier = Modifier.size(ConfirmationDefaults.SmallIconSize),
+ )
+ }
+}
+
+@Sampled
+@Composable
+fun FailureConfirmationSample() {
+ var showConfirmation by remember { mutableStateOf(false) }
+
+ Box(Modifier.fillMaxSize()) {
+ FilledTonalButton(
+ modifier = Modifier.align(Alignment.Center),
+ onClick = { showConfirmation = true },
+ label = { Text("Show Confirmation") }
+ )
+ }
+
+ FailureConfirmation(show = showConfirmation, onDismissRequest = { showConfirmation = false })
+}
+
+@Sampled
+@Composable
+fun SuccessConfirmationSample() {
+ var showConfirmation by remember { mutableStateOf(false) }
+
+ Box(Modifier.fillMaxSize()) {
+ FilledTonalButton(
+ modifier = Modifier.align(Alignment.Center),
+ onClick = { showConfirmation = true },
+ label = { Text("Show Confirmation") }
+ )
+ }
+
+ SuccessConfirmation(show = showConfirmation, onDismissRequest = { showConfirmation = false })
+}
diff --git a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/OpenOnPhoneDialogSample.kt b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/OpenOnPhoneDialogSample.kt
new file mode 100644
index 0000000..741132e
--- /dev/null
+++ b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/OpenOnPhoneDialogSample.kt
@@ -0,0 +1,50 @@
+/*
+ * 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.wear.compose.material3.samples
+
+import androidx.annotation.Sampled
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.wear.compose.material3.FilledTonalButton
+import androidx.wear.compose.material3.OpenOnPhoneDialog
+import androidx.wear.compose.material3.Text
+
+@Sampled
+@Composable
+fun OpenOnPhoneDialogSample() {
+ var showConfirmation by remember { mutableStateOf(false) }
+
+ Box(Modifier.fillMaxSize()) {
+ FilledTonalButton(
+ modifier = Modifier.align(Alignment.Center),
+ onClick = { showConfirmation = true },
+ label = { Text("Open on phone") }
+ )
+ }
+
+ OpenOnPhoneDialog(
+ show = showConfirmation,
+ onDismissRequest = { showConfirmation = false },
+ )
+}
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/dialog/AlertDialogScreenshotTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/AlertDialogScreenshotTest.kt
similarity index 95%
rename from wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/dialog/AlertDialogScreenshotTest.kt
rename to wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/AlertDialogScreenshotTest.kt
index 0b7d1f8..e049442 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/dialog/AlertDialogScreenshotTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/AlertDialogScreenshotTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.wear.compose.material3.dialog
+package androidx.wear.compose.material3
import android.content.res.Configuration
import android.os.Build
@@ -41,14 +41,6 @@
import androidx.test.filters.SdkSuppress
import androidx.test.screenshot.AndroidXScreenshotTestRule
import androidx.wear.compose.foundation.lazy.ScalingLazyListScope
-import androidx.wear.compose.material3.FilledTonalButton
-import androidx.wear.compose.material3.Icon
-import androidx.wear.compose.material3.SCREENSHOT_GOLDEN_PATH
-import androidx.wear.compose.material3.ScreenSize
-import androidx.wear.compose.material3.TEST_TAG
-import androidx.wear.compose.material3.Text
-import androidx.wear.compose.material3.goldenIdentifier
-import androidx.wear.compose.material3.setContentWithTheme
import com.google.testing.junit.testparameterinjector.TestParameter
import com.google.testing.junit.testparameterinjector.TestParameterInjector
import org.junit.Rule
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/dialog/AlertDialogTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/AlertDialogTest.kt
similarity index 96%
rename from wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/dialog/AlertDialogTest.kt
rename to wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/AlertDialogTest.kt
index 14069c6..6e59369 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/dialog/AlertDialogTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/AlertDialogTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.wear.compose.material3.dialog
+package androidx.wear.compose.material3
import android.os.Build
import androidx.compose.foundation.background
@@ -39,17 +39,6 @@
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.test.filters.SdkSuppress
-import androidx.wear.compose.material3.Button
-import androidx.wear.compose.material3.LocalContentColor
-import androidx.wear.compose.material3.LocalTextAlign
-import androidx.wear.compose.material3.LocalTextMaxLines
-import androidx.wear.compose.material3.LocalTextStyle
-import androidx.wear.compose.material3.MaterialTheme
-import androidx.wear.compose.material3.TEST_TAG
-import androidx.wear.compose.material3.TestImage
-import androidx.wear.compose.material3.Text
-import androidx.wear.compose.material3.setContentWithTheme
-import androidx.wear.compose.material3.setContentWithThemeForSizeAssertions
import junit.framework.TestCase.assertEquals
import org.junit.Rule
import org.junit.Test
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ConfirmationScreenshotTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ConfirmationScreenshotTest.kt
new file mode 100644
index 0000000..73e047e
--- /dev/null
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ConfirmationScreenshotTest.kt
@@ -0,0 +1,227 @@
+/*
+ * 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.wear.compose.material3
+
+import android.content.res.Configuration
+import android.os.Build
+import androidx.compose.foundation.layout.size
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Add
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.remember
+import androidx.compose.testutils.assertAgainstGolden
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.junit4.ComposeContentTestRule
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.unit.dp
+import androidx.test.filters.MediumTest
+import androidx.test.filters.SdkSuppress
+import androidx.test.screenshot.AndroidXScreenshotTestRule
+import androidx.wear.compose.material3.ConfirmationDefaults.curvedText
+import com.google.testing.junit.testparameterinjector.TestParameter
+import com.google.testing.junit.testparameterinjector.TestParameterInjector
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TestName
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(TestParameterInjector::class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
+class ConfirmationScreenshotTest {
+ @get:Rule val rule = createComposeRule()
+
+ @get:Rule val screenshotRule = AndroidXScreenshotTestRule(SCREENSHOT_GOLDEN_PATH)
+
+ @get:Rule val testName = TestName()
+
+ @Test
+ fun confirmation_icon_linearText(@TestParameter screenSize: ScreenSize) {
+
+ rule.verifyConfirmationScreenshot(
+ testName = testName,
+ screenshotRule = screenshotRule,
+ screenSize = screenSize
+ ) { modifier ->
+ Confirmation(
+ show = true,
+ modifier = modifier,
+ onDismissRequest = {},
+ text = { Text("Your message has been sent") }
+ ) {
+ DefaultSmallIcon()
+ }
+ }
+ }
+
+ @Test
+ fun confirmation_icon_curvedText(@TestParameter screenSize: ScreenSize) {
+ rule.verifyConfirmationScreenshot(
+ testName = testName,
+ screenshotRule = screenshotRule,
+ screenSize = screenSize
+ ) { modifier ->
+ Confirmation(
+ show = true,
+ modifier = modifier,
+ onDismissRequest = {},
+ curvedText = curvedText("Confirmed")
+ ) {
+ DefaultIcon()
+ }
+ }
+ }
+
+ @Test
+ fun confirmation_icon_noText(@TestParameter screenSize: ScreenSize) {
+ rule.verifyConfirmationScreenshot(
+ testName = testName,
+ screenshotRule = screenshotRule,
+ screenSize = screenSize
+ ) { modifier ->
+ Confirmation(
+ show = true,
+ modifier = modifier,
+ onDismissRequest = {},
+ curvedText = null
+ ) {
+ DefaultIcon()
+ }
+ }
+ }
+
+ @Test
+ fun successConfirmation_icon_text(@TestParameter screenSize: ScreenSize) {
+ rule.verifyConfirmationScreenshot(
+ testName = testName,
+ screenshotRule = screenshotRule,
+ screenSize = screenSize
+ ) { modifier ->
+ SuccessConfirmation(
+ show = true,
+ modifier = modifier,
+ onDismissRequest = {},
+ curvedText = curvedText("Success")
+ )
+ }
+ }
+
+ @Test
+ fun successConfirmation_icon_noText(@TestParameter screenSize: ScreenSize) {
+ rule.verifyConfirmationScreenshot(
+ testName = testName,
+ screenshotRule = screenshotRule,
+ screenSize = screenSize
+ ) { modifier ->
+ SuccessConfirmation(
+ show = true,
+ modifier = modifier,
+ onDismissRequest = {},
+ curvedText = null
+ )
+ }
+ }
+
+ @Test
+ fun failureConfirmation_icon_text(@TestParameter screenSize: ScreenSize) {
+ rule.verifyConfirmationScreenshot(
+ testName = testName,
+ screenshotRule = screenshotRule,
+ screenSize = screenSize
+ ) { modifier ->
+ FailureConfirmation(
+ show = true,
+ modifier = modifier,
+ onDismissRequest = {},
+ curvedText = curvedText("Failure")
+ )
+ }
+ }
+
+ @Test
+ fun failureConfirmation_icon_noText(@TestParameter screenSize: ScreenSize) {
+ rule.verifyConfirmationScreenshot(
+ testName = testName,
+ screenshotRule = screenshotRule,
+ screenSize = screenSize
+ ) { modifier ->
+ FailureConfirmation(
+ show = true,
+ modifier = modifier,
+ onDismissRequest = {},
+ curvedText = null,
+ )
+ }
+ }
+
+ private fun ComposeContentTestRule.verifyConfirmationScreenshot(
+ testName: TestName,
+ screenshotRule: AndroidXScreenshotTestRule,
+ screenSize: ScreenSize,
+ content: @Composable (modifier: Modifier) -> Unit
+ ) {
+ setContentWithTheme {
+ val originalConfiguration = LocalConfiguration.current
+ val originalContext = LocalContext.current
+ val fixedScreenSizeConfiguration =
+ remember(originalConfiguration) {
+ Configuration(originalConfiguration).apply {
+ screenWidthDp = screenSize.size
+ screenHeightDp = screenSize.size
+ }
+ }
+ originalContext.resources.configuration.updateFrom(fixedScreenSizeConfiguration)
+
+ CompositionLocalProvider(
+ LocalContext provides originalContext,
+ LocalConfiguration provides fixedScreenSizeConfiguration,
+ ) {
+ content(Modifier.size(screenSize.size.dp).testTag(TEST_TAG))
+ }
+ }
+
+ onNodeWithTag(TEST_TAG)
+ .captureToImage()
+ .assertAgainstGolden(screenshotRule, testName.goldenIdentifier())
+ }
+
+ @Composable
+ private fun DefaultIcon() {
+ Icon(
+ Icons.Filled.Add,
+ modifier = Modifier.size(ConfirmationDefaults.IconSize),
+ tint = MaterialTheme.colorScheme.primary,
+ contentDescription = null
+ )
+ }
+
+ @Composable
+ private fun DefaultSmallIcon() {
+ Icon(
+ Icons.Filled.Add,
+ modifier = Modifier.size(ConfirmationDefaults.SmallIconSize),
+ tint = MaterialTheme.colorScheme.primary,
+ contentDescription = null
+ )
+ }
+}
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ConfirmationTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ConfirmationTest.kt
new file mode 100644
index 0000000..31206a1
--- /dev/null
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ConfirmationTest.kt
@@ -0,0 +1,605 @@
+/*
+ * 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.wear.compose.material3
+
+import android.os.Build
+import androidx.compose.foundation.background
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.testutils.assertContainsColor
+import androidx.compose.testutils.assertIsEqualTo
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.compositeOver
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.getUnclippedBoundsInRoot
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithContentDescription
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performTouchInput
+import androidx.compose.ui.test.swipeRight
+import androidx.compose.ui.text.style.TextAlign
+import androidx.test.filters.SdkSuppress
+import org.junit.Rule
+import org.junit.Test
+
+class ConfirmationTest {
+ @get:Rule val rule = createComposeRule()
+
+ @Test
+ fun confirmation_linearText_supports_testtag() {
+ rule.setContentWithTheme {
+ Confirmation(
+ show = true,
+ modifier = Modifier.testTag(TEST_TAG),
+ onDismissRequest = {},
+ text = {},
+ ) {}
+ }
+ rule.onNodeWithTag(TEST_TAG).assertExists()
+ }
+
+ @Test
+ fun confirmation_curvedText_supports_testtag() {
+ rule.setContentWithTheme {
+ Confirmation(
+ show = true,
+ modifier = Modifier.testTag(TEST_TAG),
+ onDismissRequest = {},
+ curvedText = {}
+ ) {}
+ }
+ rule.onNodeWithTag(TEST_TAG).assertExists()
+ }
+
+ @Test
+ fun successConfirmation_supports_testtag() {
+ rule.setContentWithTheme {
+ SuccessConfirmation(
+ show = true,
+ modifier = Modifier.testTag(TEST_TAG),
+ onDismissRequest = {},
+ )
+ }
+ rule.onNodeWithTag(TEST_TAG).assertExists()
+ }
+
+ @Test
+ fun failureConfirmation_supports_testtag() {
+ rule.setContentWithTheme {
+ FailureConfirmation(
+ show = true,
+ modifier = Modifier.testTag(TEST_TAG),
+ onDismissRequest = {},
+ )
+ }
+ rule.onNodeWithTag(TEST_TAG).assertExists()
+ }
+
+ @Test
+ fun confirmation_linearText_supports_swipeToDismiss() {
+ rule.setContentWithTheme {
+ var showDialog by remember { mutableStateOf(true) }
+ Confirmation(
+ modifier = Modifier.testTag(TEST_TAG),
+ text = {},
+ onDismissRequest = { showDialog = false },
+ show = showDialog
+ ) {}
+ }
+
+ rule.onNodeWithTag(TEST_TAG).performTouchInput { swipeRight() }
+ rule.onNodeWithTag(TEST_TAG).assertDoesNotExist()
+ }
+
+ @Test
+ fun confirmation_curvedText_supports_swipeToDismiss() {
+ rule.setContentWithTheme {
+ var showDialog by remember { mutableStateOf(true) }
+ Confirmation(
+ modifier = Modifier.testTag(TEST_TAG),
+ onDismissRequest = { showDialog = false },
+ show = showDialog,
+ curvedText = {}
+ ) {}
+ }
+
+ rule.onNodeWithTag(TEST_TAG).performTouchInput { swipeRight() }
+ rule.onNodeWithTag(TEST_TAG).assertDoesNotExist()
+ }
+
+ @Test
+ fun successConfirmation_supports_swipeToDismiss() {
+ rule.mainClock.autoAdvance = false
+ rule.setContentWithTheme {
+ var showDialog by remember { mutableStateOf(true) }
+ SuccessConfirmation(
+ modifier = Modifier.testTag(TEST_TAG),
+ onDismissRequest = { showDialog = false },
+ show = showDialog,
+ )
+ }
+ // Advancing time so that animation will finish its motion.
+ rule.mainClock.advanceTimeBy(1000)
+ rule.onNodeWithTag(TEST_TAG).performTouchInput { swipeRight() }
+ rule.mainClock.advanceTimeBy(1000)
+ rule.onNodeWithTag(TEST_TAG).assertDoesNotExist()
+ }
+
+ @Test
+ fun failureConfirmation_supports_swipeToDismiss() {
+ rule.mainClock.autoAdvance = false
+ rule.setContentWithTheme {
+ var showDialog by remember { mutableStateOf(true) }
+ FailureConfirmation(
+ modifier = Modifier.testTag(TEST_TAG),
+ onDismissRequest = { showDialog = false },
+ show = showDialog,
+ )
+ }
+ // Advancing time so that animation will finish its motion.
+ rule.mainClock.advanceTimeBy(1000)
+ rule.onNodeWithTag(TEST_TAG).performTouchInput { swipeRight() }
+ rule.mainClock.advanceTimeBy(1000)
+ rule.onNodeWithTag(TEST_TAG).assertDoesNotExist()
+ }
+
+ @Test
+ fun hides_confirmation_linearText_when_show_false() {
+ rule.setContentWithTheme {
+ Confirmation(
+ show = false,
+ modifier = Modifier.testTag(TEST_TAG),
+ onDismissRequest = {},
+ text = {},
+ ) {}
+ }
+ rule.onNodeWithTag(TEST_TAG).assertDoesNotExist()
+ }
+
+ @Test
+ fun hides_confirmation_curvedText_when_show_false() {
+ rule.setContentWithTheme {
+ Confirmation(
+ show = false,
+ modifier = Modifier.testTag(TEST_TAG),
+ onDismissRequest = {},
+ curvedText = {}
+ ) {}
+ }
+ rule.onNodeWithTag(TEST_TAG).assertDoesNotExist()
+ }
+
+ @Test
+ fun hides_successConfirmation_when_show_false() {
+ rule.setContentWithTheme {
+ SuccessConfirmation(
+ show = false,
+ modifier = Modifier.testTag(TEST_TAG),
+ onDismissRequest = {},
+ )
+ }
+ rule.onNodeWithTag(TEST_TAG).assertDoesNotExist()
+ }
+
+ @Test
+ fun hides_failureConfirmation_when_show_false() {
+ rule.setContentWithTheme {
+ FailureConfirmation(
+ show = false,
+ modifier = Modifier.testTag(TEST_TAG),
+ onDismissRequest = {},
+ )
+ }
+ rule.onNodeWithTag(TEST_TAG).assertDoesNotExist()
+ }
+
+ @Test
+ fun confirmation_displays_icon_with_linearText() {
+ rule.setContentWithTheme {
+ Confirmation(
+ text = { Text("Text", modifier = Modifier.testTag(TextTestTag)) },
+ onDismissRequest = {},
+ show = true
+ ) {
+ TestImage(IconTestTag)
+ }
+ }
+ rule.onNodeWithTag(IconTestTag).assertExists()
+ rule.onNodeWithTag(TextTestTag).assertExists()
+ }
+
+ @Test
+ fun confirmation_displays_icon_with_curvedText() {
+ rule.setContentWithTheme {
+ Confirmation(
+ onDismissRequest = {},
+ show = true,
+ curvedText = { curvedText(CurvedText) }
+ ) {
+ TestImage(IconTestTag)
+ }
+ }
+ rule.onNodeWithTag(IconTestTag).assertExists()
+ rule.onNodeWithContentDescription(CurvedText).assertExists()
+ }
+
+ @Test
+ fun successConfirmation_displays_icon_with_text() {
+ rule.setContentWithTheme {
+ SuccessConfirmation(
+ onDismissRequest = {},
+ show = true,
+ curvedText = ConfirmationDefaults.curvedText(CurvedText)
+ ) {
+ TestImage(IconTestTag)
+ }
+ }
+ rule.onNodeWithTag(IconTestTag).assertExists()
+ rule.onNodeWithContentDescription(CurvedText).assertExists()
+ }
+
+ @Test
+ fun failureConfirmation_displays_icon_with_text() {
+ rule.setContentWithTheme {
+ FailureConfirmation(
+ onDismissRequest = {},
+ show = true,
+ curvedText = ConfirmationDefaults.curvedText(CurvedText)
+ ) {
+ TestImage(IconTestTag)
+ }
+ }
+ rule.onNodeWithTag(IconTestTag).assertExists()
+ rule.onNodeWithContentDescription(CurvedText).assertExists()
+ }
+
+ @Test
+ fun confirmation_linearText_dismissed_after_timeout() {
+ var dismissed = false
+ rule.mainClock.autoAdvance = false
+ rule.setContentWithTheme {
+ Confirmation(text = {}, onDismissRequest = { dismissed = true }, show = true) {}
+ }
+ // Timeout longer than default confirmation duration
+ rule.mainClock.advanceTimeBy(ConfirmationDefaults.ConfirmationDurationMillis + 1000)
+ assert(dismissed)
+ }
+
+ @Test
+ fun confirmation_curvedText_dismissed_after_timeout() {
+ var dismissed = false
+ rule.mainClock.autoAdvance = false
+ rule.setContentWithTheme {
+ Confirmation(onDismissRequest = { dismissed = true }, show = true, curvedText = {}) {}
+ }
+ // Timeout longer than default confirmation duration
+ rule.mainClock.advanceTimeBy(ConfirmationDefaults.ConfirmationDurationMillis + 1000)
+ assert(dismissed)
+ }
+
+ @Test
+ fun successConfirmation_dismissed_after_timeout() {
+ var dismissed = false
+ rule.mainClock.autoAdvance = false
+ rule.setContentWithTheme {
+ SuccessConfirmation(
+ onDismissRequest = { dismissed = true },
+ show = true,
+ )
+ }
+ // Timeout longer than default confirmation duration
+ rule.mainClock.advanceTimeBy(ConfirmationDefaults.ConfirmationDurationMillis + 1000)
+ assert(dismissed)
+ }
+
+ @Test
+ fun failureConfirmation_dismissed_after_timeout() {
+ var dismissed = false
+ rule.mainClock.autoAdvance = false
+ rule.setContentWithTheme {
+ FailureConfirmation(
+ onDismissRequest = { dismissed = true },
+ show = true,
+ )
+ }
+ // Timeout longer than default confirmation duration
+ rule.mainClock.advanceTimeBy(ConfirmationDefaults.ConfirmationDurationMillis + 1000)
+ assert(dismissed)
+ }
+
+ @Test
+ fun confirmation_linearText_positioning() {
+ rule.setContentWithThemeForSizeAssertions(useUnmergedTree = true) {
+ Confirmation(
+ show = true,
+ text = {
+ Text(
+ "Title",
+ modifier = Modifier.testTag(TextTestTag),
+ textAlign = TextAlign.Center
+ )
+ },
+ onDismissRequest = {},
+ modifier = Modifier.testTag(TEST_TAG),
+ ) {
+ TestIcon(Modifier.testTag(IconTestTag))
+ }
+ }
+
+ // Calculating the center of the icon
+ val iconCenter =
+ rule.onNodeWithTag(IconTestTag).getUnclippedBoundsInRoot().run { (top + bottom) / 2 }
+ val textTop = rule.onNodeWithTag(TextTestTag).getUnclippedBoundsInRoot().top
+
+ // Stepping down half of the container height with vertical content padding
+ textTop.assertIsEqualTo(
+ iconCenter +
+ ConfirmationDefaults.ConfirmationIconContainerSmallSize / 2 +
+ ConfirmationDefaults.LinearContentSpacing
+ )
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
+ @Test
+ fun confirmation_linearText_correct_colors() {
+ var expectedIconColor: Color = Color.Unspecified
+ var expectedIconContainerColor: Color = Color.Unspecified
+ var expectedTextColor: Color = Color.Unspecified
+
+ rule.setContentWithTheme {
+ Confirmation(
+ onDismissRequest = {},
+ modifier = Modifier.testTag(TEST_TAG),
+ show = true,
+ text = { Text("Text") },
+ ) {
+ TestIcon(Modifier.testTag(IconTestTag))
+ }
+ expectedIconColor = MaterialTheme.colorScheme.primary
+ expectedIconContainerColor = MaterialTheme.colorScheme.onPrimary
+ expectedTextColor = MaterialTheme.colorScheme.onBackground
+ }
+
+ rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(expectedIconColor)
+
+ rule
+ .onNodeWithTag(TEST_TAG)
+ .captureToImage()
+ .assertContainsColor(expectedIconContainerColor)
+
+ rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(expectedTextColor)
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
+ @Test
+ fun confirmation_curvedText_correct_colors() {
+ var expectedIconColor: Color = Color.Unspecified
+ var expectedIconContainerColor: Color = Color.Unspecified
+ var expectedTextColor: Color = Color.Unspecified
+ rule.setContentWithTheme {
+ Confirmation(
+ onDismissRequest = {},
+ modifier = Modifier.testTag(TEST_TAG),
+ show = true,
+ curvedText = ConfirmationDefaults.curvedText(CurvedText)
+ ) {
+ TestIcon(Modifier.testTag(IconTestTag))
+ }
+ expectedIconColor = MaterialTheme.colorScheme.primary
+ expectedIconContainerColor = MaterialTheme.colorScheme.onPrimary
+ expectedTextColor = MaterialTheme.colorScheme.onBackground
+ }
+
+ rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(expectedIconColor)
+
+ rule
+ .onNodeWithTag(TEST_TAG)
+ .captureToImage()
+ .assertContainsColor(expectedIconContainerColor)
+
+ rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(expectedTextColor)
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
+ @Test
+ fun successConfirmation_correct_colors() {
+ var expectedIconColor: Color = Color.Unspecified
+ var expectedIconContainerColor: Color = Color.Unspecified
+ var expectedTextColor: Color = Color.Unspecified
+
+ rule.setContentWithTheme {
+ SuccessConfirmation(
+ onDismissRequest = {},
+ modifier = Modifier.testTag(TEST_TAG),
+ show = true,
+ )
+ expectedIconColor = MaterialTheme.colorScheme.primary
+ expectedIconContainerColor = MaterialTheme.colorScheme.onPrimary
+ expectedTextColor = MaterialTheme.colorScheme.onBackground
+ }
+ rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(expectedIconColor)
+
+ rule
+ .onNodeWithTag(TEST_TAG)
+ .captureToImage()
+ .assertContainsColor(expectedIconContainerColor)
+
+ rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(expectedTextColor)
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
+ @Test
+ fun failureConfirmation_correct_colors() {
+ var expectedIconColor: Color = Color.Unspecified
+ var expectedIconContainerColor: Color = Color.Unspecified
+ var expectedTextColor: Color = Color.Unspecified
+ val backgroundColor = Color.Black
+ rule.setContentWithTheme {
+ FailureConfirmation(
+ onDismissRequest = {},
+ modifier = Modifier.testTag(TEST_TAG).background(backgroundColor),
+ show = true,
+ ) {
+ TestIcon(Modifier.testTag(IconTestTag))
+ }
+ expectedIconColor = MaterialTheme.colorScheme.errorContainer
+ // As we have .8 alpha, we have to merge this color with background
+ expectedIconContainerColor =
+ MaterialTheme.colorScheme.onErrorContainer.copy(.8f).compositeOver(backgroundColor)
+ expectedTextColor = MaterialTheme.colorScheme.onBackground
+ }
+
+ rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(expectedIconColor)
+
+ rule
+ .onNodeWithTag(TEST_TAG)
+ .captureToImage()
+ .assertContainsColor(expectedIconContainerColor)
+
+ rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(expectedTextColor)
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
+ @Test
+ fun confirmation_linearText_custom_colors() {
+ val customIconColor: Color = Color.Red
+ val customIconContainerColor: Color = Color.Green
+ val customTextColor: Color = Color.Blue
+
+ rule.setContentWithTheme {
+ Confirmation(
+ onDismissRequest = {},
+ modifier = Modifier.testTag(TEST_TAG),
+ show = true,
+ text = { Text("Text") },
+ colors =
+ ConfirmationDefaults.confirmationColors(
+ iconColor = customIconColor,
+ iconContainerColor = customIconContainerColor,
+ textColor = customTextColor
+ )
+ ) {
+ TestIcon(Modifier.testTag(IconTestTag))
+ }
+ }
+
+ rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(customIconColor)
+
+ rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(customIconContainerColor)
+
+ rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(customTextColor)
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
+ @Test
+ fun confirmation_curvedText_custom_colors() {
+ val customIconColor: Color = Color.Red
+ val customIconContainerColor: Color = Color.Green
+ val customTextColor: Color = Color.Blue
+
+ rule.setContentWithTheme {
+ Confirmation(
+ onDismissRequest = {},
+ modifier = Modifier.testTag(TEST_TAG),
+ show = true,
+ colors =
+ ConfirmationDefaults.confirmationColors(
+ iconColor = customIconColor,
+ iconContainerColor = customIconContainerColor,
+ textColor = customTextColor
+ ),
+ curvedText = ConfirmationDefaults.curvedText(CurvedText)
+ ) {
+ TestIcon(Modifier.testTag(IconTestTag))
+ }
+ }
+
+ rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(customIconColor)
+
+ rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(customIconContainerColor)
+
+ rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(customTextColor)
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
+ @Test
+ fun successConfirmation_curvedText_custom_colors() {
+ val customIconColor: Color = Color.Red
+ val customIconContainerColor: Color = Color.Green
+ val customTextColor: Color = Color.Blue
+
+ rule.setContentWithTheme {
+ SuccessConfirmation(
+ onDismissRequest = {},
+ modifier = Modifier.testTag(TEST_TAG),
+ show = true,
+ colors =
+ ConfirmationDefaults.successColors(
+ iconColor = customIconColor,
+ iconContainerColor = customIconContainerColor,
+ textColor = customTextColor
+ ),
+ ) {
+ TestIcon(Modifier.testTag(IconTestTag))
+ }
+ }
+
+ rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(customIconColor)
+
+ rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(customIconContainerColor)
+
+ rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(customTextColor)
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
+ @Test
+ fun failureConfirmation_curvedText_custom_colors() {
+ val customIconColor: Color = Color.Red
+ val customIconContainerColor: Color = Color.Green
+ val customTextColor: Color = Color.Blue
+
+ rule.setContentWithTheme {
+ FailureConfirmation(
+ onDismissRequest = {},
+ modifier = Modifier.testTag(TEST_TAG),
+ show = true,
+ colors =
+ ConfirmationDefaults.failureColors(
+ iconColor = customIconColor,
+ iconContainerColor = customIconContainerColor,
+ textColor = customTextColor
+ ),
+ ) {
+ TestIcon(Modifier.testTag(IconTestTag))
+ }
+ }
+
+ rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(customIconColor)
+
+ rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(customIconContainerColor)
+
+ rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(customTextColor)
+ }
+}
+
+private const val IconTestTag = "icon"
+private const val TextTestTag = "text"
+private const val CurvedText = "CurvedText"
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconToggleButtonTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconToggleButtonTest.kt
index 188a8f8..9c0a3f0 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconToggleButtonTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconToggleButtonTest.kt
@@ -618,7 +618,7 @@
private fun ComposeContentTestRule.verifyIconToggleButtonColors(
status: Status,
checked: Boolean,
- colors: @Composable () -> ToggleButtonColors,
+ colors: @Composable () -> IconToggleButtonColors,
containerColor: @Composable () -> Color,
contentColor: @Composable () -> Color,
) {
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/OpenOnPhoneDialogScreenshotTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/OpenOnPhoneDialogScreenshotTest.kt
new file mode 100644
index 0000000..74568a6
--- /dev/null
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/OpenOnPhoneDialogScreenshotTest.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.wear.compose.material3
+
+import android.content.res.Configuration
+import android.os.Build
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.remember
+import androidx.compose.testutils.assertAgainstGolden
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.junit4.ComposeContentTestRule
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.unit.dp
+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 org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TestName
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(TestParameterInjector::class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
+class OpenOnPhoneDialogScreenshotTest {
+ @get:Rule val rule = createComposeRule()
+
+ @get:Rule val screenshotRule = AndroidXScreenshotTestRule(SCREENSHOT_GOLDEN_PATH)
+
+ @get:Rule val testName = TestName()
+
+ @Test
+ fun openOnPhone_50_percent_progress(@TestParameter screenSize: ScreenSize) {
+ rule.verifyOpenOnPhoneScreenshot(
+ testName = testName,
+ screenshotRule = screenshotRule,
+ advanceTimeBy = OpenOnPhoneDialogDefaults.DurationMillis / 2,
+ screenSize = screenSize
+ )
+ }
+
+ @Test
+ fun openOnPhone_100_percent_progress(@TestParameter screenSize: ScreenSize) {
+ rule.verifyOpenOnPhoneScreenshot(
+ testName = testName,
+ screenshotRule = screenshotRule,
+ advanceTimeBy = OpenOnPhoneDialogDefaults.DurationMillis,
+ screenSize = screenSize
+ )
+ }
+
+ private fun ComposeContentTestRule.verifyOpenOnPhoneScreenshot(
+ testName: TestName,
+ screenshotRule: AndroidXScreenshotTestRule,
+ screenSize: ScreenSize,
+ advanceTimeBy: Long,
+ ) {
+ rule.mainClock.autoAdvance = false
+ setContentWithTheme {
+ val originalConfiguration = LocalConfiguration.current
+ val originalContext = LocalContext.current
+ val fixedScreenSizeConfiguration =
+ remember(originalConfiguration) {
+ Configuration(originalConfiguration).apply {
+ screenWidthDp = screenSize.size
+ screenHeightDp = screenSize.size
+ }
+ }
+ originalContext.resources.configuration.updateFrom(fixedScreenSizeConfiguration)
+
+ CompositionLocalProvider(
+ LocalContext provides originalContext,
+ LocalConfiguration provides fixedScreenSizeConfiguration,
+ ) {
+ OpenOnPhoneDialog(
+ show = true,
+ modifier = Modifier.size(screenSize.size.dp).testTag(TEST_TAG),
+ onDismissRequest = {},
+ )
+ }
+ }
+
+ rule.mainClock.advanceTimeBy(advanceTimeBy)
+ onNodeWithTag(TEST_TAG)
+ .captureToImage()
+ .assertAgainstGolden(screenshotRule, testName.goldenIdentifier())
+ }
+}
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/OpenOnPhoneDialogTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/OpenOnPhoneDialogTest.kt
new file mode 100644
index 0000000..c5de9c1
--- /dev/null
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/OpenOnPhoneDialogTest.kt
@@ -0,0 +1,176 @@
+/*
+ * 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.wear.compose.material3
+
+import android.os.Build
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.testutils.assertContainsColor
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performTouchInput
+import androidx.compose.ui.test.swipeRight
+import androidx.test.filters.SdkSuppress
+import org.junit.Rule
+import org.junit.Test
+
+class OpenOnPhoneDialogTest {
+ @get:Rule val rule = createComposeRule()
+
+ @Test
+ fun openOnPhone_supports_testtag() {
+ rule.setContentWithTheme {
+ OpenOnPhoneDialog(
+ show = true,
+ modifier = Modifier.testTag(TEST_TAG),
+ onDismissRequest = {},
+ )
+ }
+ rule.onNodeWithTag(TEST_TAG).assertExists()
+ }
+
+ @Test
+ fun openOnPhone_supports_swipeToDismiss() {
+ rule.mainClock.autoAdvance = false
+ rule.setContentWithTheme {
+ var showDialog by remember { mutableStateOf(true) }
+ OpenOnPhoneDialog(
+ modifier = Modifier.testTag(TEST_TAG),
+ onDismissRequest = { showDialog = false },
+ show = showDialog
+ )
+ }
+ rule.mainClock.advanceTimeBy(OpenOnPhoneDialogDefaults.DurationMillis / 2)
+ rule.onNodeWithTag(TEST_TAG).performTouchInput({ swipeRight() })
+ // Advancing time so that the dialog is dismissed
+ rule.mainClock.advanceTimeBy(300)
+ rule.onNodeWithTag(TEST_TAG).assertDoesNotExist()
+ }
+
+ @Test
+ fun hides_openOnPhone_when_show_false() {
+ rule.setContentWithTheme {
+ OpenOnPhoneDialog(
+ show = false,
+ modifier = Modifier.testTag(TEST_TAG),
+ onDismissRequest = {},
+ )
+ }
+ rule.onNodeWithTag(TEST_TAG).assertDoesNotExist()
+ }
+
+ @Test
+ fun openOnPhone_displays_icon() {
+ rule.setContentWithTheme {
+ OpenOnPhoneDialog(onDismissRequest = {}, show = true) { TestImage(IconTestTag) }
+ }
+ rule.onNodeWithTag(IconTestTag).assertExists()
+ }
+
+ @Test
+ fun openOnPhone_dismissed_after_timeout() {
+ var dismissed = false
+ rule.mainClock.autoAdvance = false
+ rule.setContentWithTheme {
+ OpenOnPhoneDialog(onDismissRequest = { dismissed = true }, show = true) {}
+ }
+ // Timeout longer than default confirmation duration
+ rule.mainClock.advanceTimeBy(OpenOnPhoneDialogDefaults.DurationMillis + 1000)
+ assert(dismissed)
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
+ @Test
+ fun openOnPhone_correct_colors() {
+ rule.mainClock.autoAdvance = false
+ var expectedIconColor: Color = Color.Unspecified
+ var expectedIconContainerColor: Color = Color.Unspecified
+ var expectedProgressIndicatorColor: Color = Color.Unspecified
+ var expectedProgressTrackColor: Color = Color.Unspecified
+ rule.setContentWithTheme {
+ OpenOnPhoneDialog(
+ onDismissRequest = {},
+ modifier = Modifier.testTag(TEST_TAG),
+ show = true
+ )
+ expectedIconColor = MaterialTheme.colorScheme.primary
+ expectedIconContainerColor = MaterialTheme.colorScheme.primaryContainer
+ expectedProgressIndicatorColor = MaterialTheme.colorScheme.primary
+ expectedProgressTrackColor = MaterialTheme.colorScheme.onPrimary
+ }
+ // Advance time by half of the default confirmation duration, so that the track and
+ // indicator are shown
+ rule.mainClock.advanceTimeBy(OpenOnPhoneDialogDefaults.DurationMillis / 2)
+
+ rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(expectedIconColor)
+ rule
+ .onNodeWithTag(TEST_TAG)
+ .captureToImage()
+ .assertContainsColor(expectedIconContainerColor)
+ rule
+ .onNodeWithTag(TEST_TAG)
+ .captureToImage()
+ .assertContainsColor(expectedProgressIndicatorColor)
+ rule
+ .onNodeWithTag(TEST_TAG)
+ .captureToImage()
+ .assertContainsColor(expectedProgressTrackColor)
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
+ @Test
+ fun openOnPhone_custom_colors() {
+ rule.mainClock.autoAdvance = false
+ val customIconColor: Color = Color.Red
+ val customIconContainerColor: Color = Color.Green
+ val customProgressIndicatorColor: Color = Color.Blue
+ val customProgressTrackColor: Color = Color.Magenta
+ rule.setContentWithTheme {
+ OpenOnPhoneDialog(
+ onDismissRequest = {},
+ modifier = Modifier.testTag(TEST_TAG),
+ colors =
+ OpenOnPhoneDialogDefaults.colors(
+ iconColor = customIconColor,
+ iconContainerColor = customIconContainerColor,
+ progressIndicatorColor = customProgressIndicatorColor,
+ progressTrackColor = customProgressTrackColor
+ ),
+ show = true
+ )
+ }
+ // Advance time by half of the default confirmation duration, so that the track and
+ // indicator are shown
+ rule.mainClock.advanceTimeBy(OpenOnPhoneDialogDefaults.DurationMillis / 2)
+
+ rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(customIconColor)
+ rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(customIconContainerColor)
+ rule
+ .onNodeWithTag(TEST_TAG)
+ .captureToImage()
+ .assertContainsColor(customProgressIndicatorColor)
+ rule.onNodeWithTag(TEST_TAG).captureToImage().assertContainsColor(customProgressTrackColor)
+ }
+}
+
+private const val IconTestTag = "icon"
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextToggleButtonTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextToggleButtonTest.kt
index 3b9584c..166a54c 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextToggleButtonTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextToggleButtonTest.kt
@@ -611,7 +611,7 @@
private fun ComposeContentTestRule.verifyTextToggleButtonColors(
status: Status,
checked: Boolean,
- colors: @Composable () -> ToggleButtonColors,
+ colors: @Composable () -> TextToggleButtonColors,
containerColor: @Composable () -> Color,
contentColor: @Composable () -> Color,
) {
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/dialog/AlertDialog.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/AlertDialog.kt
similarity index 92%
rename from wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/dialog/AlertDialog.kt
rename to wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/AlertDialog.kt
index 2b34a71..f0cc000 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/dialog/AlertDialog.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/AlertDialog.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.wear.compose.material3.dialog
+package androidx.wear.compose.material3
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
@@ -46,25 +46,12 @@
import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
import androidx.wear.compose.foundation.lazy.ScalingLazyListScope
import androidx.wear.compose.foundation.lazy.rememberScalingLazyListState
-import androidx.wear.compose.material3.Button
-import androidx.wear.compose.material3.ButtonDefaults
-import androidx.wear.compose.material3.EdgeButton
-import androidx.wear.compose.material3.FilledIconButton
-import androidx.wear.compose.material3.FilledTonalIconButton
-import androidx.wear.compose.material3.Icon
-import androidx.wear.compose.material3.LocalContentColor
-import androidx.wear.compose.material3.LocalTextAlign
-import androidx.wear.compose.material3.LocalTextMaxLines
-import androidx.wear.compose.material3.LocalTextStyle
-import androidx.wear.compose.material3.MaterialTheme
-import androidx.wear.compose.material3.PaddingDefaults
-import androidx.wear.compose.material3.ScreenScaffold
-import androidx.wear.compose.material3.dialog.AlertDialogDefaults.bottomSpacing
-import androidx.wear.compose.material3.dialog.AlertDialogDefaults.contentTopSpacing
-import androidx.wear.compose.material3.dialog.AlertDialogDefaults.iconBottomSpacing
-import androidx.wear.compose.material3.dialog.AlertDialogDefaults.textMessageTopSpacing
-import androidx.wear.compose.material3.dialog.AlertDialogDefaults.textPaddingFraction
-import androidx.wear.compose.material3.dialog.AlertDialogDefaults.titlePaddingFraction
+import androidx.wear.compose.material3.AlertDialogDefaults.bottomSpacing
+import androidx.wear.compose.material3.AlertDialogDefaults.contentTopSpacing
+import androidx.wear.compose.material3.AlertDialogDefaults.iconBottomSpacing
+import androidx.wear.compose.material3.AlertDialogDefaults.textMessageTopSpacing
+import androidx.wear.compose.material3.AlertDialogDefaults.textPaddingFraction
+import androidx.wear.compose.material3.AlertDialogDefaults.titlePaddingFraction
import androidx.wear.compose.materialcore.isSmallScreen
import androidx.wear.compose.materialcore.screenWidthDp
@@ -79,7 +66,7 @@
*
* Example of an [AlertDialog] with an icon, title and two buttons to confirm and dismiss:
*
- * @sample androidx.wear.compose.material3.samples.dialog.AlertDialogWithConfirmAndDismissSample
+ * @sample androidx.wear.compose.material3.samples.AlertDialogWithConfirmAndDismissSample
* @param show A boolean indicating whether the dialog should be displayed.
* @param onDismissRequest A lambda function to be called when the dialog is dismissed by swiping
* right (typically also called by the [dismissButton]).
@@ -146,11 +133,11 @@
*
* Example of an [AlertDialog] with an icon, title, text and bottom [EdgeButton]:
*
- * @sample androidx.wear.compose.material3.samples.dialog.AlertDialogWithBottomButtonSample
+ * @sample androidx.wear.compose.material3.samples.AlertDialogWithBottomButtonSample
*
* Example of an [AlertDialog] with content groups and a bottom [EdgeButton]:
*
- * @sample androidx.wear.compose.material3.samples.dialog.AlertDialogWithContentGroupsSample
+ * @sample androidx.wear.compose.material3.samples.AlertDialogWithContentGroupsSample
* @param show A boolean indicating whether the dialog should be displayed.
* @param onDismissRequest A lambda function to be called when the dialog is dismissed by swiping to
* the right or by other dismiss action.
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ColorScheme.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ColorScheme.kt
index e6e84b1..ec6ee95a 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ColorScheme.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ColorScheme.kt
@@ -220,7 +220,7 @@
internal var defaultOutlinedIconButtonColorsCached: IconButtonColors? = null
// Icon Toggle Button
- internal var defaultIconToggleButtonColorsCached: ToggleButtonColors? = null
+ internal var defaultIconToggleButtonColorsCached: IconToggleButtonColors? = null
// Text Button
internal var defaultTextButtonColorsCached: TextButtonColors? = null
@@ -230,7 +230,7 @@
internal var defaultOutlinedTextButtonColorsCached: TextButtonColors? = null
// Text Toggle Button
- internal var defaultTextToggleButtonColorsCached: ToggleButtonColors? = null
+ internal var defaultTextToggleButtonColorsCached: TextToggleButtonColors? = null
// Card
internal var defaultCardColorsCached: CardColors? = null
@@ -254,6 +254,14 @@
// Level Indicator
internal var defaultLevelIndicatorColorsCached: LevelIndicatorColors? = null
+ // Confirmation
+ internal var defaultConfirmationColorsCached: ConfirmationColors? = null
+ internal var defaultSuccessConfirmationColorsCached: ConfirmationColors? = null
+ internal var defaultFailureConfirmationColorsCached: ConfirmationColors? = null
+
+ // Open on Phone dialog
+ internal var mDefaultOpenOnPhoneDialogColorsCached: OpenOnPhoneDialogColors? = null
+
// Picker
internal var defaultTimePickerColorsCached: TimePickerColors? = null
internal var defaultDatePickerColorsCached: DatePickerColors? = null
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Confirmation.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Confirmation.kt
new file mode 100644
index 0000000..e88a30b
--- /dev/null
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Confirmation.kt
@@ -0,0 +1,656 @@
+/*
+ * 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.wear.compose.material3
+
+import androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi
+import androidx.compose.animation.graphics.res.animatedVectorResource
+import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter
+import androidx.compose.animation.graphics.vector.AnimatedImageVector
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.ColumnScope
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.platform.LocalAccessibilityManager
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.window.DialogProperties
+import androidx.wear.compose.foundation.CurvedDirection
+import androidx.wear.compose.foundation.CurvedLayout
+import androidx.wear.compose.foundation.CurvedModifier
+import androidx.wear.compose.foundation.CurvedScope
+import androidx.wear.compose.foundation.CurvedTextStyle
+import androidx.wear.compose.foundation.padding
+import androidx.wear.compose.material3.tokens.ColorSchemeKeyTokens
+import androidx.wear.compose.materialcore.screenHeightDp
+import androidx.wear.compose.materialcore.screenWidthDp
+import kotlinx.coroutines.delay
+
+/**
+ * Shows a [Confirmation] dialog with an icon and optional very short curved text. The length of the
+ * curved text should be very short and should not exceed 1-2 words. If a longer text required, then
+ * another [Confirmation] overload with a column content should be used instead.
+ *
+ * The confirmation will be showing a message to the user for [durationMillis]. After a specified
+ * timeout, the [onDismissRequest] callback will be invoked, where it's up to the caller to handle
+ * the dismissal. To hide the confirmation, [show] parameter should be set to false.
+ *
+ * Example of a [Confirmation] with an icon and a curved text content:
+ *
+ * @sample androidx.wear.compose.material3.samples.ConfirmationSample
+ * @param show A boolean indicating whether the confirmation should be displayed.
+ * @param onDismissRequest A lambda function to be called when the dialog is dismissed - either by
+ * swiping right or when the [durationMillis] has passed.
+ * @param curvedText A slot for displaying curved text content which will be shown along the bottom
+ * edge of the dialog.
+ * @param modifier Modifier to be applied to the confirmation content.
+ * @param colors A [ConfirmationColors] object for customizing the colors used in this
+ * [Confirmation].
+ * @param properties An optional [DialogProperties] object for configuring the dialog's behavior.
+ * @param durationMillis The duration in milliseconds for which the dialog is displayed. Defaults to
+ * [ConfirmationDefaults.ConfirmationDurationMillis].
+ * @param content A slot for displaying an icon inside the confirmation dialog. It's recommended to
+ * set its size to [ConfirmationDefaults.IconSize]
+ */
+@Composable
+fun Confirmation(
+ show: Boolean,
+ onDismissRequest: () -> Unit,
+ curvedText: (CurvedScope.() -> Unit)?,
+ modifier: Modifier = Modifier,
+ colors: ConfirmationColors = ConfirmationDefaults.confirmationColors(),
+ properties: DialogProperties = DialogProperties(),
+ durationMillis: Long = ConfirmationDefaults.ConfirmationDurationMillis,
+ content: @Composable BoxScope.() -> Unit
+) {
+ ConfirmationImpl(
+ show = show,
+ onDismissRequest = onDismissRequest,
+ modifier = modifier,
+ iconContainer = confirmationIconContainer(true, colors.iconContainerColor),
+ curvedText = curvedText,
+ colors = colors,
+ properties = properties,
+ durationMillis = durationMillis,
+ content = content
+ )
+}
+
+/**
+ * Shows a [Confirmation] dialog with an icon and optional short text. The length of the text should
+ * not exceed 3 lines. If the text is very short and fits into 1-2 words, consider using another
+ * [Confirmation] overload with curvedContent instead.
+ *
+ * The confirmation will show a message to the user for [durationMillis]. After a specified timeout,
+ * the [onDismissRequest] callback will be invoked, where it's up to the caller to handle the
+ * dismissal. To hide the confirmation, [show] parameter should be set to false.
+ *
+ * Example of a [Confirmation] with an icon and a text which fits into 3 lines:
+ *
+ * @sample androidx.wear.compose.material3.samples.LongTextConfirmationSample
+ * @param show A boolean indicating whether the confirmation should be displayed.
+ * @param onDismissRequest A lambda function to be called when the dialog is dismissed - either by
+ * swiping right or when the [durationMillis] has passed.
+ * @param text A slot for displaying text below the icon. It should not exceed 3 lines.
+ * @param modifier Modifier to be applied to the confirmation content.
+ * @param colors A [ConfirmationColors] object for customizing the colors used in this
+ * [Confirmation].
+ * @param properties An optional [DialogProperties] object for configuring the dialog's behavior.
+ * @param durationMillis The duration in milliseconds for which the dialog is displayed. Defaults to
+ * [ConfirmationDefaults.ConfirmationDurationMillis].
+ * @param content A slot for displaying an icon inside the confirmation dialog, which can be
+ * animated. It's recommended to set its size to [ConfirmationDefaults.SmallIconSize]
+ */
+@Composable
+fun Confirmation(
+ show: Boolean,
+ onDismissRequest: () -> Unit,
+ text: @Composable (ColumnScope.() -> Unit)?,
+ modifier: Modifier = Modifier,
+ colors: ConfirmationColors = ConfirmationDefaults.confirmationColors(),
+ properties: DialogProperties = DialogProperties(),
+ durationMillis: Long = ConfirmationDefaults.ConfirmationDurationMillis,
+ content: @Composable BoxScope.() -> Unit
+) {
+
+ val a11yDurationMillis =
+ LocalAccessibilityManager.current?.calculateRecommendedTimeoutMillis(
+ originalTimeoutMillis = durationMillis,
+ containsIcons = true,
+ containsText = text != null,
+ containsControls = false,
+ ) ?: durationMillis
+
+ LaunchedEffect(show, a11yDurationMillis) {
+ if (show) {
+ delay(a11yDurationMillis)
+ onDismissRequest()
+ }
+ }
+
+ Dialog(
+ showDialog = show,
+ modifier = modifier,
+ onDismissRequest = onDismissRequest,
+ properties = properties,
+ ) {
+ Box(Modifier.fillMaxSize()) {
+ val horizontalPadding =
+ screenWidthDp().dp * ConfirmationDefaults.HorizontalLinearContentPaddingFraction
+ Column(
+ modifier = Modifier.align(Alignment.Center).padding(horizontal = horizontalPadding),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Box(
+ modifier = Modifier.align(Alignment.CenterHorizontally),
+ contentAlignment = Alignment.Center
+ ) {
+ confirmationIconContainer(false, colors.iconContainerColor)()
+ CompositionLocalProvider(LocalContentColor provides colors.iconColor) {
+ content()
+ }
+ }
+ CompositionLocalProvider(
+ LocalContentColor provides colors.textColor,
+ LocalTextStyle provides MaterialTheme.typography.titleMedium,
+ LocalTextAlign provides TextAlign.Center,
+ LocalTextMaxLines provides ConfirmationDefaults.LinearContentMaxLines
+ ) {
+ if (text != null) {
+ Spacer(Modifier.height(ConfirmationDefaults.LinearContentSpacing))
+ text()
+ Spacer(Modifier.height(ConfirmationDefaults.LinearContentSpacing))
+ }
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Shows a [Confirmation] dialog with a success icon and optional short curved text. This
+ * confirmation indicates a successful operation or action.
+ *
+ * The confirmation will show a message to the user for [durationMillis]. After a specified timeout,
+ * the [onDismissRequest] callback will be invoked, where it's up to the caller to handle the
+ * dismissal. To hide the confirmation, [show] parameter should be set to false.
+ *
+ * Example of a [SuccessConfirmation] usage:
+ *
+ * @sample androidx.wear.compose.material3.samples.SuccessConfirmationSample
+ * @param show A boolean indicating whether the confirmation should be displayed.
+ * @param onDismissRequest A lambda function to be called when the dialog is dismissed - either by
+ * swiping right or when the [durationMillis] has passed.
+ * @param modifier Modifier to be applied to the confirmation content.
+ * @param curvedText A slot for displaying curved text content which will be shown along the bottom
+ * edge of the dialog. Defaults to a localized success message.
+ * @param colors A [ConfirmationColors] object for customizing the colors used in this
+ * [SuccessConfirmation].
+ * @param properties An optional [DialogProperties] object for configuring the dialog's behavior.
+ * @param durationMillis The duration in milliseconds for which the dialog is displayed. Defaults to
+ * [ConfirmationDefaults.ConfirmationDurationMillis].
+ * @param content A slot for displaying an icon inside the confirmation dialog, which can be
+ * animated. Defaults to an animated [ConfirmationDefaults.SuccessIcon].
+ */
+@Composable
+fun SuccessConfirmation(
+ show: Boolean,
+ onDismissRequest: () -> Unit,
+ modifier: Modifier = Modifier,
+ curvedText: (CurvedScope.() -> Unit)? = ConfirmationDefaults.successText(),
+ colors: ConfirmationColors = ConfirmationDefaults.successColors(),
+ properties: DialogProperties = DialogProperties(),
+ durationMillis: Long = ConfirmationDefaults.ConfirmationDurationMillis,
+ content: @Composable BoxScope.() -> Unit = ConfirmationDefaults.SuccessIcon,
+) {
+ ConfirmationImpl(
+ show = show,
+ onDismissRequest = onDismissRequest,
+ modifier = modifier,
+ content = content,
+ iconContainer = successIconContainer(colors.iconContainerColor),
+ curvedText = curvedText,
+ colors = colors,
+ properties = properties,
+ durationMillis = durationMillis
+ )
+}
+
+/**
+ * Shows a [Confirmation] dialog with a failure icon and an optional short curved text. This
+ * confirmation indicates an unsuccessful operation or action.
+ *
+ * The confirmation will show a message to the user for [durationMillis]. After a specified timeout,
+ * the [onDismissRequest] callback will be invoked, where it's up to the caller to handle the
+ * dismissal. To hide the confirmation, [show] parameter should be set to false.
+ *
+ * Example of a [FailureConfirmation] usage:
+ *
+ * @sample androidx.wear.compose.material3.samples.FailureConfirmationSample
+ * @param show A boolean indicating whether the confirmation should be displayed.
+ * @param onDismissRequest A lambda function to be called when the dialog is dismissed - either by
+ * swiping right or when the [durationMillis] has passed.
+ * @param modifier Modifier to be applied to the confirmation content.
+ * @param curvedText A slot for displaying curved text content which will be shown along the bottom
+ * edge of the dialog. Defaults to a localized failure message.
+ * @param colors A [ConfirmationColors] object for customizing the colors used in this
+ * [FailureConfirmation].
+ * @param properties An optional [DialogProperties] object for configuring the dialog's behavior.
+ * @param durationMillis The duration in milliseconds for which the dialog is displayed. Defaults to
+ * [ConfirmationDefaults.ConfirmationDurationMillis].
+ * @param content A slot for displaying an icon inside the confirmation dialog, which can be
+ * animated. Defaults to [ConfirmationDefaults.FailureIcon].
+ */
+@Composable
+fun FailureConfirmation(
+ show: Boolean,
+ onDismissRequest: () -> Unit,
+ modifier: Modifier = Modifier,
+ curvedText: (CurvedScope.() -> Unit)? = ConfirmationDefaults.failureText(),
+ colors: ConfirmationColors = ConfirmationDefaults.failureColors(),
+ properties: DialogProperties = DialogProperties(),
+ durationMillis: Long = ConfirmationDefaults.ConfirmationDurationMillis,
+ content: @Composable BoxScope.() -> Unit = ConfirmationDefaults.FailureIcon,
+) {
+ ConfirmationImpl(
+ show = show,
+ onDismissRequest = onDismissRequest,
+ modifier = modifier,
+ iconContainer = failureIconContainer(colors.iconContainerColor),
+ curvedText = curvedText,
+ colors = colors,
+ properties = properties,
+ durationMillis = durationMillis,
+ content = content
+ )
+}
+
+/** Contains default values used by [Confirmation] composable. */
+object ConfirmationDefaults {
+
+ /**
+ * Returns a lambda to display a curved success message. The success message is retrieved from
+ * the application's string resources.
+ */
+ @Composable
+ fun successText(): CurvedScope.() -> Unit =
+ curvedText(
+ LocalContext.current.resources.getString(R.string.wear_m3c_confirmation_success_message)
+ )
+
+ /**
+ * Returns a lambda to display a curved failure message. The failure message is retrieved from
+ * the application's string resources.
+ */
+ @Composable
+ fun failureText(): CurvedScope.() -> Unit =
+ curvedText(
+ LocalContext.current.resources.getString(R.string.wear_m3c_confirmation_failure_message)
+ )
+
+ /**
+ * A default composable used in [SuccessConfirmation] that displays a success icon with an
+ * animation.
+ */
+ @OptIn(ExperimentalAnimationGraphicsApi::class)
+ val SuccessIcon: @Composable BoxScope.() -> Unit = {
+ val animation = AnimatedImageVector.animatedVectorResource(R.drawable.check_animation)
+ var atEnd by remember { mutableStateOf(false) }
+ LaunchedEffect(Unit) {
+ delay(FailureIconDelay)
+ atEnd = true
+ }
+ Icon(
+ painter = rememberAnimatedVectorPainter(animation, atEnd),
+ contentDescription = null,
+ modifier = Modifier.size(IconSize)
+ )
+ }
+
+ /**
+ * A default composable used in [FailureConfirmation] that displays a failure icon with an
+ * animation.
+ */
+ @OptIn(ExperimentalAnimationGraphicsApi::class)
+ val FailureIcon: @Composable BoxScope.() -> Unit = {
+ val animation = AnimatedImageVector.animatedVectorResource(R.drawable.failure_animation)
+ var atEnd by remember { mutableStateOf(false) }
+ LaunchedEffect(Unit) {
+ delay(FailureIconDelay)
+ atEnd = true
+ }
+ Icon(
+ painter = rememberAnimatedVectorPainter(animation, atEnd),
+ contentDescription = null,
+ modifier = Modifier.size(IconSize)
+ )
+ }
+
+ /**
+ * A default composable that displays text along a curved path, used in [Confirmation].
+ *
+ * @param text The text to display.
+ * @param style The style to apply to the text. Defaults to
+ * CurvedTextStyle(MaterialTheme.typography.titleLarge).
+ */
+ @Composable
+ fun curvedText(
+ text: String,
+ style: CurvedTextStyle = CurvedTextStyle(MaterialTheme.typography.titleLarge)
+ ): CurvedScope.() -> Unit = {
+ curvedText(
+ text = text,
+ style = style,
+ maxSweepAngle = CurvedTextDefaults.StaticContentMaxSweepAngle,
+ modifier = CurvedModifier.padding(PaddingDefaults.edgePadding),
+ angularDirection = CurvedDirection.Angular.Reversed
+ )
+ }
+
+ /**
+ * Creates a [ConfirmationColors] that represents the default colors used in a [Confirmation].
+ */
+ @Composable fun confirmationColors() = MaterialTheme.colorScheme.defaultConfirmationColors
+
+ /**
+ * Creates a [ConfirmationColors] with modified colors used in [Confirmation].
+ *
+ * @param iconColor The icon color.
+ * @param iconContainerColor The icon container color.
+ * @param textColor The text color.
+ */
+ @Composable
+ fun confirmationColors(
+ iconColor: Color = Color.Unspecified,
+ iconContainerColor: Color = Color.Unspecified,
+ textColor: Color = Color.Unspecified,
+ ) =
+ MaterialTheme.colorScheme.defaultConfirmationColors.copy(
+ iconColor = iconColor,
+ iconContainerColor = iconContainerColor,
+ textColor = textColor,
+ )
+
+ /**
+ * Creates a [ConfirmationColors] that represents the default colors used in a
+ * [SuccessConfirmation].
+ */
+ @Composable fun successColors() = MaterialTheme.colorScheme.defaultSuccessConfirmationColors
+
+ /**
+ * Creates a [ConfirmationColors] with modified colors used in [SuccessConfirmation].
+ *
+ * @param iconColor The icon color.
+ * @param iconContainerColor The icon container color.
+ * @param textColor The text color.
+ */
+ @Composable
+ fun successColors(
+ iconColor: Color = Color.Unspecified,
+ iconContainerColor: Color = Color.Unspecified,
+ textColor: Color = Color.Unspecified,
+ ) =
+ MaterialTheme.colorScheme.defaultSuccessConfirmationColors.copy(
+ iconColor = iconColor,
+ iconContainerColor = iconContainerColor,
+ textColor = textColor,
+ )
+
+ /**
+ * Creates a [ConfirmationColors] that represents the default colors used in a
+ * [FailureConfirmation].
+ */
+ @Composable fun failureColors() = MaterialTheme.colorScheme.defaultFailureConfirmationColors
+
+ /**
+ * Creates a [ConfirmationColors] with modified colors used in [FailureConfirmation].
+ *
+ * @param iconColor The icon color.
+ * @param iconContainerColor The icon container color.
+ * @param textColor The text color.
+ */
+ @Composable
+ fun failureColors(
+ iconColor: Color = Color.Unspecified,
+ iconContainerColor: Color = Color.Unspecified,
+ textColor: Color = Color.Unspecified,
+ ) =
+ MaterialTheme.colorScheme.defaultFailureConfirmationColors.copy(
+ iconColor = iconColor,
+ iconContainerColor = iconContainerColor,
+ textColor = textColor,
+ )
+
+ /** Default timeout for the [Confirmation] dialog, in milliseconds. */
+ const val ConfirmationDurationMillis = 4000L
+
+ /** Default icon size for the [Confirmation] with curved content */
+ val IconSize = 52.dp
+
+ /** Default icon size for the [Confirmation] with linear content */
+ val SmallIconSize = 36.dp
+
+ private val ColorScheme.defaultConfirmationColors: ConfirmationColors
+ get() {
+ return defaultConfirmationColorsCached
+ ?: ConfirmationColors(
+ iconColor = fromToken(ColorSchemeKeyTokens.Primary),
+ iconContainerColor = fromToken(ColorSchemeKeyTokens.OnPrimary),
+ textColor = fromToken(ColorSchemeKeyTokens.OnBackground)
+ )
+ .also { defaultConfirmationColorsCached = it }
+ }
+
+ private val ColorScheme.defaultSuccessConfirmationColors: ConfirmationColors
+ get() {
+ return defaultSuccessConfirmationColorsCached
+ ?: ConfirmationColors(
+ iconColor = fromToken(ColorSchemeKeyTokens.Primary),
+ iconContainerColor = fromToken(ColorSchemeKeyTokens.OnPrimary),
+ textColor = fromToken(ColorSchemeKeyTokens.OnBackground)
+ )
+ .also { defaultSuccessConfirmationColorsCached = it }
+ }
+
+ private val ColorScheme.defaultFailureConfirmationColors: ConfirmationColors
+ get() {
+ return defaultFailureConfirmationColorsCached
+ ?: ConfirmationColors(
+ iconColor = fromToken(ColorSchemeKeyTokens.ErrorContainer),
+ iconContainerColor =
+ fromToken(ColorSchemeKeyTokens.OnErrorContainer).copy(.8f),
+ textColor = fromToken(ColorSchemeKeyTokens.OnBackground)
+ )
+ .also { defaultFailureConfirmationColorsCached = it }
+ }
+
+ internal val FailureIconDelay = 67L
+
+ internal val SuccessWidthFraction = 0.496f
+ internal val SuccessHeightFraction = 0.6f
+ internal val FailureSizeFraction = 0.52f
+
+ internal val ConfirmationIconContainerSmallSize = 80.dp
+ internal val ConfirmationIconContainerSizeFraction = 0.52
+
+ internal val ExtraBottomPaddingFraction = 0.02f
+
+ internal val LinearContentSpacing = 8.dp
+ internal val LinearContentMaxLines = 3
+ internal val HorizontalLinearContentPaddingFraction = 0.12f
+}
+
+/**
+ * Represents the colors used in [Confirmation], [SuccessConfirmation] and [FailureConfirmation].
+ *
+ * @param iconColor Color used to tint the icon.
+ * @param iconContainerColor The color of the container behind the icon.
+ * @param textColor Color used to tint the text.
+ */
+class ConfirmationColors(
+ val iconColor: Color,
+ val iconContainerColor: Color,
+ val textColor: Color,
+) {
+ internal fun copy(
+ iconColor: Color? = null,
+ iconContainerColor: Color? = null,
+ textColor: Color? = null
+ ) =
+ ConfirmationColors(
+ iconColor = iconColor ?: this.iconColor,
+ iconContainerColor = iconContainerColor ?: this.iconContainerColor,
+ textColor = textColor ?: this.textColor,
+ )
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other == null || other !is ConfirmationColors) return false
+
+ if (iconColor != other.iconColor) return false
+ if (iconContainerColor != other.iconContainerColor) return false
+ if (textColor != other.textColor) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = iconColor.hashCode()
+ result = 31 * result + iconContainerColor.hashCode()
+ result = 31 * result + textColor.hashCode()
+ return result
+ }
+}
+
+@Composable
+internal fun ConfirmationImpl(
+ show: Boolean,
+ onDismissRequest: () -> Unit,
+ modifier: Modifier,
+ iconContainer: @Composable BoxScope.() -> Unit,
+ curvedText: (CurvedScope.() -> Unit)?,
+ colors: ConfirmationColors,
+ properties: DialogProperties,
+ durationMillis: Long,
+ content: @Composable BoxScope.() -> Unit
+) {
+ val a11yDurationMillis =
+ LocalAccessibilityManager.current?.calculateRecommendedTimeoutMillis(
+ originalTimeoutMillis = durationMillis,
+ containsIcons = true,
+ containsText = curvedText != null,
+ containsControls = false,
+ ) ?: durationMillis
+
+ LaunchedEffect(show, a11yDurationMillis) {
+ if (show) {
+ delay(a11yDurationMillis)
+ onDismissRequest()
+ }
+ }
+
+ Dialog(
+ showDialog = show,
+ modifier = modifier,
+ onDismissRequest = onDismissRequest,
+ properties = properties,
+ ) {
+ Box(modifier = Modifier.fillMaxSize()) {
+ val bottomPadding =
+ if (curvedText != null)
+ screenHeightDp() * ConfirmationDefaults.ExtraBottomPaddingFraction
+ else 0f
+ Box(
+ Modifier.fillMaxSize().padding(bottom = bottomPadding.dp),
+ contentAlignment = Alignment.Center
+ ) {
+ iconContainer()
+ CompositionLocalProvider(LocalContentColor provides colors.iconColor) { content() }
+ }
+ CompositionLocalProvider(LocalContentColor provides colors.textColor) {
+ curvedText?.let { CurvedLayout(anchor = 90f, contentBuilder = curvedText) }
+ }
+ }
+ }
+}
+
+private fun confirmationIconContainer(
+ curvedContent: Boolean,
+ color: Color
+): @Composable BoxScope.() -> Unit = {
+ val iconShape =
+ if (curvedContent) MaterialTheme.shapes.extraLarge else MaterialTheme.shapes.large
+ val width =
+ if (curvedContent) {
+ (screenWidthDp() * ConfirmationDefaults.ConfirmationIconContainerSizeFraction).dp
+ } else ConfirmationDefaults.ConfirmationIconContainerSmallSize
+
+ Box(
+ Modifier.size(width)
+ .graphicsLayer {
+ shape = iconShape
+ clip = true
+ }
+ .background(color)
+ .align(Alignment.Center)
+ )
+}
+
+private fun successIconContainer(color: Color): @Composable BoxScope.() -> Unit = {
+ val width = screenWidthDp() * ConfirmationDefaults.SuccessWidthFraction
+ val height = screenWidthDp() * ConfirmationDefaults.SuccessHeightFraction
+ Box(
+ Modifier.size(width.dp, height.dp)
+ .graphicsLayer {
+ rotationZ = 45f
+ shape = CircleShape
+ clip = true
+ }
+ .background(color)
+ )
+}
+
+private fun failureIconContainer(color: Color): @Composable BoxScope.() -> Unit = {
+ val iconShape = MaterialTheme.shapes.extraLarge
+ val width = screenWidthDp() * ConfirmationDefaults.FailureSizeFraction
+ Box(
+ Modifier.size(width.dp)
+ .graphicsLayer {
+ shape = iconShape
+ clip = true
+ }
+ .background(color)
+ )
+}
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/dialog/Dialog.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Dialog.kt
similarity index 97%
rename from wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/dialog/Dialog.kt
rename to wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Dialog.kt
index d42ba2d..f5c6dc6 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/dialog/Dialog.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Dialog.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package androidx.wear.compose.material3.dialog
+package androidx.wear.compose.material3
import androidx.compose.animation.core.MutableTransitionState
import androidx.compose.animation.core.Transition
@@ -34,9 +34,6 @@
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.window.DialogProperties
import androidx.wear.compose.foundation.rememberSwipeToDismissBoxState
-import androidx.wear.compose.material3.MaterialTheme
-import androidx.wear.compose.material3.ScreenScaffold
-import androidx.wear.compose.material3.SwipeToDismissBox
import androidx.wear.compose.material3.tokens.MotionTokens
/**
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/IconButton.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/IconButton.kt
index 7f89966..1d4e8e4 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/IconButton.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/IconButton.kt
@@ -16,6 +16,8 @@
package androidx.wear.compose.material3
+import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.animation.core.tween
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.interaction.Interaction
import androidx.compose.foundation.interaction.InteractionSource
@@ -27,6 +29,7 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
+import androidx.compose.runtime.State
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
@@ -37,8 +40,10 @@
import androidx.wear.compose.material3.tokens.FilledTonalIconButtonTokens
import androidx.wear.compose.material3.tokens.IconButtonTokens
import androidx.wear.compose.material3.tokens.IconToggleButtonTokens
+import androidx.wear.compose.material3.tokens.MotionTokens
import androidx.wear.compose.material3.tokens.OutlinedIconButtonTokens
import androidx.wear.compose.material3.tokens.ShapeTokens
+import androidx.wear.compose.materialcore.animateSelectionColor
/**
* Wear Material [IconButton] is a circular, icon-only button with transparent background and no
@@ -355,8 +360,8 @@
* @param modifier Modifier to be applied to the toggle button.
* @param enabled Controls the enabled state of the toggle button. When `false`, this toggle button
* will not be clickable.
- * @param colors [ToggleButtonColors] that will be used to resolve the container and content color
- * for this toggle button.
+ * @param colors [IconToggleButtonColors] that will be used to resolve the container and content
+ * color for this toggle button.
* @param interactionSource an optional hoisted [MutableInteractionSource] for observing and
* emitting [Interaction]s for this button. You can use this to change the button's appearance or
* preview the button in different states. Note that if `null` is provided, interactions will
@@ -372,7 +377,7 @@
onCheckedChange: (Boolean) -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
- colors: ToggleButtonColors = IconButtonDefaults.iconToggleButtonColors(),
+ colors: IconToggleButtonColors = IconButtonDefaults.iconToggleButtonColors(),
interactionSource: MutableInteractionSource? = null,
shape: Shape = IconButtonDefaults.shape,
border: BorderStroke? = null,
@@ -606,7 +611,7 @@
)
/**
- * Creates a [ToggleButtonColors] for a [IconToggleButton]
+ * Creates an [IconToggleButtonColors] for a [IconToggleButton]
* - by default, a colored background with a contrasting content color.
*
* If the button is disabled, then the colors will have an alpha ([DisabledContentAlpha] and
@@ -616,7 +621,7 @@
fun iconToggleButtonColors() = MaterialTheme.colorScheme.defaultIconToggleButtonColors
/**
- * Creates a [ToggleButtonColors] for a [IconToggleButton]
+ * Creates a [IconToggleButtonColors] for a [IconToggleButton]
* - by default, a colored background with a contrasting content color.
*
* If the button is disabled, then the colors will have an alpha ([DisabledContentAlpha] and
@@ -649,7 +654,7 @@
disabledCheckedContentColor: Color = Color.Unspecified,
disabledUncheckedContainerColor: Color = Color.Unspecified,
disabledUncheckedContentColor: Color = Color.Unspecified,
- ): ToggleButtonColors =
+ ): IconToggleButtonColors =
MaterialTheme.colorScheme.defaultIconToggleButtonColors.copy(
checkedContainerColor = checkedContainerColor,
checkedContentColor = checkedContentColor,
@@ -797,10 +802,10 @@
.also { defaultIconButtonColorsCached = it }
}
- private val ColorScheme.defaultIconToggleButtonColors: ToggleButtonColors
+ private val ColorScheme.defaultIconToggleButtonColors: IconToggleButtonColors
get() {
return defaultIconToggleButtonColorsCached
- ?: ToggleButtonColors(
+ ?: IconToggleButtonColors(
checkedContainerColor =
fromToken(IconToggleButtonTokens.CheckedContainerColor),
checkedContentColor = fromToken(IconToggleButtonTokens.CheckedContentColor),
@@ -914,3 +919,129 @@
return result
}
}
+
+/**
+ * Represents the different container and content colors used for [IconToggleButton] in various
+ * states, that are checked, unchecked, enabled and disabled.
+ *
+ * @param checkedContainerColor Container or background color when the toggle button is checked
+ * @param checkedContentColor Color of the content (text or icon) when the toggle button is checked
+ * @param uncheckedContainerColor Container or background color when the toggle button is unchecked
+ * @param uncheckedContentColor Color of the content (text or icon) when the toggle button is
+ * unchecked
+ * @param disabledCheckedContainerColor Container or background color when the toggle button is
+ * disabled and checked
+ * @param disabledCheckedContentColor Color of content (text or icon) when the toggle button is
+ * disabled and checked
+ * @param disabledUncheckedContainerColor Container or background color when the toggle button is
+ * disabled and unchecked
+ * @param disabledUncheckedContentColor Color of the content (text or icon) when the toggle button
+ * is disabled and unchecked
+ */
+@Immutable
+class IconToggleButtonColors(
+ val checkedContainerColor: Color,
+ val checkedContentColor: Color,
+ val uncheckedContainerColor: Color,
+ val uncheckedContentColor: Color,
+ val disabledCheckedContainerColor: Color,
+ val disabledCheckedContentColor: Color,
+ val disabledUncheckedContainerColor: Color,
+ val disabledUncheckedContentColor: Color,
+) {
+ internal fun copy(
+ checkedContainerColor: Color,
+ checkedContentColor: Color,
+ uncheckedContainerColor: Color,
+ uncheckedContentColor: Color,
+ disabledCheckedContainerColor: Color,
+ disabledCheckedContentColor: Color,
+ disabledUncheckedContainerColor: Color,
+ disabledUncheckedContentColor: Color,
+ ): IconToggleButtonColors =
+ IconToggleButtonColors(
+ checkedContainerColor = checkedContainerColor.takeOrElse { this.checkedContainerColor },
+ checkedContentColor = checkedContentColor.takeOrElse { this.checkedContentColor },
+ uncheckedContainerColor =
+ uncheckedContainerColor.takeOrElse { this.uncheckedContainerColor },
+ uncheckedContentColor = uncheckedContentColor.takeOrElse { this.uncheckedContentColor },
+ disabledCheckedContainerColor =
+ disabledCheckedContainerColor.takeOrElse { this.disabledCheckedContainerColor },
+ disabledCheckedContentColor =
+ disabledCheckedContentColor.takeOrElse { this.disabledCheckedContentColor },
+ disabledUncheckedContainerColor =
+ disabledUncheckedContainerColor.takeOrElse { this.disabledUncheckedContainerColor },
+ disabledUncheckedContentColor =
+ disabledUncheckedContentColor.takeOrElse { this.disabledUncheckedContentColor },
+ )
+
+ /**
+ * Determines the container color based on whether the toggle button is [enabled] and [checked].
+ *
+ * @param enabled Whether the toggle button is enabled
+ * @param checked Whether the toggle button is checked
+ */
+ @Composable
+ internal fun containerColor(enabled: Boolean, checked: Boolean): State<Color> =
+ animateSelectionColor(
+ enabled = enabled,
+ checked = checked,
+ checkedColor = checkedContainerColor,
+ uncheckedColor = uncheckedContainerColor,
+ disabledCheckedColor = disabledCheckedContainerColor,
+ disabledUncheckedColor = disabledUncheckedContainerColor,
+ animationSpec = COLOR_ANIMATION_SPEC
+ )
+
+ /**
+ * Determines the content color based on whether the toggle button is [enabled] and [checked].
+ *
+ * @param enabled Whether the toggle button is enabled
+ * @param checked Whether the toggle button is checked
+ */
+ @Composable
+ internal fun contentColor(enabled: Boolean, checked: Boolean): State<Color> =
+ animateSelectionColor(
+ enabled = enabled,
+ checked = checked,
+ checkedColor = checkedContentColor,
+ uncheckedColor = uncheckedContentColor,
+ disabledCheckedColor = disabledCheckedContentColor,
+ disabledUncheckedColor = disabledUncheckedContentColor,
+ animationSpec = COLOR_ANIMATION_SPEC
+ )
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other == null) return false
+ if (this::class != other::class) return false
+
+ other as IconToggleButtonColors
+
+ if (checkedContainerColor != other.checkedContainerColor) return false
+ if (checkedContentColor != other.checkedContentColor) return false
+ if (uncheckedContainerColor != other.uncheckedContainerColor) return false
+ if (uncheckedContentColor != other.uncheckedContentColor) return false
+ if (disabledCheckedContainerColor != other.disabledCheckedContainerColor) return false
+ if (disabledCheckedContentColor != other.disabledCheckedContentColor) return false
+ if (disabledUncheckedContainerColor != other.disabledUncheckedContainerColor) return false
+ if (disabledUncheckedContentColor != other.disabledUncheckedContentColor) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = checkedContainerColor.hashCode()
+ result = 31 * result + checkedContentColor.hashCode()
+ result = 31 * result + uncheckedContainerColor.hashCode()
+ result = 31 * result + uncheckedContentColor.hashCode()
+ result = 31 * result + disabledCheckedContainerColor.hashCode()
+ result = 31 * result + disabledCheckedContentColor.hashCode()
+ result = 31 * result + disabledUncheckedContainerColor.hashCode()
+ result = 31 * result + disabledUncheckedContentColor.hashCode()
+ return result
+ }
+}
+
+private val COLOR_ANIMATION_SPEC: AnimationSpec<Color> =
+ tween(MotionTokens.DurationMedium1, 0, MotionTokens.EasingStandardDecelerate)
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/OpenOnPhoneDialog.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/OpenOnPhoneDialog.kt
new file mode 100644
index 0000000..c7944ef
--- /dev/null
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/OpenOnPhoneDialog.kt
@@ -0,0 +1,338 @@
+/*
+ * 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.wear.compose.material3
+
+import androidx.compose.animation.core.Animatable
+import androidx.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi
+import androidx.compose.animation.graphics.res.animatedVectorResource
+import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter
+import androidx.compose.animation.graphics.vector.AnimatedImageVector
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.platform.LocalAccessibilityManager
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.window.DialogProperties
+import androidx.wear.compose.foundation.CurvedDirection
+import androidx.wear.compose.foundation.CurvedLayout
+import androidx.wear.compose.foundation.CurvedModifier
+import androidx.wear.compose.foundation.CurvedScope
+import androidx.wear.compose.foundation.CurvedTextStyle
+import androidx.wear.compose.foundation.padding
+import androidx.wear.compose.material3.tokens.ColorSchemeKeyTokens
+import androidx.wear.compose.materialcore.screenHeightDp
+import androidx.wear.compose.materialcore.screenWidthDp
+import kotlinx.coroutines.delay
+
+/**
+ * A full-screen dialog that displays an animated icon with a curved text at the bottom.
+ *
+ * The dialog will be showing a message to the user for [durationMillis]. After a specified timeout,
+ * the [onDismissRequest] callback will be invoked, where it's up to the caller to handle the
+ * dismissal. To hide the dialog, [show] parameter should be set to false.
+ *
+ * This dialog is typically used to indicate that an action has been initiated and will continue on
+ * the user's phone. Once this dialog is displayed, it's developer responsibility to establish the
+ * connection between the watch and the phone.
+ *
+ * Example of an [OpenOnPhoneDialog] usage:
+ *
+ * @sample androidx.wear.compose.material3.samples.OpenOnPhoneDialogSample
+ * @param show A boolean indicating whether the dialog should be displayed.
+ * @param onDismissRequest A lambda function to be called when the dialog is dismissed - either by
+ * swiping right or when the [durationMillis] has passed.
+ * @param modifier Modifier to be applied to the dialog content.
+ * @param curvedText A slot for displaying curved text content which will be shown along the bottom
+ * edge of the dialog. Defaults to a localized open on phone message.
+ * @param colors [OpenOnPhoneDialogColors] that will be used to resolve the colors used for this
+ * [OpenOnPhoneDialog].
+ * @param properties An optional [DialogProperties] object for configuring the dialog's behavior.
+ * @param durationMillis The duration in milliseconds for which the dialog is displayed. Defaults to
+ * [OpenOnPhoneDialogDefaults.DurationMillis].
+ * @param content A slot for displaying an icon inside the open on phone dialog, which can be
+ * animated. Defaults to [OpenOnPhoneDialogDefaults.Icon].
+ */
+@Composable
+fun OpenOnPhoneDialog(
+ show: Boolean,
+ onDismissRequest: () -> Unit,
+ modifier: Modifier = Modifier,
+ curvedText: (CurvedScope.() -> Unit)? = OpenOnPhoneDialogDefaults.curvedText(),
+ colors: OpenOnPhoneDialogColors = OpenOnPhoneDialogDefaults.colors(),
+ properties: DialogProperties = DialogProperties(),
+ durationMillis: Long = OpenOnPhoneDialogDefaults.DurationMillis,
+ content: @Composable BoxScope.() -> Unit = OpenOnPhoneDialogDefaults.Icon,
+) {
+ var progress by remember(show) { mutableFloatStateOf(0f) }
+ val animatable = remember { Animatable(0f) }
+
+ val a11yDurationMillis =
+ LocalAccessibilityManager.current?.calculateRecommendedTimeoutMillis(
+ originalTimeoutMillis = durationMillis,
+ containsIcons = true,
+ containsText = curvedText != null,
+ containsControls = false,
+ ) ?: durationMillis
+
+ LaunchedEffect(show, a11yDurationMillis) {
+ if (show) {
+ animatable.snapTo(0f)
+ animatable.animateTo(
+ targetValue = 1f,
+ animationSpec =
+ tween(durationMillis = a11yDurationMillis.toInt(), easing = LinearEasing),
+ ) {
+ progress = value
+ }
+ onDismissRequest()
+ }
+ }
+
+ Dialog(
+ showDialog = show,
+ modifier = modifier,
+ onDismissRequest = onDismissRequest,
+ properties = properties,
+ ) {
+ Box(modifier = Modifier.fillMaxSize()) {
+ val bottomPadding =
+ if (curvedText != null)
+ screenHeightDp() * OpenOnPhoneDialogDefaults.ExtraBottomPaddingFraction
+ else 0f
+ Box(
+ Modifier.fillMaxSize().padding(bottom = bottomPadding.dp),
+ contentAlignment = Alignment.Center
+ ) {
+ iconContainer(
+ iconContainerColor = colors.iconContainerColor,
+ progressIndicatorColors =
+ ProgressIndicatorColors(
+ SolidColor(colors.progressIndicatorColor),
+ SolidColor(colors.progressTrackColor)
+ ),
+ progress = { progress }
+ )()
+ CompositionLocalProvider(LocalContentColor provides colors.iconColor) { content() }
+ }
+ CompositionLocalProvider(LocalContentColor provides colors.textColor) {
+ curvedText?.let { CurvedLayout(anchor = 90f, contentBuilder = curvedText) }
+ }
+ }
+ }
+}
+
+/** Contains the default values used by [OpenOnPhoneDialog]. */
+object OpenOnPhoneDialogDefaults {
+
+ /**
+ * A default composable used in [OpenOnPhoneDialog] that displays an open on phone icon with an
+ * animation.
+ */
+ @OptIn(ExperimentalAnimationGraphicsApi::class)
+ val Icon: @Composable BoxScope.() -> Unit = {
+ val animation =
+ AnimatedImageVector.animatedVectorResource(R.drawable.open_on_phone_animation)
+ var atEnd by remember { mutableStateOf(false) }
+ LaunchedEffect(Unit) {
+ delay(IconDelay)
+ atEnd = true
+ }
+ Icon(
+ painter = rememberAnimatedVectorPainter(animation, atEnd),
+ contentDescription = null,
+ modifier = Modifier.size(IconSize).align(Alignment.Center),
+ )
+ }
+
+ /**
+ * A default composable that displays text along a curved path, used in [OpenOnPhoneDialog].
+ *
+ * @param text The text to display. Defaults to an open on phone message.
+ * @param style The style to apply to the text. Defaults to
+ * CurvedTextStyle(MaterialTheme.typography.titleLarge).
+ */
+ @Composable
+ fun curvedText(
+ text: String = LocalContext.current.resources.getString(R.string.wear_m3c_open_on_phone),
+ style: CurvedTextStyle = CurvedTextStyle(MaterialTheme.typography.titleLarge)
+ ): CurvedScope.() -> Unit = {
+ curvedText(
+ text = text,
+ style = style,
+ maxSweepAngle = CurvedTextDefaults.StaticContentMaxSweepAngle,
+ modifier = CurvedModifier.padding(PaddingDefaults.edgePadding),
+ angularDirection = CurvedDirection.Angular.Reversed
+ )
+ }
+
+ /**
+ * Creates a [OpenOnPhoneDialogColors] that represents the default colors used in
+ * [OpenOnPhoneDialog].
+ */
+ @Composable fun colors() = MaterialTheme.colorScheme.defaultOpenOnPhoneDialogColors
+
+ /**
+ * Creates a [OpenOnPhoneDialogColors] with modified colors used in [OpenOnPhoneDialog].
+ *
+ * @param iconColor The icon color.
+ * @param iconContainerColor The icon container color.
+ * @param progressIndicatorColor The progress indicator color.
+ * @param progressTrackColor The progress track color.
+ * @param textColor The text color.
+ */
+ @Composable
+ fun colors(
+ iconColor: Color = Color.Unspecified,
+ iconContainerColor: Color = Color.Unspecified,
+ progressIndicatorColor: Color = Color.Unspecified,
+ progressTrackColor: Color = Color.Unspecified,
+ textColor: Color = Color.Unspecified
+ ) =
+ MaterialTheme.colorScheme.defaultOpenOnPhoneDialogColors.copy(
+ iconColor = iconColor,
+ iconContainerColor = iconContainerColor,
+ progressIndicatorColor = progressIndicatorColor,
+ progressTrackColor = progressTrackColor,
+ textColor = textColor
+ )
+
+ /** Default timeout for the [OpenOnPhoneDialog] dialog, in milliseconds. */
+ const val DurationMillis = 4000L
+
+ internal val IconDelay = 67L
+ internal val SizeFraction = 0.6f
+ internal val ExtraBottomPaddingFraction = 0.02f
+ internal val IconSize = 52.dp
+
+ internal val progressIndicatorStrokeWidth = 5.dp
+ internal val progressIndicatorPadding = 5.dp
+
+ private val ColorScheme.defaultOpenOnPhoneDialogColors: OpenOnPhoneDialogColors
+ get() {
+ return mDefaultOpenOnPhoneDialogColorsCached
+ ?: OpenOnPhoneDialogColors(
+ iconColor = fromToken(ColorSchemeKeyTokens.Primary),
+ iconContainerColor = fromToken(ColorSchemeKeyTokens.PrimaryContainer),
+ progressIndicatorColor = fromToken(ColorSchemeKeyTokens.Primary),
+ progressTrackColor = fromToken(ColorSchemeKeyTokens.OnPrimary),
+ textColor = fromToken(ColorSchemeKeyTokens.OnBackground)
+ )
+ .also { mDefaultOpenOnPhoneDialogColorsCached = it }
+ }
+}
+
+/**
+ * Represents the colors used in [OpenOnPhoneDialog].
+ *
+ * @param iconColor Color used to tint the icon.
+ * @param iconContainerColor The color of the container behind the icon.
+ * @param progressIndicatorColor Color used to draw the indicator arc of progress indicator.
+ * @param progressTrackColor Color used to draw the track of progress indicator.
+ * @param textColor Color used to draw the text.
+ */
+class OpenOnPhoneDialogColors(
+ val iconColor: Color,
+ val iconContainerColor: Color,
+ val progressIndicatorColor: Color,
+ val progressTrackColor: Color,
+ val textColor: Color
+) {
+ internal fun copy(
+ iconColor: Color? = null,
+ iconContainerColor: Color? = null,
+ progressIndicatorColor: Color? = null,
+ progressTrackColor: Color? = null,
+ textColor: Color? = null
+ ) =
+ OpenOnPhoneDialogColors(
+ iconColor = iconColor ?: this.iconColor,
+ iconContainerColor = iconContainerColor ?: this.iconContainerColor,
+ progressIndicatorColor = progressIndicatorColor ?: this.progressIndicatorColor,
+ progressTrackColor = progressTrackColor ?: this.progressTrackColor,
+ textColor = textColor ?: this.textColor
+ )
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other == null || other !is OpenOnPhoneDialogColors) return false
+
+ if (iconColor != other.iconColor) return false
+ if (iconContainerColor != other.iconContainerColor) return false
+ if (progressIndicatorColor != other.progressIndicatorColor) return false
+ if (progressTrackColor != other.progressTrackColor) return false
+ if (textColor != other.textColor) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = iconColor.hashCode()
+ result = 31 * result + iconContainerColor.hashCode()
+ result = 31 * result + progressIndicatorColor.hashCode()
+ result = 31 * result + progressTrackColor.hashCode()
+ result = 31 * result + textColor.hashCode()
+ return result
+ }
+}
+
+private fun iconContainer(
+ iconContainerColor: Color,
+ progressIndicatorColors: ProgressIndicatorColors,
+ progress: () -> Float
+): @Composable BoxScope.() -> Unit = {
+ val size = screenWidthDp() * OpenOnPhoneDialogDefaults.SizeFraction
+ Box(Modifier.size(size.dp)) {
+ Box(
+ Modifier.fillMaxSize()
+ .padding(
+ OpenOnPhoneDialogDefaults.progressIndicatorStrokeWidth +
+ OpenOnPhoneDialogDefaults.progressIndicatorPadding
+ )
+ .graphicsLayer {
+ shape = CircleShape
+ clip = true
+ }
+ .background(iconContainerColor)
+ )
+
+ CircularProgressIndicator(
+ progress = progress,
+ strokeWidth = OpenOnPhoneDialogDefaults.progressIndicatorStrokeWidth,
+ colors = progressIndicatorColors
+ )
+ }
+}
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TextButton.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TextButton.kt
index c559aa5..20e8047 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TextButton.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/TextButton.kt
@@ -16,6 +16,8 @@
package androidx.wear.compose.material3
+import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.animation.core.tween
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.interaction.Interaction
import androidx.compose.foundation.interaction.InteractionSource
@@ -28,16 +30,19 @@
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.Stable
+import androidx.compose.runtime.State
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.takeOrElse
import androidx.wear.compose.material3.tokens.FilledTextButtonTokens
import androidx.wear.compose.material3.tokens.FilledTonalTextButtonTokens
+import androidx.wear.compose.material3.tokens.MotionTokens
import androidx.wear.compose.material3.tokens.OutlinedTextButtonTokens
import androidx.wear.compose.material3.tokens.ShapeTokens
import androidx.wear.compose.material3.tokens.TextButtonTokens
import androidx.wear.compose.material3.tokens.TextToggleButtonTokens
+import androidx.wear.compose.materialcore.animateSelectionColor
/**
* Wear Material [TextButton] is a circular, text-only button with transparent background and no
@@ -170,7 +175,7 @@
onCheckedChange: (Boolean) -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
- colors: ToggleButtonColors = TextButtonDefaults.textToggleButtonColors(),
+ colors: TextToggleButtonColors = TextButtonDefaults.textToggleButtonColors(),
interactionSource: MutableInteractionSource? = null,
shape: Shape = TextButtonDefaults.shape,
border: BorderStroke? = null,
@@ -398,7 +403,7 @@
)
/**
- * Creates a [ToggleButtonColors] for a [TextToggleButton]
+ * Creates a [TextToggleButtonColors] for a [TextToggleButton]
* - by default, a colored background with a contrasting content color. If the button is
* disabled, then the colors will have an alpha ([DisabledContainerAlpha] or
* [DisabledContentAlpha]) value applied.
@@ -407,7 +412,7 @@
fun textToggleButtonColors() = MaterialTheme.colorScheme.defaultTextToggleButtonColors
/**
- * Creates a [ToggleButtonColors] for a [TextToggleButton]
+ * Creates a [TextToggleButtonColors] for a [TextToggleButton]
* - by default, a colored background with a contrasting content color. If the button is
* disabled, then the colors will have an alpha ([DisabledContainerAlpha] or
* [DisabledContentAlpha]) value applied.
@@ -439,7 +444,7 @@
disabledCheckedContentColor: Color = Color.Unspecified,
disabledUncheckedContainerColor: Color = Color.Unspecified,
disabledUncheckedContentColor: Color = Color.Unspecified,
- ): ToggleButtonColors =
+ ): TextToggleButtonColors =
MaterialTheme.colorScheme.defaultTextToggleButtonColors.copy(
checkedContainerColor = checkedContainerColor,
checkedContentColor = checkedContentColor,
@@ -575,10 +580,10 @@
.also { defaultTextButtonColorsCached = it }
}
- private val ColorScheme.defaultTextToggleButtonColors: ToggleButtonColors
+ private val ColorScheme.defaultTextToggleButtonColors: TextToggleButtonColors
get() {
return defaultTextToggleButtonColorsCached
- ?: ToggleButtonColors(
+ ?: TextToggleButtonColors(
checkedContainerColor =
fromToken(TextToggleButtonTokens.CheckedContainerColor),
checkedContentColor = fromToken(TextToggleButtonTokens.CheckedContentColor),
@@ -691,3 +696,129 @@
return result
}
}
+
+/**
+ * Represents the different container and content colors used for [TextToggleButton] in various
+ * states, that are checked, unchecked, enabled and disabled.
+ *
+ * @param checkedContainerColor Container or background color when the toggle button is checked
+ * @param checkedContentColor Color of the content (text or icon) when the toggle button is checked
+ * @param uncheckedContainerColor Container or background color when the toggle button is unchecked
+ * @param uncheckedContentColor Color of the content (text or icon) when the toggle button is
+ * unchecked
+ * @param disabledCheckedContainerColor Container or background color when the toggle button is
+ * disabled and checked
+ * @param disabledCheckedContentColor Color of content (text or icon) when the toggle button is
+ * disabled and checked
+ * @param disabledUncheckedContainerColor Container or background color when the toggle button is
+ * disabled and unchecked
+ * @param disabledUncheckedContentColor Color of the content (text or icon) when the toggle button
+ * is disabled and unchecked
+ */
+@Immutable
+class TextToggleButtonColors(
+ val checkedContainerColor: Color,
+ val checkedContentColor: Color,
+ val uncheckedContainerColor: Color,
+ val uncheckedContentColor: Color,
+ val disabledCheckedContainerColor: Color,
+ val disabledCheckedContentColor: Color,
+ val disabledUncheckedContainerColor: Color,
+ val disabledUncheckedContentColor: Color,
+) {
+ internal fun copy(
+ checkedContainerColor: Color,
+ checkedContentColor: Color,
+ uncheckedContainerColor: Color,
+ uncheckedContentColor: Color,
+ disabledCheckedContainerColor: Color,
+ disabledCheckedContentColor: Color,
+ disabledUncheckedContainerColor: Color,
+ disabledUncheckedContentColor: Color,
+ ): TextToggleButtonColors =
+ TextToggleButtonColors(
+ checkedContainerColor = checkedContainerColor.takeOrElse { this.checkedContainerColor },
+ checkedContentColor = checkedContentColor.takeOrElse { this.checkedContentColor },
+ uncheckedContainerColor =
+ uncheckedContainerColor.takeOrElse { this.uncheckedContainerColor },
+ uncheckedContentColor = uncheckedContentColor.takeOrElse { this.uncheckedContentColor },
+ disabledCheckedContainerColor =
+ disabledCheckedContainerColor.takeOrElse { this.disabledCheckedContainerColor },
+ disabledCheckedContentColor =
+ disabledCheckedContentColor.takeOrElse { this.disabledCheckedContentColor },
+ disabledUncheckedContainerColor =
+ disabledUncheckedContainerColor.takeOrElse { this.disabledUncheckedContainerColor },
+ disabledUncheckedContentColor =
+ disabledUncheckedContentColor.takeOrElse { this.disabledUncheckedContentColor },
+ )
+
+ /**
+ * Determines the container color based on whether the toggle button is [enabled] and [checked].
+ *
+ * @param enabled Whether the toggle button is enabled
+ * @param checked Whether the toggle button is checked
+ */
+ @Composable
+ internal fun containerColor(enabled: Boolean, checked: Boolean): State<Color> =
+ animateSelectionColor(
+ enabled = enabled,
+ checked = checked,
+ checkedColor = checkedContainerColor,
+ uncheckedColor = uncheckedContainerColor,
+ disabledCheckedColor = disabledCheckedContainerColor,
+ disabledUncheckedColor = disabledUncheckedContainerColor,
+ animationSpec = COLOR_ANIMATION_SPEC
+ )
+
+ /**
+ * Determines the content color based on whether the toggle button is [enabled] and [checked].
+ *
+ * @param enabled Whether the toggle button is enabled
+ * @param checked Whether the toggle button is checked
+ */
+ @Composable
+ internal fun contentColor(enabled: Boolean, checked: Boolean): State<Color> =
+ animateSelectionColor(
+ enabled = enabled,
+ checked = checked,
+ checkedColor = checkedContentColor,
+ uncheckedColor = uncheckedContentColor,
+ disabledCheckedColor = disabledCheckedContentColor,
+ disabledUncheckedColor = disabledUncheckedContentColor,
+ animationSpec = COLOR_ANIMATION_SPEC
+ )
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other == null) return false
+ if (this::class != other::class) return false
+
+ other as TextToggleButtonColors
+
+ if (checkedContainerColor != other.checkedContainerColor) return false
+ if (checkedContentColor != other.checkedContentColor) return false
+ if (uncheckedContainerColor != other.uncheckedContainerColor) return false
+ if (uncheckedContentColor != other.uncheckedContentColor) return false
+ if (disabledCheckedContainerColor != other.disabledCheckedContainerColor) return false
+ if (disabledCheckedContentColor != other.disabledCheckedContentColor) return false
+ if (disabledUncheckedContainerColor != other.disabledUncheckedContainerColor) return false
+ if (disabledUncheckedContentColor != other.disabledUncheckedContentColor) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = checkedContainerColor.hashCode()
+ result = 31 * result + checkedContentColor.hashCode()
+ result = 31 * result + uncheckedContainerColor.hashCode()
+ result = 31 * result + uncheckedContentColor.hashCode()
+ result = 31 * result + disabledCheckedContainerColor.hashCode()
+ result = 31 * result + disabledCheckedContentColor.hashCode()
+ result = 31 * result + disabledUncheckedContainerColor.hashCode()
+ result = 31 * result + disabledUncheckedContentColor.hashCode()
+ return result
+ }
+}
+
+private val COLOR_ANIMATION_SPEC: AnimationSpec<Color> =
+ tween(MotionTokens.DurationMedium1, 0, MotionTokens.EasingStandardDecelerate)
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ToggleButton.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ToggleButton.kt
deleted file mode 100644
index cc9cc49..0000000
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ToggleButton.kt
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.wear.compose.material3
-
-import androidx.compose.animation.core.AnimationSpec
-import androidx.compose.animation.core.tween
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.Immutable
-import androidx.compose.runtime.State
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.takeOrElse
-import androidx.wear.compose.material3.tokens.MotionTokens
-import androidx.wear.compose.materialcore.animateSelectionColor
-
-/**
- * Represents the different container and content colors used for [IconToggleButton] and
- * [TextToggleButton]) in various states, that are checked, unchecked, enabled and disabled.
- *
- * @param checkedContainerColor Container or background color when the toggle button is checked
- * @param checkedContentColor Color of the content (text or icon) when the toggle button is checked
- * @param uncheckedContainerColor Container or background color when the toggle button is unchecked
- * @param uncheckedContentColor Color of the content (text or icon) when the toggle button is
- * unchecked
- * @param disabledCheckedContainerColor Container or background color when the toggle button is
- * disabled and checked
- * @param disabledCheckedContentColor Color of content (text or icon) when the toggle button is
- * disabled and checked
- * @param disabledUncheckedContainerColor Container or background color when the toggle button is
- * disabled and unchecked
- * @param disabledUncheckedContentColor Color of the content (text or icon) when the toggle button
- * is disabled and unchecked
- */
-@Immutable
-class ToggleButtonColors(
- val checkedContainerColor: Color,
- val checkedContentColor: Color,
- val uncheckedContainerColor: Color,
- val uncheckedContentColor: Color,
- val disabledCheckedContainerColor: Color,
- val disabledCheckedContentColor: Color,
- val disabledUncheckedContainerColor: Color,
- val disabledUncheckedContentColor: Color,
-) {
- internal fun copy(
- checkedContainerColor: Color,
- checkedContentColor: Color,
- uncheckedContainerColor: Color,
- uncheckedContentColor: Color,
- disabledCheckedContainerColor: Color,
- disabledCheckedContentColor: Color,
- disabledUncheckedContainerColor: Color,
- disabledUncheckedContentColor: Color,
- ): ToggleButtonColors =
- ToggleButtonColors(
- checkedContainerColor = checkedContainerColor.takeOrElse { this.checkedContainerColor },
- checkedContentColor = checkedContentColor.takeOrElse { this.checkedContentColor },
- uncheckedContainerColor =
- uncheckedContainerColor.takeOrElse { this.uncheckedContainerColor },
- uncheckedContentColor = uncheckedContentColor.takeOrElse { this.uncheckedContentColor },
- disabledCheckedContainerColor =
- disabledCheckedContainerColor.takeOrElse { this.disabledCheckedContainerColor },
- disabledCheckedContentColor =
- disabledCheckedContentColor.takeOrElse { this.disabledCheckedContentColor },
- disabledUncheckedContainerColor =
- disabledUncheckedContainerColor.takeOrElse { this.disabledUncheckedContainerColor },
- disabledUncheckedContentColor =
- disabledUncheckedContentColor.takeOrElse { this.disabledUncheckedContentColor },
- )
-
- /**
- * Determines the container color based on whether the toggle button is [enabled] and [checked].
- *
- * @param enabled Whether the toggle button is enabled
- * @param checked Whether the toggle button is checked
- */
- @Composable
- internal fun containerColor(enabled: Boolean, checked: Boolean): State<Color> =
- animateSelectionColor(
- enabled = enabled,
- checked = checked,
- checkedColor = checkedContainerColor,
- uncheckedColor = uncheckedContainerColor,
- disabledCheckedColor = disabledCheckedContainerColor,
- disabledUncheckedColor = disabledUncheckedContainerColor,
- animationSpec = COLOR_ANIMATION_SPEC
- )
-
- /**
- * Determines the content color based on whether the toggle button is [enabled] and [checked].
- *
- * @param enabled Whether the toggle button is enabled
- * @param checked Whether the toggle button is checked
- */
- @Composable
- internal fun contentColor(enabled: Boolean, checked: Boolean): State<Color> =
- animateSelectionColor(
- enabled = enabled,
- checked = checked,
- checkedColor = checkedContentColor,
- uncheckedColor = uncheckedContentColor,
- disabledCheckedColor = disabledCheckedContentColor,
- disabledUncheckedColor = disabledUncheckedContentColor,
- animationSpec = COLOR_ANIMATION_SPEC
- )
-
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (other == null) return false
- if (this::class != other::class) return false
-
- other as ToggleButtonColors
-
- if (checkedContainerColor != other.checkedContainerColor) return false
- if (checkedContentColor != other.checkedContentColor) return false
- if (uncheckedContainerColor != other.uncheckedContainerColor) return false
- if (uncheckedContentColor != other.uncheckedContentColor) return false
- if (disabledCheckedContainerColor != other.disabledCheckedContainerColor) return false
- if (disabledCheckedContentColor != other.disabledCheckedContentColor) return false
- if (disabledUncheckedContainerColor != other.disabledUncheckedContainerColor) return false
- if (disabledUncheckedContentColor != other.disabledUncheckedContentColor) return false
-
- return true
- }
-
- override fun hashCode(): Int {
- var result = checkedContainerColor.hashCode()
- result = 31 * result + checkedContentColor.hashCode()
- result = 31 * result + uncheckedContainerColor.hashCode()
- result = 31 * result + uncheckedContentColor.hashCode()
- result = 31 * result + disabledCheckedContainerColor.hashCode()
- result = 31 * result + disabledCheckedContentColor.hashCode()
- result = 31 * result + disabledUncheckedContainerColor.hashCode()
- result = 31 * result + disabledUncheckedContentColor.hashCode()
- return result
- }
-}
-
-private val COLOR_ANIMATION_SPEC: AnimationSpec<Color> =
- tween(MotionTokens.DurationMedium1, 0, MotionTokens.EasingStandardDecelerate)
diff --git a/wear/compose/compose-material3/src/main/res/drawable/check_animation.xml b/wear/compose/compose-material3/src/main/res/drawable/check_animation.xml
new file mode 100644
index 0000000..90fd02a
--- /dev/null
+++ b/wear/compose/compose-material3/src/main/res/drawable/check_animation.xml
@@ -0,0 +1,17 @@
+<!--
+ 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.
+ -->
+
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"><aapt:attr name="android:drawable"><vector android:height="300dp" android:width="400dp" android:viewportHeight="300" android:viewportWidth="400"><group android:name="_R_G"><group android:name="_R_G_L_0_G" android:translateX="200" android:translateY="176" android:scaleX="1.05507" android:scaleY="1.05556"><path android:name="_R_G_L_0_G_D_0_P_0" android:strokeColor="#d3e3fd" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="34" android:strokeAlpha="1" android:trimPathStart="0" android:trimPathEnd="0" android:trimPathOffset="0" android:pathData=" M-147.5 -20 C-147.5,-20 -51.5,76 -51.5,76 C-51.5,76 151.5,-127 151.5,-127 "/></group></group><group android:name="time_group"/></vector></aapt:attr><target android:name="_R_G_L_0_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="trimPathEnd" android:duration="250" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="time_group"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="translateX" android:duration="10017" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/></set></aapt:attr></target></animated-vector>
\ No newline at end of file
diff --git a/wear/compose/compose-material3/src/main/res/drawable/failure_animation.xml b/wear/compose/compose-material3/src/main/res/drawable/failure_animation.xml
new file mode 100644
index 0000000..2c5120b
--- /dev/null
+++ b/wear/compose/compose-material3/src/main/res/drawable/failure_animation.xml
@@ -0,0 +1,17 @@
+<!--
+ 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.
+ -->
+
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"><aapt:attr name="android:drawable"><vector android:height="800dp" android:width="800dp" android:viewportHeight="800" android:viewportWidth="800"><group android:name="_R_G"><group android:name="_R_G_L_2_G" android:translateX="414" android:translateY="398" android:scaleX="10.57" android:scaleY="10.57"><path android:name="_R_G_L_2_G_D_0_P_0" android:fillColor="#ff8986" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-14.49 17.77 C-14.49,17.77 -14.49,19.61 -14.49,19.61 C-14.49,19.72 -14.45,19.81 -14.38,19.88 C-14.31,19.95 -14.22,19.99 -14.11,19.99 C-14.11,19.99 7.78,19.99 7.78,19.99 C7.88,19.99 7.97,19.95 8.05,19.88 C8.12,19.81 8.15,19.72 8.15,19.61 C8.15,19.61 8.15,17.77 8.15,17.77 C8.15,17.77 -14.49,17.77 -14.49,17.77c M-14.49 -17.77 C-14.49,-17.77 8.15,-17.77 8.15,-17.77 C8.15,-17.77 8.15,-19.61 8.15,-19.61 C8.15,-19.72 8.12,-19.81 8.05,-19.88 C7.97,-19.95 7.88,-19.99 7.78,-19.99 C7.78,-19.99 -14.11,-19.99 -14.11,-19.99 C-14.22,-19.99 -14.31,-19.95 -14.38,-19.88 C-14.45,-19.81 -14.49,-19.72 -14.49,-19.61 C-14.49,-19.61 -14.49,-17.77 -14.49,-17.77c M-14.11 23.29 C-15.12,23.29 -15.99,22.93 -16.71,22.21 C-17.43,21.49 -17.79,20.62 -17.79,19.61 C-17.79,19.61 -17.79,-19.61 -17.79,-19.61 C-17.79,-20.62 -17.43,-21.49 -16.71,-22.21 C-15.99,-22.93 -15.12,-23.29 -14.11,-23.29 C-14.11,-23.29 7.78,-23.29 7.78,-23.29 C8.79,-23.29 9.65,-22.93 10.38,-22.21 C11.1,-21.49 11.46,-20.62 11.46,-19.61 C11.46,-19.61 11.46,-13.32 11.46,-13.32 C11.46,-12.86 11.3,-12.46 10.97,-12.13 C10.65,-11.81 10.25,-11.65 9.78,-11.65 C9.31,-11.65 8.91,-11.81 8.59,-12.13 C8.3,-12.46 8.15,-12.86 8.15,-13.32 C8.15,-13.32 8.15,-14.46 8.15,-14.46 C8.15,-14.46 -14.49,-14.46 -14.49,-14.46 C-14.49,-14.46 -14.49,14.46 -14.49,14.46 C-14.49,14.46 8.15,14.46 8.15,14.46 C8.15,14.46 8.15,13.33 8.15,13.33 C8.15,12.86 8.3,12.46 8.59,12.13 C8.91,11.81 9.31,11.65 9.78,11.65 C10.25,11.65 10.65,11.81 10.97,12.13 C11.3,12.46 11.46,12.86 11.46,13.33 C11.46,13.33 11.46,19.61 11.46,19.61 C11.46,20.62 11.1,21.49 10.38,22.21 C9.65,22.93 8.79,23.29 7.78,23.29 C7.78,23.29 -14.11,23.29 -14.11,23.29c "/></group><group android:name="_R_G_L_1_G" android:translateX="400" android:translateY="400"><path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#003352" android:fillAlpha="1" android:fillType="nonZero" android:trimPathStart="0" android:trimPathEnd="0" android:trimPathOffset="0" android:pathData=" M52 61 C52,61 179,-67 179,-67 "/><path android:name="_R_G_L_1_G_D_1_P_0" android:strokeColor="#ff8986" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="34" android:strokeAlpha="1" android:trimPathStart="0" android:trimPathEnd="0" android:trimPathOffset="0" android:pathData=" M52 61 C52,61 179,-67 179,-67 "/></group><group android:name="_R_G_L_0_G" android:translateX="400" android:translateY="400"><path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#003352" android:fillAlpha="1" android:fillType="nonZero" android:trimPathStart="0" android:trimPathEnd="0" android:trimPathOffset="0" android:pathData=" M52 -66 C52,-66 177,60 177,60 "/><path android:name="_R_G_L_0_G_D_1_P_0" android:strokeColor="#ff8986" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="35" android:strokeAlpha="1" android:trimPathStart="0" android:trimPathEnd="0" android:trimPathOffset="0" android:pathData=" M52 -66 C52,-66 177,60 177,60 "/></group></group><group android:name="time_group"/></vector></aapt:attr><target android:name="_R_G_L_1_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="trimPathEnd" android:duration="100" android:startOffset="0" android:valueFrom="0" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="trimPathEnd" android:duration="267" android:startOffset="100" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_1_G_D_1_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="trimPathEnd" android:duration="100" android:startOffset="0" android:valueFrom="0" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="trimPathEnd" android:duration="267" android:startOffset="100" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="trimPathEnd" android:duration="267" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G_D_1_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="trimPathEnd" android:duration="267" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="time_group"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="translateX" android:duration="10017" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/></set></aapt:attr></target></animated-vector>
\ No newline at end of file
diff --git a/wear/compose/compose-material3/src/main/res/drawable/open_on_phone_animation.xml b/wear/compose/compose-material3/src/main/res/drawable/open_on_phone_animation.xml
new file mode 100644
index 0000000..26c7f42
--- /dev/null
+++ b/wear/compose/compose-material3/src/main/res/drawable/open_on_phone_animation.xml
@@ -0,0 +1,17 @@
+<!--
+ 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.
+ -->
+
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"><aapt:attr name="android:drawable"><vector android:height="800dp" android:width="800dp" android:viewportHeight="800" android:viewportWidth="800"><group android:name="_R_G"><group android:name="_R_G_L_2_G" android:translateX="372" android:translateY="396" android:scaleX="9.86" android:scaleY="9.86"><path android:name="_R_G_L_2_G_D_0_P_0" android:fillColor="#d3e3fd" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-7.44 23.29 C-8.45,23.29 -9.32,22.93 -10.04,22.21 C-10.76,21.49 -11.13,20.62 -11.13,19.61 C-11.13,19.61 -11.13,8.18 -11.13,8.18 C-11.13,7.71 -10.96,7.31 -10.64,6.99 C-10.31,6.66 -9.91,6.5 -9.45,6.5 C-8.98,6.5 -8.6,6.66 -8.31,6.99 C-7.98,7.31 -7.82,7.71 -7.82,8.18 C-7.82,8.18 -7.82,14.46 -7.82,14.46 C-7.82,14.46 14.82,14.46 14.82,14.46 C14.82,14.46 14.82,-14.46 14.82,-14.46 C14.82,-14.46 -7.82,-14.46 -7.82,-14.46 C-7.82,-14.46 -7.82,-8.18 -7.82,-8.18 C-7.82,-7.71 -7.98,-7.31 -8.31,-6.99 C-8.6,-6.66 -8.98,-6.5 -9.45,-6.5 C-9.91,-6.5 -10.31,-6.66 -10.64,-6.99 C-10.96,-7.31 -11.13,-7.71 -11.13,-8.18 C-11.13,-8.18 -11.13,-19.61 -11.13,-19.61 C-11.13,-20.62 -10.76,-21.49 -10.04,-22.21 C-9.32,-22.93 -8.45,-23.29 -7.44,-23.29 C-7.44,-23.29 14.44,-23.29 14.44,-23.29 C15.45,-23.29 16.32,-22.93 17.04,-22.21 C17.76,-21.49 18.12,-20.62 18.12,-19.61 C18.12,-19.61 18.12,19.61 18.12,19.61 C18.12,20.62 17.76,21.49 17.04,22.21 C16.32,22.93 15.45,23.29 14.44,23.29 C14.44,23.29 -7.44,23.29 -7.44,23.29c M-7.82 17.77 C-7.82,17.77 -7.82,19.61 -7.82,19.61 C-7.82,19.72 -7.78,19.81 -7.71,19.88 C-7.64,19.95 -7.55,19.99 -7.44,19.99 C-7.44,19.99 14.44,19.99 14.44,19.99 C14.55,19.99 14.64,19.95 14.71,19.88 C14.78,19.81 14.82,19.72 14.82,19.61 C14.82,19.61 14.82,17.77 14.82,17.77 C14.82,17.77 -7.82,17.77 -7.82,17.77c M-7.82 -17.77 C-7.82,-17.77 14.82,-17.77 14.82,-17.77 C14.82,-17.77 14.82,-19.61 14.82,-19.61 C14.82,-19.72 14.78,-19.81 14.71,-19.88 C14.64,-19.95 14.55,-19.99 14.44,-19.99 C14.44,-19.99 -7.44,-19.99 -7.44,-19.99 C-7.55,-19.99 -7.64,-19.95 -7.71,-19.88 C-7.78,-19.81 -7.82,-19.72 -7.82,-19.61 C-7.82,-19.61 -7.82,-17.77 -7.82,-17.77c "/></group><group android:name="_R_G_L_1_G" android:translateX="370" android:translateY="428" android:scaleY="0"><path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#003352" android:fillAlpha="1" android:fillType="nonZero" android:trimPathStart="0" android:trimPathEnd="0" android:trimPathOffset="0" android:pathData=" M-189 -33 C-189,-33 20,-33 20,-33 "/><path android:name="_R_G_L_1_G_D_1_P_0" android:strokeColor="#d3e3fd" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="30" android:strokeAlpha="1" android:trimPathStart="0" android:trimPathEnd="0" android:trimPathOffset="0" android:pathData=" M-189 -33 C-189,-33 20,-33 20,-33 "/></group><group android:name="_R_G_L_0_G_T_1" android:translateX="180" android:translateY="395" android:scaleY="0"><group android:name="_R_G_L_0_G" android:translateX="-20" android:translateY="33"><path android:name="_R_G_L_0_G_D_0_P_0" android:strokeColor="#d3e3fd" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="32" android:strokeAlpha="1" android:trimPathStart="0.49" android:trimPathEnd="0.51" android:trimPathOffset="0" android:pathData=" M-36 -90 C-36,-90 21,-33 21,-33 C21,-33 -36,24 -36,24 "/></group></group></group><group android:name="time_group"/></vector></aapt:attr><target android:name="_R_G_L_1_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="trimPathEnd" android:duration="67" android:startOffset="0" android:valueFrom="0" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.5,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="trimPathEnd" android:duration="217" android:startOffset="67" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.5,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_1_G_D_1_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="trimPathEnd" android:duration="67" android:startOffset="0" android:valueFrom="0" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.5,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="trimPathEnd" android:duration="217" android:startOffset="67" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.5,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_1_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="translateXY" android:duration="67" android:startOffset="0" android:propertyXName="translateX" android:propertyYName="translateY" android:pathData="M 370,428C 370,428 370,428 370,428"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.5,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="translateXY" android:duration="217" android:startOffset="67" android:propertyXName="translateX" android:propertyYName="translateY" android:pathData="M 370,428C 370,428 412,428 412,428"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.5,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="translateXY" android:duration="200" android:startOffset="283" android:propertyXName="translateX" android:propertyYName="translateY" android:pathData="M 412,428C 412,428 400,428 400,428"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.44,0 0.55,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_1_G"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleY" android:duration="0" android:startOffset="67" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/></set></aapt:attr></target><target android:name="_R_G_L_0_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="trimPathStart" android:duration="133" android:startOffset="0" android:valueFrom="0.49" android:valueTo="0.49" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.2,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="trimPathStart" android:duration="100" android:startOffset="133" android:valueFrom="0.49" android:valueTo="0" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.2,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G_D_0_P_0"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="trimPathEnd" android:duration="133" android:startOffset="0" android:valueFrom="0.51" android:valueTo="0.51" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.2,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="trimPathEnd" android:duration="100" android:startOffset="133" android:valueFrom="0.51" android:valueTo="1" android:valueType="floatType"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.2,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G_T_1"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="translateXY" android:duration="67" android:startOffset="0" android:propertyXName="translateX" android:propertyYName="translateY" android:pathData="M 180,395C 180,395 180,395 180,395"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.5,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="translateXY" android:duration="217" android:startOffset="67" android:propertyXName="translateX" android:propertyYName="translateY" android:pathData="M 180,395C 180,395 432,395 432,395"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.5,1 1.0,1.0"/></aapt:attr></objectAnimator><objectAnimator android:propertyName="translateXY" android:duration="200" android:startOffset="283" android:propertyXName="translateX" android:propertyYName="translateY" android:pathData="M 432,395C 432,395 420,395 420,395"><aapt:attr name="android:interpolator"><pathInterpolator android:pathData="M 0.0,0.0 c0.44,0 0.55,1 1.0,1.0"/></aapt:attr></objectAnimator></set></aapt:attr></target><target android:name="_R_G_L_0_G_T_1"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="scaleY" android:duration="0" android:startOffset="133" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/></set></aapt:attr></target><target android:name="time_group"><aapt:attr name="android:animation"><set android:ordering="together"><objectAnimator android:propertyName="translateX" android:duration="10017" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/></set></aapt:attr></target></animated-vector>
\ No newline at end of file
diff --git a/wear/compose/compose-material3/src/main/res/values-af/strings.xml b/wear/compose/compose-material3/src/main/res/values-af/strings.xml
index d19c450..4951307 100644
--- a/wear/compose/compose-material3/src/main/res/values-af/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-af/strings.xml
@@ -33,5 +33,13 @@
<item quantity="one">%d sekonde</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Tydperk"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Bevestig"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-am/strings.xml b/wear/compose/compose-material3/src/main/res/values-am/strings.xml
index ee40287..da5ab9c 100644
--- a/wear/compose/compose-material3/src/main/res/values-am/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-am/strings.xml
@@ -33,5 +33,13 @@
<item quantity="other">%d ሰከንዶች</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"ክፍለ ጊዜ"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"አረጋግጥ"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-ar/strings.xml b/wear/compose/compose-material3/src/main/res/values-ar/strings.xml
index 6f65d6e..450227f 100644
--- a/wear/compose/compose-material3/src/main/res/values-ar/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-ar/strings.xml
@@ -45,5 +45,13 @@
<item quantity="one">ثانية واحدة</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"فترة"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"تأكيد"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-as/strings.xml b/wear/compose/compose-material3/src/main/res/values-as/strings.xml
index 867bba0..178eaa4 100644
--- a/wear/compose/compose-material3/src/main/res/values-as/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-as/strings.xml
@@ -33,5 +33,13 @@
<item quantity="other">%d ছেকেণ্ড</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"পিৰিয়ড"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"নিশ্চিত কৰক"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-az/strings.xml b/wear/compose/compose-material3/src/main/res/values-az/strings.xml
index 43fc687..de43d4f 100644
--- a/wear/compose/compose-material3/src/main/res/values-az/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-az/strings.xml
@@ -33,5 +33,13 @@
<item quantity="one">%d saniyə</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Müddət"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Təsdiq edin"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-b+sr+Latn/strings.xml b/wear/compose/compose-material3/src/main/res/values-b+sr+Latn/strings.xml
index 7130d77..eca919a 100644
--- a/wear/compose/compose-material3/src/main/res/values-b+sr+Latn/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-b+sr+Latn/strings.xml
@@ -36,5 +36,13 @@
<item quantity="other">%d sekundi</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Period"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Potvrdi"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-be/strings.xml b/wear/compose/compose-material3/src/main/res/values-be/strings.xml
index 1425da3..dc5e832 100644
--- a/wear/compose/compose-material3/src/main/res/values-be/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-be/strings.xml
@@ -39,5 +39,13 @@
<item quantity="other">%d секунды</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Перыяд"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Пацвердзіць"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-bg/strings.xml b/wear/compose/compose-material3/src/main/res/values-bg/strings.xml
index a2b7c61..2866f08 100644
--- a/wear/compose/compose-material3/src/main/res/values-bg/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-bg/strings.xml
@@ -33,5 +33,9 @@
<item quantity="one">%d секунда</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Точка"</string>
+ <string name="wear_m3c_date_picker_day" msgid="8932770593644830235">"Ден"</string>
+ <string name="wear_m3c_date_picker_month" msgid="5962969526377479136">"Месец"</string>
+ <string name="wear_m3c_date_picker_year" msgid="4697690064312147449">"Година"</string>
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Потвърждаване"</string>
+ <string name="wear_m3c_picker_next_button_content_description" msgid="3346011303652897029">"Напред"</string>
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-bn/strings.xml b/wear/compose/compose-material3/src/main/res/values-bn/strings.xml
index b68d6d9..34a09f1 100644
--- a/wear/compose/compose-material3/src/main/res/values-bn/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-bn/strings.xml
@@ -33,5 +33,13 @@
<item quantity="other">%d সেকেন্ড</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"সময়সীমা"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"কনফার্ম করুন"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-bs/strings.xml b/wear/compose/compose-material3/src/main/res/values-bs/strings.xml
index 0654f2f..9e53fc8 100644
--- a/wear/compose/compose-material3/src/main/res/values-bs/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-bs/strings.xml
@@ -36,5 +36,13 @@
<item quantity="other">%d sekundi</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Period"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Potvrđivanje"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-ca/strings.xml b/wear/compose/compose-material3/src/main/res/values-ca/strings.xml
index cd62e3f..0c19945 100644
--- a/wear/compose/compose-material3/src/main/res/values-ca/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-ca/strings.xml
@@ -36,5 +36,9 @@
<item quantity="one">%d segon</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Període"</string>
+ <string name="wear_m3c_date_picker_day" msgid="8932770593644830235">"Dia"</string>
+ <string name="wear_m3c_date_picker_month" msgid="5962969526377479136">"Mes"</string>
+ <string name="wear_m3c_date_picker_year" msgid="4697690064312147449">"Any"</string>
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Confirma"</string>
+ <string name="wear_m3c_picker_next_button_content_description" msgid="3346011303652897029">"Següent"</string>
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-cs/strings.xml b/wear/compose/compose-material3/src/main/res/values-cs/strings.xml
index e8dfcdc..98dc3ea 100644
--- a/wear/compose/compose-material3/src/main/res/values-cs/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-cs/strings.xml
@@ -39,5 +39,13 @@
<item quantity="one">%d sekunda</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Období"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Potvrdit"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-da/strings.xml b/wear/compose/compose-material3/src/main/res/values-da/strings.xml
index 340890b..d1bd436 100644
--- a/wear/compose/compose-material3/src/main/res/values-da/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-da/strings.xml
@@ -33,5 +33,13 @@
<item quantity="other">%d sekunder</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Format"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Bekræft"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-de/strings.xml b/wear/compose/compose-material3/src/main/res/values-de/strings.xml
index fd7b287..9fdd438 100644
--- a/wear/compose/compose-material3/src/main/res/values-de/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-de/strings.xml
@@ -33,5 +33,13 @@
<item quantity="one">%d Sekunde</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Zeitraum"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Bestätigen"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-el/strings.xml b/wear/compose/compose-material3/src/main/res/values-el/strings.xml
index b105e52..20374c9 100644
--- a/wear/compose/compose-material3/src/main/res/values-el/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-el/strings.xml
@@ -33,5 +33,13 @@
<item quantity="one">%d δευτερόλεπτο</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Περίοδος"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Επιβεβαίωση"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-en-rAU/strings.xml b/wear/compose/compose-material3/src/main/res/values-en-rAU/strings.xml
index b52363a..58982b2 100644
--- a/wear/compose/compose-material3/src/main/res/values-en-rAU/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-en-rAU/strings.xml
@@ -33,5 +33,13 @@
<item quantity="one">%d second</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Period"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Confirm"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-en-rCA/strings.xml b/wear/compose/compose-material3/src/main/res/values-en-rCA/strings.xml
index 37d75e2..fb5c42c 100644
--- a/wear/compose/compose-material3/src/main/res/values-en-rCA/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-en-rCA/strings.xml
@@ -33,5 +33,9 @@
<item quantity="one">%d Second</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Period"</string>
+ <string name="wear_m3c_date_picker_day" msgid="8932770593644830235">"Day"</string>
+ <string name="wear_m3c_date_picker_month" msgid="5962969526377479136">"Month"</string>
+ <string name="wear_m3c_date_picker_year" msgid="4697690064312147449">"Year"</string>
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Confirm"</string>
+ <string name="wear_m3c_picker_next_button_content_description" msgid="3346011303652897029">"Next"</string>
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-en-rGB/strings.xml b/wear/compose/compose-material3/src/main/res/values-en-rGB/strings.xml
index b52363a..58982b2 100644
--- a/wear/compose/compose-material3/src/main/res/values-en-rGB/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-en-rGB/strings.xml
@@ -33,5 +33,13 @@
<item quantity="one">%d second</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Period"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Confirm"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-en-rIN/strings.xml b/wear/compose/compose-material3/src/main/res/values-en-rIN/strings.xml
index b52363a..58982b2 100644
--- a/wear/compose/compose-material3/src/main/res/values-en-rIN/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-en-rIN/strings.xml
@@ -33,5 +33,13 @@
<item quantity="one">%d second</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Period"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Confirm"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-en-rXC/strings.xml b/wear/compose/compose-material3/src/main/res/values-en-rXC/strings.xml
index ba2dc8b..0bff2a4 100644
--- a/wear/compose/compose-material3/src/main/res/values-en-rXC/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-en-rXC/strings.xml
@@ -33,5 +33,9 @@
<item quantity="one">%d Second</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Period"</string>
+ <string name="wear_m3c_date_picker_day" msgid="8932770593644830235">"Day"</string>
+ <string name="wear_m3c_date_picker_month" msgid="5962969526377479136">"Month"</string>
+ <string name="wear_m3c_date_picker_year" msgid="4697690064312147449">"Year"</string>
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Confirm"</string>
+ <string name="wear_m3c_picker_next_button_content_description" msgid="3346011303652897029">"Next"</string>
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-es-rUS/strings.xml b/wear/compose/compose-material3/src/main/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..5dac2b5
--- /dev/null
+++ b/wear/compose/compose-material3/src/main/res/values-es-rUS/strings.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wear_m3c_time_picker_hour" msgid="5670838450714030035">"Hora"</string>
+ <string name="wear_m3c_time_picker_minute" msgid="2847700380677127030">"Minuto"</string>
+ <string name="wear_m3c_time_picker_second" msgid="5551916170669814925">"Segundo"</string>
+ <plurals name="wear_m3c_time_picker_hours_content_description" formatted="false" msgid="7688673698789346225">
+ <item quantity="many">%d de horas</item>
+ <item quantity="other">%d horas</item>
+ <item quantity="one">%d hora</item>
+ </plurals>
+ <plurals name="wear_m3c_time_picker_minutes_content_description" formatted="false" msgid="8268405448590438607">
+ <item quantity="many">%d de minutos</item>
+ <item quantity="other">%d minutos</item>
+ <item quantity="one">%d minuto</item>
+ </plurals>
+ <plurals name="wear_m3c_time_picker_seconds_content_description" formatted="false" msgid="1073969431850983434">
+ <item quantity="many">%d de segundos</item>
+ <item quantity="other">%d segundos</item>
+ <item quantity="one">%d segundo</item>
+ </plurals>
+ <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Período"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
+ <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Confirmar"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
+</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-es/strings.xml b/wear/compose/compose-material3/src/main/res/values-es/strings.xml
new file mode 100644
index 0000000..7ed6eed
--- /dev/null
+++ b/wear/compose/compose-material3/src/main/res/values-es/strings.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="wear_m3c_time_picker_hour" msgid="5670838450714030035">"Hora"</string>
+ <string name="wear_m3c_time_picker_minute" msgid="2847700380677127030">"Minuto"</string>
+ <string name="wear_m3c_time_picker_second" msgid="5551916170669814925">"Segundo"</string>
+ <plurals name="wear_m3c_time_picker_hours_content_description" formatted="false" msgid="7688673698789346225">
+ <item quantity="many">%d horas</item>
+ <item quantity="other">%d horas</item>
+ <item quantity="one">%d hora</item>
+ </plurals>
+ <plurals name="wear_m3c_time_picker_minutes_content_description" formatted="false" msgid="8268405448590438607">
+ <item quantity="many">%d minutos</item>
+ <item quantity="other">%d minutos</item>
+ <item quantity="one">%d minuto</item>
+ </plurals>
+ <plurals name="wear_m3c_time_picker_seconds_content_description" formatted="false" msgid="1073969431850983434">
+ <item quantity="many">%d segundos</item>
+ <item quantity="other">%d segundos</item>
+ <item quantity="one">%d segundo</item>
+ </plurals>
+ <string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Periodo"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
+ <string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Confirmar"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
+</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-et/strings.xml b/wear/compose/compose-material3/src/main/res/values-et/strings.xml
index e2ae406..898d4f1 100644
--- a/wear/compose/compose-material3/src/main/res/values-et/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-et/strings.xml
@@ -33,5 +33,13 @@
<item quantity="one">%d sekund</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Periood"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Kinnita"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-eu/strings.xml b/wear/compose/compose-material3/src/main/res/values-eu/strings.xml
index 98f7e97..202e7ee 100644
--- a/wear/compose/compose-material3/src/main/res/values-eu/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-eu/strings.xml
@@ -33,5 +33,13 @@
<item quantity="one">%d segundo</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Epea"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Berretsi"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-fa/strings.xml b/wear/compose/compose-material3/src/main/res/values-fa/strings.xml
index e51ff20..8f9c137 100644
--- a/wear/compose/compose-material3/src/main/res/values-fa/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-fa/strings.xml
@@ -33,5 +33,13 @@
<item quantity="other">%d ثانیه</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"مدت زمان"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"تأیید کردن"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-fi/strings.xml b/wear/compose/compose-material3/src/main/res/values-fi/strings.xml
index 7a0c590..0f0b50b 100644
--- a/wear/compose/compose-material3/src/main/res/values-fi/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-fi/strings.xml
@@ -33,5 +33,13 @@
<item quantity="one">%d sekunti</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Jakso"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Vahvista"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-fr-rCA/strings.xml b/wear/compose/compose-material3/src/main/res/values-fr-rCA/strings.xml
index e1e9794..cdc3ad5 100644
--- a/wear/compose/compose-material3/src/main/res/values-fr-rCA/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-fr-rCA/strings.xml
@@ -36,5 +36,13 @@
<item quantity="other">%d secondes</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Période"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Confirmer"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-fr/strings.xml b/wear/compose/compose-material3/src/main/res/values-fr/strings.xml
index 97370e0..bdb1f1b 100644
--- a/wear/compose/compose-material3/src/main/res/values-fr/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-fr/strings.xml
@@ -36,5 +36,13 @@
<item quantity="other">%d secondes</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Période"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Confirmer"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-gl/strings.xml b/wear/compose/compose-material3/src/main/res/values-gl/strings.xml
index 1f5b0d0..ad5ca33 100644
--- a/wear/compose/compose-material3/src/main/res/values-gl/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-gl/strings.xml
@@ -33,5 +33,13 @@
<item quantity="one">%d segundo</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Período"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Confirmar"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-gu/strings.xml b/wear/compose/compose-material3/src/main/res/values-gu/strings.xml
index cd43c67..b01e9be 100644
--- a/wear/compose/compose-material3/src/main/res/values-gu/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-gu/strings.xml
@@ -33,5 +33,13 @@
<item quantity="other">%d સેકન્ડ</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"અવધિ"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"કન્ફર્મ કરો"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-hi/strings.xml b/wear/compose/compose-material3/src/main/res/values-hi/strings.xml
index 0d50a66..c861b3af 100644
--- a/wear/compose/compose-material3/src/main/res/values-hi/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-hi/strings.xml
@@ -33,5 +33,9 @@
<item quantity="other">%d सेकंड</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"समयअवधि"</string>
+ <string name="wear_m3c_date_picker_day" msgid="8932770593644830235">"दिन"</string>
+ <string name="wear_m3c_date_picker_month" msgid="5962969526377479136">"महीना"</string>
+ <string name="wear_m3c_date_picker_year" msgid="4697690064312147449">"साल"</string>
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"पुष्टि करें"</string>
+ <string name="wear_m3c_picker_next_button_content_description" msgid="3346011303652897029">"अगला"</string>
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-hr/strings.xml b/wear/compose/compose-material3/src/main/res/values-hr/strings.xml
index 71b0d59..af212a5 100644
--- a/wear/compose/compose-material3/src/main/res/values-hr/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-hr/strings.xml
@@ -36,5 +36,13 @@
<item quantity="other">%d sekundi</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Razdoblje"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Potvrdi"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-hu/strings.xml b/wear/compose/compose-material3/src/main/res/values-hu/strings.xml
index ead4509..2cce16e 100644
--- a/wear/compose/compose-material3/src/main/res/values-hu/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-hu/strings.xml
@@ -33,5 +33,13 @@
<item quantity="one">%d másodperc</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Időszak"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Megerősítés"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-hy/strings.xml b/wear/compose/compose-material3/src/main/res/values-hy/strings.xml
index bfff0c2..cda99db 100644
--- a/wear/compose/compose-material3/src/main/res/values-hy/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-hy/strings.xml
@@ -33,5 +33,13 @@
<item quantity="other">%d վայրկյան</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Ժամանակահատված"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Հաստատել"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-in/strings.xml b/wear/compose/compose-material3/src/main/res/values-in/strings.xml
index aba3a73..66a973e 100644
--- a/wear/compose/compose-material3/src/main/res/values-in/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-in/strings.xml
@@ -33,5 +33,13 @@
<item quantity="one">%d Detik</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Jangka waktu"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Konfirmasi"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-is/strings.xml b/wear/compose/compose-material3/src/main/res/values-is/strings.xml
index 1065161..8983c3f 100644
--- a/wear/compose/compose-material3/src/main/res/values-is/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-is/strings.xml
@@ -33,5 +33,13 @@
<item quantity="other">%d sekúndur</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Punktur"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Staðfesta"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-it/strings.xml b/wear/compose/compose-material3/src/main/res/values-it/strings.xml
index 33d9d41..a9566b7 100644
--- a/wear/compose/compose-material3/src/main/res/values-it/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-it/strings.xml
@@ -36,5 +36,13 @@
<item quantity="one">%d secondo</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Periodo"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Conferma"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-iw/strings.xml b/wear/compose/compose-material3/src/main/res/values-iw/strings.xml
index 320aeef..91f1e27 100644
--- a/wear/compose/compose-material3/src/main/res/values-iw/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-iw/strings.xml
@@ -36,5 +36,13 @@
<item quantity="other">%d שניות</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"תקופת זמן"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"אישור"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-ja/strings.xml b/wear/compose/compose-material3/src/main/res/values-ja/strings.xml
index 2b82492..65e9cc7 100644
--- a/wear/compose/compose-material3/src/main/res/values-ja/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-ja/strings.xml
@@ -33,5 +33,9 @@
<item quantity="one">%d 秒</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"期間"</string>
+ <string name="wear_m3c_date_picker_day" msgid="8932770593644830235">"日"</string>
+ <string name="wear_m3c_date_picker_month" msgid="5962969526377479136">"月"</string>
+ <string name="wear_m3c_date_picker_year" msgid="4697690064312147449">"年"</string>
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"確認"</string>
+ <string name="wear_m3c_picker_next_button_content_description" msgid="3346011303652897029">"次へ"</string>
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-ka/strings.xml b/wear/compose/compose-material3/src/main/res/values-ka/strings.xml
index f1930fd..4f4734e 100644
--- a/wear/compose/compose-material3/src/main/res/values-ka/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-ka/strings.xml
@@ -33,5 +33,9 @@
<item quantity="one">%d წამი</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"პერიოდი"</string>
+ <string name="wear_m3c_date_picker_day" msgid="8932770593644830235">"დღე"</string>
+ <string name="wear_m3c_date_picker_month" msgid="5962969526377479136">"თვე"</string>
+ <string name="wear_m3c_date_picker_year" msgid="4697690064312147449">"წელი"</string>
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"დადასტურება"</string>
+ <string name="wear_m3c_picker_next_button_content_description" msgid="3346011303652897029">"შემდეგი"</string>
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-kk/strings.xml b/wear/compose/compose-material3/src/main/res/values-kk/strings.xml
index 0b79463..e0d8b3d 100644
--- a/wear/compose/compose-material3/src/main/res/values-kk/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-kk/strings.xml
@@ -33,5 +33,13 @@
<item quantity="one">%d секунд</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Кезең"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Растау"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-km/strings.xml b/wear/compose/compose-material3/src/main/res/values-km/strings.xml
index b302b3b..410a6f2 100644
--- a/wear/compose/compose-material3/src/main/res/values-km/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-km/strings.xml
@@ -33,5 +33,13 @@
<item quantity="one">%d វិនាទី</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"រយៈពេល"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"បញ្ជាក់"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-kn/strings.xml b/wear/compose/compose-material3/src/main/res/values-kn/strings.xml
index fe7c9cf..6ae6994 100644
--- a/wear/compose/compose-material3/src/main/res/values-kn/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-kn/strings.xml
@@ -33,5 +33,13 @@
<item quantity="other">%d ಸೆಕೆಂಡ್ಗಳು</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"ಅವಧಿ"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"ದೃಢೀಕರಿಸಿ"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-ko/strings.xml b/wear/compose/compose-material3/src/main/res/values-ko/strings.xml
index 231ff8f5..45f8446 100644
--- a/wear/compose/compose-material3/src/main/res/values-ko/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-ko/strings.xml
@@ -33,5 +33,13 @@
<item quantity="one">%d초</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"기간"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"확인"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-ky/strings.xml b/wear/compose/compose-material3/src/main/res/values-ky/strings.xml
index efbade7..692726e 100644
--- a/wear/compose/compose-material3/src/main/res/values-ky/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-ky/strings.xml
@@ -33,5 +33,13 @@
<item quantity="one">%d секунд</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Чекит"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Ырастоо"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-lo/strings.xml b/wear/compose/compose-material3/src/main/res/values-lo/strings.xml
index 8e4a64e..59d2d94 100644
--- a/wear/compose/compose-material3/src/main/res/values-lo/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-lo/strings.xml
@@ -33,5 +33,13 @@
<item quantity="one">%d ວິນາທີ</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"ໄລຍະເວລາ"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"ຢືນຢັນ"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-lt/strings.xml b/wear/compose/compose-material3/src/main/res/values-lt/strings.xml
index ad7f022..615054e 100644
--- a/wear/compose/compose-material3/src/main/res/values-lt/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-lt/strings.xml
@@ -39,5 +39,13 @@
<item quantity="other">%d sekundžių</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Laikotarpis"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Patvirtinti"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-lv/strings.xml b/wear/compose/compose-material3/src/main/res/values-lv/strings.xml
index 1057111..08f2d2c 100644
--- a/wear/compose/compose-material3/src/main/res/values-lv/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-lv/strings.xml
@@ -36,5 +36,13 @@
<item quantity="other">%d sekundes</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Periods"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Apstiprināt"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-mk/strings.xml b/wear/compose/compose-material3/src/main/res/values-mk/strings.xml
index 3e57b97..a093425 100644
--- a/wear/compose/compose-material3/src/main/res/values-mk/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-mk/strings.xml
@@ -33,5 +33,13 @@
<item quantity="other">%d секунди</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Период"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Потврди"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-ml/strings.xml b/wear/compose/compose-material3/src/main/res/values-ml/strings.xml
index ff5642f..36d8b54 100644
--- a/wear/compose/compose-material3/src/main/res/values-ml/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-ml/strings.xml
@@ -33,5 +33,13 @@
<item quantity="one">%d സെക്കൻഡ്</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"കാലയളവ്"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"സ്ഥിരീകരിക്കുക"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-mn/strings.xml b/wear/compose/compose-material3/src/main/res/values-mn/strings.xml
index 7db8e1c..629e16a 100644
--- a/wear/compose/compose-material3/src/main/res/values-mn/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-mn/strings.xml
@@ -33,5 +33,13 @@
<item quantity="one">%d секунд</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Хугацаа"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Баталгаажуулах"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-mr/strings.xml b/wear/compose/compose-material3/src/main/res/values-mr/strings.xml
index 23e9efe..904ad4f 100644
--- a/wear/compose/compose-material3/src/main/res/values-mr/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-mr/strings.xml
@@ -33,5 +33,13 @@
<item quantity="one">%d सेकंद</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"कालावधी"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"कन्फर्म करा"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-ms/strings.xml b/wear/compose/compose-material3/src/main/res/values-ms/strings.xml
index 5869290..df4b8c9 100644
--- a/wear/compose/compose-material3/src/main/res/values-ms/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-ms/strings.xml
@@ -33,5 +33,13 @@
<item quantity="one">%d Saat</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Tempoh"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Sahkan"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-my/strings.xml b/wear/compose/compose-material3/src/main/res/values-my/strings.xml
index f328281..aa85494 100644
--- a/wear/compose/compose-material3/src/main/res/values-my/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-my/strings.xml
@@ -33,5 +33,13 @@
<item quantity="one">%d စက္ကန့်</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"အချိန်ကာလ"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"အတည်ပြုရန်"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-nb/strings.xml b/wear/compose/compose-material3/src/main/res/values-nb/strings.xml
index b4fd2e7..0d9dbd3 100644
--- a/wear/compose/compose-material3/src/main/res/values-nb/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-nb/strings.xml
@@ -33,5 +33,13 @@
<item quantity="one">%d sekund</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Periode"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Bekreft"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-ne/strings.xml b/wear/compose/compose-material3/src/main/res/values-ne/strings.xml
index b234e16..4f5d479 100644
--- a/wear/compose/compose-material3/src/main/res/values-ne/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-ne/strings.xml
@@ -33,5 +33,13 @@
<item quantity="one">%d सेकेन्ड</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"अवधि"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"पुष्टि गर्नुहोस्"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-nl/strings.xml b/wear/compose/compose-material3/src/main/res/values-nl/strings.xml
index 3afe2c9..8561545 100644
--- a/wear/compose/compose-material3/src/main/res/values-nl/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-nl/strings.xml
@@ -33,5 +33,13 @@
<item quantity="one">%d seconde</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Periode"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Bevestigen"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-or/strings.xml b/wear/compose/compose-material3/src/main/res/values-or/strings.xml
index acf7af8..7701b598 100644
--- a/wear/compose/compose-material3/src/main/res/values-or/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-or/strings.xml
@@ -33,5 +33,13 @@
<item quantity="one">%d ସେକେଣ୍ଡ</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"ଅବଧି"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"ସୁନିଶ୍ଚିତ କରନ୍ତୁ"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-pa/strings.xml b/wear/compose/compose-material3/src/main/res/values-pa/strings.xml
index 80dbbc1..1000cde 100644
--- a/wear/compose/compose-material3/src/main/res/values-pa/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-pa/strings.xml
@@ -33,5 +33,13 @@
<item quantity="other">%d ਸਕਿੰਟ</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"ਮਿਆਦ"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"ਤਸਦੀਕ ਕਰੋ"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-pl/strings.xml b/wear/compose/compose-material3/src/main/res/values-pl/strings.xml
index 84a021b..5d56d82 100644
--- a/wear/compose/compose-material3/src/main/res/values-pl/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-pl/strings.xml
@@ -39,5 +39,13 @@
<item quantity="one">%d sekunda</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Kropka"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Potwierdź"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-pt-rBR/strings.xml b/wear/compose/compose-material3/src/main/res/values-pt-rBR/strings.xml
index 2dcdcbf..77c3710 100644
--- a/wear/compose/compose-material3/src/main/res/values-pt-rBR/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-pt-rBR/strings.xml
@@ -36,5 +36,13 @@
<item quantity="other">%d segundos</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Período"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Confirmar"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-pt-rPT/strings.xml b/wear/compose/compose-material3/src/main/res/values-pt-rPT/strings.xml
index 9bbdb89..416cbc0 100644
--- a/wear/compose/compose-material3/src/main/res/values-pt-rPT/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-pt-rPT/strings.xml
@@ -36,5 +36,9 @@
<item quantity="one">%d segundo</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Período"</string>
+ <string name="wear_m3c_date_picker_day" msgid="8932770593644830235">"Dia"</string>
+ <string name="wear_m3c_date_picker_month" msgid="5962969526377479136">"Mês"</string>
+ <string name="wear_m3c_date_picker_year" msgid="4697690064312147449">"Ano"</string>
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Confirmar"</string>
+ <string name="wear_m3c_picker_next_button_content_description" msgid="3346011303652897029">"Seguinte"</string>
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-pt/strings.xml b/wear/compose/compose-material3/src/main/res/values-pt/strings.xml
index 2dcdcbf..77c3710 100644
--- a/wear/compose/compose-material3/src/main/res/values-pt/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-pt/strings.xml
@@ -36,5 +36,13 @@
<item quantity="other">%d segundos</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Período"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Confirmar"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-ro/strings.xml b/wear/compose/compose-material3/src/main/res/values-ro/strings.xml
index a682983b..f9723df 100644
--- a/wear/compose/compose-material3/src/main/res/values-ro/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-ro/strings.xml
@@ -36,5 +36,13 @@
<item quantity="one">%d secundă</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Perioada"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Confirmă"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-ru/strings.xml b/wear/compose/compose-material3/src/main/res/values-ru/strings.xml
index 47fb6f8..e01e3c3 100644
--- a/wear/compose/compose-material3/src/main/res/values-ru/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-ru/strings.xml
@@ -39,5 +39,13 @@
<item quantity="other">%d секунды</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Период"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Подтвердить"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-si/strings.xml b/wear/compose/compose-material3/src/main/res/values-si/strings.xml
index 491d592..6f3d4c6 100644
--- a/wear/compose/compose-material3/src/main/res/values-si/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-si/strings.xml
@@ -33,5 +33,13 @@
<item quantity="other">තත්පර %d</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"කාල පරිච්ඡේදය"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"තහවුරු කරන්න"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-sk/strings.xml b/wear/compose/compose-material3/src/main/res/values-sk/strings.xml
index 7b45b82..8840530 100644
--- a/wear/compose/compose-material3/src/main/res/values-sk/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-sk/strings.xml
@@ -39,5 +39,13 @@
<item quantity="one">%d sekunda</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Obdobie"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Potvrdiť"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-sl/strings.xml b/wear/compose/compose-material3/src/main/res/values-sl/strings.xml
index 164b53e..cc79d76 100644
--- a/wear/compose/compose-material3/src/main/res/values-sl/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-sl/strings.xml
@@ -39,5 +39,9 @@
<item quantity="other">%d sekund</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Pika"</string>
+ <string name="wear_m3c_date_picker_day" msgid="8932770593644830235">"Dan"</string>
+ <string name="wear_m3c_date_picker_month" msgid="5962969526377479136">"Mesec"</string>
+ <string name="wear_m3c_date_picker_year" msgid="4697690064312147449">"Leto"</string>
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Potrdi"</string>
+ <string name="wear_m3c_picker_next_button_content_description" msgid="3346011303652897029">"Naprej"</string>
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-sq/strings.xml b/wear/compose/compose-material3/src/main/res/values-sq/strings.xml
index 149c7bb..411d53e 100644
--- a/wear/compose/compose-material3/src/main/res/values-sq/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-sq/strings.xml
@@ -33,5 +33,13 @@
<item quantity="one">%d sekondë</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Periudha"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Konfirmo"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-sr/strings.xml b/wear/compose/compose-material3/src/main/res/values-sr/strings.xml
index decaa24c..661b6d2 100644
--- a/wear/compose/compose-material3/src/main/res/values-sr/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-sr/strings.xml
@@ -36,5 +36,13 @@
<item quantity="other">%d секунди</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Период"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Потврди"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-sv/strings.xml b/wear/compose/compose-material3/src/main/res/values-sv/strings.xml
index 33c96f7..b395ba1d 100644
--- a/wear/compose/compose-material3/src/main/res/values-sv/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-sv/strings.xml
@@ -33,5 +33,13 @@
<item quantity="one">%d sekund</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Punkt"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Bekräfta"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-sw/strings.xml b/wear/compose/compose-material3/src/main/res/values-sw/strings.xml
index d1cc257..aad1959 100644
--- a/wear/compose/compose-material3/src/main/res/values-sw/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-sw/strings.xml
@@ -33,5 +33,13 @@
<item quantity="one">Sekunde %d</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Kipindi"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Thibitisha"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-ta/strings.xml b/wear/compose/compose-material3/src/main/res/values-ta/strings.xml
index 669314b..aacd3cd 100644
--- a/wear/compose/compose-material3/src/main/res/values-ta/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-ta/strings.xml
@@ -33,5 +33,13 @@
<item quantity="one">%d வினாடி</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"கால இடைவெளி"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"உறுதிசெய்யும்"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-te/strings.xml b/wear/compose/compose-material3/src/main/res/values-te/strings.xml
index 353d9fc..2374063 100644
--- a/wear/compose/compose-material3/src/main/res/values-te/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-te/strings.xml
@@ -33,5 +33,9 @@
<item quantity="one">%d సెకను</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"వ్యవధి"</string>
+ <string name="wear_m3c_date_picker_day" msgid="8932770593644830235">"రోజు"</string>
+ <string name="wear_m3c_date_picker_month" msgid="5962969526377479136">"నెల"</string>
+ <string name="wear_m3c_date_picker_year" msgid="4697690064312147449">"సంవత్సరం"</string>
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"నిర్ధారించండి"</string>
+ <string name="wear_m3c_picker_next_button_content_description" msgid="3346011303652897029">"తర్వాత"</string>
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-th/strings.xml b/wear/compose/compose-material3/src/main/res/values-th/strings.xml
index 3bd9b5c..d1ad793 100644
--- a/wear/compose/compose-material3/src/main/res/values-th/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-th/strings.xml
@@ -33,5 +33,9 @@
<item quantity="one">%d วินาที</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"ระยะเวลา"</string>
+ <string name="wear_m3c_date_picker_day" msgid="8932770593644830235">"วัน"</string>
+ <string name="wear_m3c_date_picker_month" msgid="5962969526377479136">"เดือน"</string>
+ <string name="wear_m3c_date_picker_year" msgid="4697690064312147449">"ปี"</string>
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"ยืนยัน"</string>
+ <string name="wear_m3c_picker_next_button_content_description" msgid="3346011303652897029">"ถัดไป"</string>
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-tl/strings.xml b/wear/compose/compose-material3/src/main/res/values-tl/strings.xml
index 5d1f051..fe20f09 100644
--- a/wear/compose/compose-material3/src/main/res/values-tl/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-tl/strings.xml
@@ -33,5 +33,9 @@
<item quantity="other">%d na Segundo</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Panahon"</string>
+ <string name="wear_m3c_date_picker_day" msgid="8932770593644830235">"Araw"</string>
+ <string name="wear_m3c_date_picker_month" msgid="5962969526377479136">"Buwan"</string>
+ <string name="wear_m3c_date_picker_year" msgid="4697690064312147449">"Taon"</string>
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Kumpirmahin"</string>
+ <string name="wear_m3c_picker_next_button_content_description" msgid="3346011303652897029">"Susunod"</string>
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-tr/strings.xml b/wear/compose/compose-material3/src/main/res/values-tr/strings.xml
index 1a0f669..3ec35be 100644
--- a/wear/compose/compose-material3/src/main/res/values-tr/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-tr/strings.xml
@@ -33,5 +33,13 @@
<item quantity="one">%d Saniye</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Aralık"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Onayla"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-uk/strings.xml b/wear/compose/compose-material3/src/main/res/values-uk/strings.xml
index 40e732c..bb037ea 100644
--- a/wear/compose/compose-material3/src/main/res/values-uk/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-uk/strings.xml
@@ -39,5 +39,13 @@
<item quantity="other">%d секунди</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Період"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Підтвердити"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-ur/strings.xml b/wear/compose/compose-material3/src/main/res/values-ur/strings.xml
index f15dacf..6f902ab 100644
--- a/wear/compose/compose-material3/src/main/res/values-ur/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-ur/strings.xml
@@ -33,5 +33,13 @@
<item quantity="one">%d سیکنڈ</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"وقفہ"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"تصدیق کریں"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-uz/strings.xml b/wear/compose/compose-material3/src/main/res/values-uz/strings.xml
index 1025cad..4b1121e 100644
--- a/wear/compose/compose-material3/src/main/res/values-uz/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-uz/strings.xml
@@ -33,5 +33,9 @@
<item quantity="one">%d soniya</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Oraliq"</string>
+ <string name="wear_m3c_date_picker_day" msgid="8932770593644830235">"Kun"</string>
+ <string name="wear_m3c_date_picker_month" msgid="5962969526377479136">"Oy"</string>
+ <string name="wear_m3c_date_picker_year" msgid="4697690064312147449">"Yil"</string>
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Tasdiqlash"</string>
+ <string name="wear_m3c_picker_next_button_content_description" msgid="3346011303652897029">"Keyingisi"</string>
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-vi/strings.xml b/wear/compose/compose-material3/src/main/res/values-vi/strings.xml
index 6d61d00..3b40f28 100644
--- a/wear/compose/compose-material3/src/main/res/values-vi/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-vi/strings.xml
@@ -33,5 +33,13 @@
<item quantity="one">%d giây</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Khoảng thời gian"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Xác nhận"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-zh-rCN/strings.xml b/wear/compose/compose-material3/src/main/res/values-zh-rCN/strings.xml
index d89158c..14ce25a 100644
--- a/wear/compose/compose-material3/src/main/res/values-zh-rCN/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-zh-rCN/strings.xml
@@ -33,5 +33,9 @@
<item quantity="one">%d 秒</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"时段"</string>
+ <string name="wear_m3c_date_picker_day" msgid="8932770593644830235">"日"</string>
+ <string name="wear_m3c_date_picker_month" msgid="5962969526377479136">"月"</string>
+ <string name="wear_m3c_date_picker_year" msgid="4697690064312147449">"年"</string>
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"确认"</string>
+ <string name="wear_m3c_picker_next_button_content_description" msgid="3346011303652897029">"下一个"</string>
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-zh-rHK/strings.xml b/wear/compose/compose-material3/src/main/res/values-zh-rHK/strings.xml
index 68bc1b0..0d30e02 100644
--- a/wear/compose/compose-material3/src/main/res/values-zh-rHK/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-zh-rHK/strings.xml
@@ -33,5 +33,13 @@
<item quantity="one">%d 秒</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"時段"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"確認"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-zh-rTW/strings.xml b/wear/compose/compose-material3/src/main/res/values-zh-rTW/strings.xml
index 067f1dc..3e569a9 100644
--- a/wear/compose/compose-material3/src/main/res/values-zh-rTW/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-zh-rTW/strings.xml
@@ -33,5 +33,13 @@
<item quantity="one">%d 秒</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"期間"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"確認"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values-zu/strings.xml b/wear/compose/compose-material3/src/main/res/values-zu/strings.xml
index 159343b..43167ed 100644
--- a/wear/compose/compose-material3/src/main/res/values-zu/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values-zu/strings.xml
@@ -33,5 +33,13 @@
<item quantity="other">Imizuzwana engu-%d</item>
</plurals>
<string name="wear_m3c_time_picker_period" msgid="5567285614451063120">"Isikhathi"</string>
+ <!-- no translation found for wear_m3c_date_picker_day (8932770593644830235) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_month (5962969526377479136) -->
+ <skip />
+ <!-- no translation found for wear_m3c_date_picker_year (4697690064312147449) -->
+ <skip />
<string name="wear_m3c_picker_confirm_button_content_description" msgid="8440144719909288057">"Qinisekisa"</string>
+ <!-- no translation found for wear_m3c_picker_next_button_content_description (3346011303652897029) -->
+ <skip />
</resources>
diff --git a/wear/compose/compose-material3/src/main/res/values/strings.xml b/wear/compose/compose-material3/src/main/res/values/strings.xml
index 213c3ad..868ca2b 100644
--- a/wear/compose/compose-material3/src/main/res/values/strings.xml
+++ b/wear/compose/compose-material3/src/main/res/values/strings.xml
@@ -36,4 +36,8 @@
<string description="Lets the user know that this picker is to change the value of the year date unit. Appears on the DatePicker component, on top of the year picker. [CHAR_LIMIT=8]" name="wear_m3c_date_picker_year">Year</string>
<string description="Content description of the confirm button of DatePicker and TimePicker components. It lets the user confirm the date or time selected. [CHAR_LIMIT=NONE]" name="wear_m3c_picker_confirm_button_content_description">Confirm</string>
<string description="Content description of the next button of DatePicker and TimePicker components. It lets the user to move to the next picker. [CHAR_LIMIT=NONE]" name="wear_m3c_picker_next_button_content_description">Next</string>
-</resources>
\ No newline at end of file
+
+ <string description="A message which is used to indicate that an action failed in a FailureConfirmation [CHAR_LIMIT=12]" name="wear_m3c_confirmation_failure_message">Failed</string>
+ <string description="A message which is used to indicate that an action succeeded in a SuccessConfirmation [CHAR_LIMIT=12]" name="wear_m3c_confirmation_success_message">Success</string>
+ <string description="A message which is used to indicate than the user should continue their action on their phone, used in OpenOnPhone component [CHAR_LIMIT=12]" name="wear_m3c_open_on_phone">Open on phone</string>
+</resources>
diff --git a/wear/compose/compose-navigation/build.gradle b/wear/compose/compose-navigation/build.gradle
index 2e021e1..bb35fa2 100644
--- a/wear/compose/compose-navigation/build.gradle
+++ b/wear/compose/compose-navigation/build.gradle
@@ -31,8 +31,8 @@
}
dependencies {
- api("androidx.compose.ui:ui:1.7.0-rc01")
- api("androidx.compose.runtime:runtime:1.7.0-rc01")
+ api("androidx.compose.ui:ui:1.7.0")
+ api("androidx.compose.runtime:runtime:1.7.0")
api("androidx.navigation:navigation-runtime:2.6.0")
api(project(":wear:compose:compose-material"))
api("androidx.activity:activity-compose:1.7.0")
diff --git a/wear/compose/compose-ui-tooling/build.gradle b/wear/compose/compose-ui-tooling/build.gradle
index d49acb1..5dcfeea 100644
--- a/wear/compose/compose-ui-tooling/build.gradle
+++ b/wear/compose/compose-ui-tooling/build.gradle
@@ -32,7 +32,7 @@
dependencies {
api("androidx.annotation:annotation:1.8.1")
- api("androidx.compose.ui:ui-tooling-preview:1.7.0-rc01")
+ api("androidx.compose.ui:ui-tooling-preview:1.7.0")
implementation(libs.kotlinStdlib)
implementation("androidx.wear:wear-tooling-preview:1.0.0")
diff --git a/window/window-core/api/current.txt b/window/window-core/api/current.txt
index aa7ee82..2c8a4d2 100644
--- a/window/window-core/api/current.txt
+++ b/window/window-core/api/current.txt
@@ -8,39 +8,68 @@
package androidx.window.core.layout {
- public final class WindowHeightSizeClass {
- field public static final androidx.window.core.layout.WindowHeightSizeClass COMPACT;
- field public static final androidx.window.core.layout.WindowHeightSizeClass.Companion Companion;
- field public static final androidx.window.core.layout.WindowHeightSizeClass EXPANDED;
- field public static final androidx.window.core.layout.WindowHeightSizeClass MEDIUM;
+ @Deprecated public final class WindowHeightSizeClass {
+ field @Deprecated public static final androidx.window.core.layout.WindowHeightSizeClass COMPACT;
+ field @Deprecated public static final androidx.window.core.layout.WindowHeightSizeClass.Companion Companion;
+ field @Deprecated public static final androidx.window.core.layout.WindowHeightSizeClass EXPANDED;
+ field @Deprecated public static final androidx.window.core.layout.WindowHeightSizeClass MEDIUM;
}
- public static final class WindowHeightSizeClass.Companion {
+ @Deprecated public static final class WindowHeightSizeClass.Companion {
+ method @Deprecated public androidx.window.core.layout.WindowHeightSizeClass getCOMPACT();
+ method @Deprecated public androidx.window.core.layout.WindowHeightSizeClass getEXPANDED();
+ method @Deprecated public androidx.window.core.layout.WindowHeightSizeClass getMEDIUM();
+ property @Deprecated public androidx.window.core.layout.WindowHeightSizeClass COMPACT;
+ property @Deprecated public androidx.window.core.layout.WindowHeightSizeClass EXPANDED;
+ property @Deprecated public androidx.window.core.layout.WindowHeightSizeClass MEDIUM;
}
public final class WindowSizeClass {
- method public static androidx.window.core.layout.WindowSizeClass compute(float dpWidth, float dpHeight);
- method @SuppressCompatibility @androidx.window.core.ExperimentalWindowCoreApi public static androidx.window.core.layout.WindowSizeClass compute(int widthPx, int heightPx, float density);
- method public androidx.window.core.layout.WindowHeightSizeClass getWindowHeightSizeClass();
- method public androidx.window.core.layout.WindowWidthSizeClass getWindowWidthSizeClass();
- property public final androidx.window.core.layout.WindowHeightSizeClass windowHeightSizeClass;
- property public final androidx.window.core.layout.WindowWidthSizeClass windowWidthSizeClass;
+ ctor public WindowSizeClass(float widthDp, float heightDp);
+ ctor public WindowSizeClass(int minWidthDp, int minHeightDp);
+ method @Deprecated public static androidx.window.core.layout.WindowSizeClass compute(float dpWidth, float dpHeight);
+ method public int getMinHeightDp();
+ method public int getMinWidthDp();
+ method @Deprecated public androidx.window.core.layout.WindowHeightSizeClass getWindowHeightSizeClass();
+ method @Deprecated public androidx.window.core.layout.WindowWidthSizeClass getWindowWidthSizeClass();
+ method public boolean isAtLeast(int widthDp, int heightDp);
+ method public boolean isHeightAtLeast(int heightDp);
+ method public boolean isWidthAtLeast(int widthDp);
+ property public final int minHeightDp;
+ property public final int minWidthDp;
+ property @Deprecated public final androidx.window.core.layout.WindowHeightSizeClass windowHeightSizeClass;
+ property @Deprecated public final androidx.window.core.layout.WindowWidthSizeClass windowWidthSizeClass;
+ field public static final java.util.Set<androidx.window.core.layout.WindowSizeClass> BREAKPOINTS_V1;
field public static final androidx.window.core.layout.WindowSizeClass.Companion Companion;
+ field public static final int HEIGHT_DP_EXPANDED_LOWER_BOUND = 900; // 0x384
+ field public static final int HEIGHT_DP_MEDIUM_LOWER_BOUND = 480; // 0x1e0
+ field public static final int WIDTH_DP_EXPANDED_LOWER_BOUND = 840; // 0x348
+ field public static final int WIDTH_DP_MEDIUM_LOWER_BOUND = 600; // 0x258
}
public static final class WindowSizeClass.Companion {
- method public androidx.window.core.layout.WindowSizeClass compute(float dpWidth, float dpHeight);
- method @SuppressCompatibility @androidx.window.core.ExperimentalWindowCoreApi public androidx.window.core.layout.WindowSizeClass compute(int widthPx, int heightPx, float density);
+ method @Deprecated public androidx.window.core.layout.WindowSizeClass compute(float dpWidth, float dpHeight);
}
- public final class WindowWidthSizeClass {
- field public static final androidx.window.core.layout.WindowWidthSizeClass COMPACT;
- field public static final androidx.window.core.layout.WindowWidthSizeClass.Companion Companion;
- field public static final androidx.window.core.layout.WindowWidthSizeClass EXPANDED;
- field public static final androidx.window.core.layout.WindowWidthSizeClass MEDIUM;
+ public final class WindowSizeClassSelectors {
+ method public static androidx.window.core.layout.WindowSizeClass computeWindowSizeClass(java.util.Set<androidx.window.core.layout.WindowSizeClass>, int widthDp, int heightDp);
+ method public static androidx.window.core.layout.WindowSizeClass computeWindowSizeClassPreferHeight(java.util.Set<androidx.window.core.layout.WindowSizeClass>, int widthDp, int heightDp);
}
- public static final class WindowWidthSizeClass.Companion {
+ @Deprecated public final class WindowWidthSizeClass {
+ field @Deprecated public static final androidx.window.core.layout.WindowWidthSizeClass COMPACT;
+ field @Deprecated public static final androidx.window.core.layout.WindowWidthSizeClass.Companion Companion;
+ field @Deprecated public static final androidx.window.core.layout.WindowWidthSizeClass EXPANDED;
+ field @Deprecated public static final androidx.window.core.layout.WindowWidthSizeClass MEDIUM;
+ }
+
+ @Deprecated public static final class WindowWidthSizeClass.Companion {
+ method @Deprecated public androidx.window.core.layout.WindowWidthSizeClass getCOMPACT();
+ method @Deprecated public androidx.window.core.layout.WindowWidthSizeClass getEXPANDED();
+ method @Deprecated public androidx.window.core.layout.WindowWidthSizeClass getMEDIUM();
+ property @Deprecated public androidx.window.core.layout.WindowWidthSizeClass COMPACT;
+ property @Deprecated public androidx.window.core.layout.WindowWidthSizeClass EXPANDED;
+ property @Deprecated public androidx.window.core.layout.WindowWidthSizeClass MEDIUM;
}
}
diff --git a/window/window-core/api/restricted_current.txt b/window/window-core/api/restricted_current.txt
index aa7ee82..2c8a4d2 100644
--- a/window/window-core/api/restricted_current.txt
+++ b/window/window-core/api/restricted_current.txt
@@ -8,39 +8,68 @@
package androidx.window.core.layout {
- public final class WindowHeightSizeClass {
- field public static final androidx.window.core.layout.WindowHeightSizeClass COMPACT;
- field public static final androidx.window.core.layout.WindowHeightSizeClass.Companion Companion;
- field public static final androidx.window.core.layout.WindowHeightSizeClass EXPANDED;
- field public static final androidx.window.core.layout.WindowHeightSizeClass MEDIUM;
+ @Deprecated public final class WindowHeightSizeClass {
+ field @Deprecated public static final androidx.window.core.layout.WindowHeightSizeClass COMPACT;
+ field @Deprecated public static final androidx.window.core.layout.WindowHeightSizeClass.Companion Companion;
+ field @Deprecated public static final androidx.window.core.layout.WindowHeightSizeClass EXPANDED;
+ field @Deprecated public static final androidx.window.core.layout.WindowHeightSizeClass MEDIUM;
}
- public static final class WindowHeightSizeClass.Companion {
+ @Deprecated public static final class WindowHeightSizeClass.Companion {
+ method @Deprecated public androidx.window.core.layout.WindowHeightSizeClass getCOMPACT();
+ method @Deprecated public androidx.window.core.layout.WindowHeightSizeClass getEXPANDED();
+ method @Deprecated public androidx.window.core.layout.WindowHeightSizeClass getMEDIUM();
+ property @Deprecated public androidx.window.core.layout.WindowHeightSizeClass COMPACT;
+ property @Deprecated public androidx.window.core.layout.WindowHeightSizeClass EXPANDED;
+ property @Deprecated public androidx.window.core.layout.WindowHeightSizeClass MEDIUM;
}
public final class WindowSizeClass {
- method public static androidx.window.core.layout.WindowSizeClass compute(float dpWidth, float dpHeight);
- method @SuppressCompatibility @androidx.window.core.ExperimentalWindowCoreApi public static androidx.window.core.layout.WindowSizeClass compute(int widthPx, int heightPx, float density);
- method public androidx.window.core.layout.WindowHeightSizeClass getWindowHeightSizeClass();
- method public androidx.window.core.layout.WindowWidthSizeClass getWindowWidthSizeClass();
- property public final androidx.window.core.layout.WindowHeightSizeClass windowHeightSizeClass;
- property public final androidx.window.core.layout.WindowWidthSizeClass windowWidthSizeClass;
+ ctor public WindowSizeClass(float widthDp, float heightDp);
+ ctor public WindowSizeClass(int minWidthDp, int minHeightDp);
+ method @Deprecated public static androidx.window.core.layout.WindowSizeClass compute(float dpWidth, float dpHeight);
+ method public int getMinHeightDp();
+ method public int getMinWidthDp();
+ method @Deprecated public androidx.window.core.layout.WindowHeightSizeClass getWindowHeightSizeClass();
+ method @Deprecated public androidx.window.core.layout.WindowWidthSizeClass getWindowWidthSizeClass();
+ method public boolean isAtLeast(int widthDp, int heightDp);
+ method public boolean isHeightAtLeast(int heightDp);
+ method public boolean isWidthAtLeast(int widthDp);
+ property public final int minHeightDp;
+ property public final int minWidthDp;
+ property @Deprecated public final androidx.window.core.layout.WindowHeightSizeClass windowHeightSizeClass;
+ property @Deprecated public final androidx.window.core.layout.WindowWidthSizeClass windowWidthSizeClass;
+ field public static final java.util.Set<androidx.window.core.layout.WindowSizeClass> BREAKPOINTS_V1;
field public static final androidx.window.core.layout.WindowSizeClass.Companion Companion;
+ field public static final int HEIGHT_DP_EXPANDED_LOWER_BOUND = 900; // 0x384
+ field public static final int HEIGHT_DP_MEDIUM_LOWER_BOUND = 480; // 0x1e0
+ field public static final int WIDTH_DP_EXPANDED_LOWER_BOUND = 840; // 0x348
+ field public static final int WIDTH_DP_MEDIUM_LOWER_BOUND = 600; // 0x258
}
public static final class WindowSizeClass.Companion {
- method public androidx.window.core.layout.WindowSizeClass compute(float dpWidth, float dpHeight);
- method @SuppressCompatibility @androidx.window.core.ExperimentalWindowCoreApi public androidx.window.core.layout.WindowSizeClass compute(int widthPx, int heightPx, float density);
+ method @Deprecated public androidx.window.core.layout.WindowSizeClass compute(float dpWidth, float dpHeight);
}
- public final class WindowWidthSizeClass {
- field public static final androidx.window.core.layout.WindowWidthSizeClass COMPACT;
- field public static final androidx.window.core.layout.WindowWidthSizeClass.Companion Companion;
- field public static final androidx.window.core.layout.WindowWidthSizeClass EXPANDED;
- field public static final androidx.window.core.layout.WindowWidthSizeClass MEDIUM;
+ public final class WindowSizeClassSelectors {
+ method public static androidx.window.core.layout.WindowSizeClass computeWindowSizeClass(java.util.Set<androidx.window.core.layout.WindowSizeClass>, int widthDp, int heightDp);
+ method public static androidx.window.core.layout.WindowSizeClass computeWindowSizeClassPreferHeight(java.util.Set<androidx.window.core.layout.WindowSizeClass>, int widthDp, int heightDp);
}
- public static final class WindowWidthSizeClass.Companion {
+ @Deprecated public final class WindowWidthSizeClass {
+ field @Deprecated public static final androidx.window.core.layout.WindowWidthSizeClass COMPACT;
+ field @Deprecated public static final androidx.window.core.layout.WindowWidthSizeClass.Companion Companion;
+ field @Deprecated public static final androidx.window.core.layout.WindowWidthSizeClass EXPANDED;
+ field @Deprecated public static final androidx.window.core.layout.WindowWidthSizeClass MEDIUM;
+ }
+
+ @Deprecated public static final class WindowWidthSizeClass.Companion {
+ method @Deprecated public androidx.window.core.layout.WindowWidthSizeClass getCOMPACT();
+ method @Deprecated public androidx.window.core.layout.WindowWidthSizeClass getEXPANDED();
+ method @Deprecated public androidx.window.core.layout.WindowWidthSizeClass getMEDIUM();
+ property @Deprecated public androidx.window.core.layout.WindowWidthSizeClass COMPACT;
+ property @Deprecated public androidx.window.core.layout.WindowWidthSizeClass EXPANDED;
+ property @Deprecated public androidx.window.core.layout.WindowWidthSizeClass MEDIUM;
}
}
diff --git a/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowHeightSizeClass.kt b/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowHeightSizeClass.kt
index e69a63f..1eccac7 100644
--- a/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowHeightSizeClass.kt
+++ b/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowHeightSizeClass.kt
@@ -16,9 +16,6 @@
package androidx.window.core.layout
-import androidx.window.core.layout.WindowHeightSizeClass.Companion.COMPACT
-import androidx.window.core.layout.WindowHeightSizeClass.Companion.EXPANDED
-import androidx.window.core.layout.WindowHeightSizeClass.Companion.MEDIUM
import kotlin.jvm.JvmField
/**
@@ -27,6 +24,8 @@
* type. It is possible to have resizeable windows in different device types. The viewport might
* change from a [COMPACT] all the way to an [EXPANDED] size class.
*/
+@Suppress("DEPRECATION")
+@Deprecated("WindowHeightSizeClass will not be developed further, use WindowSizeClass instead.")
class WindowHeightSizeClass private constructor(private val rawValue: Int) {
override fun toString(): String {
@@ -56,16 +55,22 @@
companion object {
/** A bucket to represent a compact height, typical for a phone that is in landscape. */
- @JvmField val COMPACT: WindowHeightSizeClass = WindowHeightSizeClass(0)
+ @Deprecated("WindowHeightSizeClass not be developed further.")
+ @JvmField
+ val COMPACT: WindowHeightSizeClass = WindowHeightSizeClass(0)
/** A bucket to represent a medium height, typical for a phone in portrait or a tablet. */
- @JvmField val MEDIUM: WindowHeightSizeClass = WindowHeightSizeClass(1)
+ @Deprecated("WindowHeightSizeClass not be developed further.")
+ @JvmField
+ val MEDIUM: WindowHeightSizeClass = WindowHeightSizeClass(1)
/**
* A bucket to represent an expanded height window, typical for a large tablet or a desktop
* form-factor.
*/
- @JvmField val EXPANDED: WindowHeightSizeClass = WindowHeightSizeClass(2)
+ @Deprecated("WindowHeightSizeClass not be developed further.")
+ @JvmField
+ val EXPANDED: WindowHeightSizeClass = WindowHeightSizeClass(2)
/**
* Returns a recommended [WindowHeightSizeClass] for the height of a window given the height
@@ -75,6 +80,7 @@
* @return A recommended size class for the height
* @throws IllegalArgumentException if the height is negative
*/
+ @Deprecated("WindowHeightSizeClass not be developed further.")
internal fun compute(dpHeight: Float): WindowHeightSizeClass {
require(dpHeight >= 0) { "Height must be positive, received $dpHeight" }
return when {
diff --git a/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowSizeClass.kt b/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowSizeClass.kt
index 9df0361..43bef20 100644
--- a/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowSizeClass.kt
+++ b/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowSizeClass.kt
@@ -16,30 +16,28 @@
package androidx.window.core.layout
-import androidx.window.core.ExperimentalWindowCoreApi
+import kotlin.jvm.JvmField
import kotlin.jvm.JvmStatic
/**
- * [WindowSizeClass] represents breakpoints for a viewport. The recommended width and height break
- * points are presented through [windowWidthSizeClass] and [windowHeightSizeClass]. Designers should
- * design around the different combinations of width and height buckets. Developers should use the
- * different buckets to specify the layouts. Ideally apps will work well in each bucket and by
- * extension work well across multiple devices. If two devices are in similar buckets they should
- * behave similarly.
+ * [WindowSizeClass] represents breakpoints for a viewport. Designers should design around the
+ * different combinations of width and height buckets. Developers should use the different buckets
+ * to specify the layouts. Ideally apps will work well in each bucket and by extension work well
+ * across multiple devices. If two devices are in similar buckets they should behave similarly.
*
* This class is meant to be a common definition that can be shared across different device types.
- * Application developers can use WindowSizeClass to have standard window buckets and design the UI
- * around those buckets. Library developers can use these buckets to create different UI with
+ * Application developers can use [WindowSizeClass] to have standard window buckets and design the
+ * UI around those buckets. Library developers can use these buckets to create different UI with
* respect to each bucket. This will help with consistency across multiple device types.
*
* A library developer use-case can be creating some navigation UI library. For a size class with
- * the [WindowWidthSizeClass.EXPANDED] width it might be more reasonable to have a side navigation.
- * For a [WindowWidthSizeClass.COMPACT] width, a bottom navigation might be a better fit.
+ * the [WindowSizeClass.WIDTH_DP_EXPANDED_LOWER_BOUND] width it might be more reasonable to have a
+ * side navigation.
*
* An application use-case can be applied for apps that use a list-detail pattern. The app can use
- * the [WindowWidthSizeClass.MEDIUM] to determine if there is enough space to show the list and the
- * detail side by side. If all apps follow this guidance then it will present a very consistent user
- * experience.
+ * the [WindowSizeClass.WIDTH_DP_MEDIUM_LOWER_BOUND] to determine if there is enough space to show
+ * the list and the detail side by side. If all apps follow this guidance then it will present a
+ * very consistent user experience.
*
* In some cases developers or UI systems may decide to create their own break points. A developer
* might optimize for a window that is smaller than the supported break points or larger. A UI
@@ -50,13 +48,58 @@
* @see WindowWidthSizeClass
* @see WindowHeightSizeClass
*/
-class WindowSizeClass
-private constructor(
+class WindowSizeClass(
+ /** Returns the lower bound for the width of the size class in dp. */
+ val minWidthDp: Int,
+ /** Returns the lower bound for the height of the size class in dp. */
+ val minHeightDp: Int
+) {
+
+ /** A convenience constructor that will truncate to ints. */
+ constructor(widthDp: Float, heightDp: Float) : this(widthDp.toInt(), heightDp.toInt())
+
+ init {
+ require(minWidthDp >= 0) {
+ "Expected minWidthDp to be at least 0, minWidthDp: $minWidthDp."
+ }
+ require(minHeightDp >= 0) {
+ "Expected minHeightDp to be at least 0, minHeightDp: $minHeightDp."
+ }
+ }
+
+ @Suppress("DEPRECATION")
+ @Deprecated("Use either isWidthAtLeast or isAtLeast to check matching bounds.")
/** Returns the [WindowWidthSizeClass] that corresponds to the widthDp of the window. */
- val windowWidthSizeClass: WindowWidthSizeClass,
+ val windowWidthSizeClass: WindowWidthSizeClass
+ get() = WindowWidthSizeClass.compute(minWidthDp.toFloat())
+
+ @Suppress("DEPRECATION")
+ @Deprecated("Use either isHeightAtLeast or isAtLeast to check matching bounds.")
/** Returns the [WindowHeightSizeClass] that corresponds to the heightDp of the window. */
val windowHeightSizeClass: WindowHeightSizeClass
-) {
+ get() = WindowHeightSizeClass.compute(minHeightDp.toFloat())
+
+ /**
+ * Returns `true` when [widthDp] is greater than or equal to [minWidthDp], `false` otherwise.
+ */
+ fun isWidthAtLeast(widthDp: Int): Boolean {
+ return widthDp >= minWidthDp
+ }
+
+ /**
+ * Returns `true` when [heightDp] is greater than or equal to [minHeightDp], `false` otherwise.
+ */
+ fun isHeightAtLeast(heightDp: Int): Boolean {
+ return heightDp >= minHeightDp
+ }
+
+ /**
+ * Returns `true` when [widthDp] is greater than or equal to [minWidthDp] and [heightDp] is
+ * greater than or equal to [minHeightDp], `false` otherwise.
+ */
+ fun isAtLeast(widthDp: Int, heightDp: Int): Boolean {
+ return isWidthAtLeast(widthDp) && isHeightAtLeast(heightDp)
+ }
override fun equals(other: Any?): Boolean {
if (this === other) return true
@@ -64,25 +107,49 @@
other as WindowSizeClass
- if (windowWidthSizeClass != other.windowWidthSizeClass) return false
- if (windowHeightSizeClass != other.windowHeightSizeClass) return false
+ if (minWidthDp != other.minWidthDp) return false
+ if (minHeightDp != other.minHeightDp) return false
return true
}
override fun hashCode(): Int {
- var result = windowWidthSizeClass.hashCode()
- result = 31 * result + windowHeightSizeClass.hashCode()
+ var result = minWidthDp
+ result = 31 * result + minHeightDp
return result
}
override fun toString(): String {
- return "WindowSizeClass {" +
- "windowWidthSizeClass=$windowWidthSizeClass, " +
- "windowHeightSizeClass=$windowHeightSizeClass }"
+ return "WindowSizeClass(minWidthDp=$minWidthDp, minHeightDp=$minHeightDp)"
}
companion object {
+ /** A lower bound for a size class with Medium width in dp. */
+ const val WIDTH_DP_MEDIUM_LOWER_BOUND = 600
+
+ /** A lower bound for a size class with Expanded width in dp. */
+ const val WIDTH_DP_EXPANDED_LOWER_BOUND = 840
+
+ /** A lower bound for a size class with Medium height in dp. */
+ const val HEIGHT_DP_MEDIUM_LOWER_BOUND = 480
+
+ /** A lower bound for a size class with Expanded height in dp. */
+ const val HEIGHT_DP_EXPANDED_LOWER_BOUND = 900
+
+ private val WIDTH_DP_BREAKPOINTS_V1 =
+ listOf(WIDTH_DP_MEDIUM_LOWER_BOUND, WIDTH_DP_EXPANDED_LOWER_BOUND)
+
+ private val HEIGHT_DP_BREAKPOINTS_V1 =
+ listOf(HEIGHT_DP_MEDIUM_LOWER_BOUND, HEIGHT_DP_EXPANDED_LOWER_BOUND)
+
+ @JvmField
+ val BREAKPOINTS_V1 =
+ WIDTH_DP_BREAKPOINTS_V1.flatMap { widthBp ->
+ HEIGHT_DP_BREAKPOINTS_V1.map { heightBp ->
+ WindowSizeClass(minWidthDp = widthBp, minHeightDp = heightBp)
+ }
+ }
+ .toSet()
/**
* Computes the recommended [WindowSizeClass] for the given width and height in DP.
@@ -93,28 +160,21 @@
* @throws IllegalArgumentException if [dpWidth] or [dpHeight] is negative.
*/
@JvmStatic
+ @Deprecated("Use the constructor instead.")
fun compute(dpWidth: Float, dpHeight: Float): WindowSizeClass {
- return WindowSizeClass(
- WindowWidthSizeClass.compute(dpWidth),
- WindowHeightSizeClass.compute(dpHeight)
- )
- }
-
- /**
- * Computes the [WindowSizeClass] for the given width and height in pixels with density.
- *
- * @param widthPx width of a window in PX.
- * @param heightPx height of a window in PX.
- * @param density density of the display where the window is shown.
- * @return [WindowSizeClass] that is recommended for the given dimensions.
- * @throws IllegalArgumentException if [widthPx], [heightPx], or [density] is negative.
- */
- @JvmStatic
- @ExperimentalWindowCoreApi
- fun compute(widthPx: Int, heightPx: Int, density: Float): WindowSizeClass {
- val widthDp = widthPx / density
- val heightDp = heightPx / density
- return compute(widthDp, heightDp)
+ val widthDp =
+ when {
+ dpWidth >= WIDTH_DP_EXPANDED_LOWER_BOUND -> WIDTH_DP_EXPANDED_LOWER_BOUND
+ dpWidth >= WIDTH_DP_MEDIUM_LOWER_BOUND -> WIDTH_DP_MEDIUM_LOWER_BOUND
+ else -> 0
+ }
+ val heightDp =
+ when {
+ dpHeight >= HEIGHT_DP_EXPANDED_LOWER_BOUND -> HEIGHT_DP_EXPANDED_LOWER_BOUND
+ dpHeight >= HEIGHT_DP_MEDIUM_LOWER_BOUND -> HEIGHT_DP_MEDIUM_LOWER_BOUND
+ else -> 0
+ }
+ return WindowSizeClass(widthDp, heightDp)
}
}
}
diff --git a/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowSizeClassSelectors.kt b/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowSizeClassSelectors.kt
new file mode 100644
index 0000000..25ef7c0
--- /dev/null
+++ b/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowSizeClassSelectors.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.
+ */
+
+@file:JvmName("WindowSizeClassSelectors")
+
+package androidx.window.core.layout
+
+import kotlin.jvm.JvmName
+
+/**
+ * Returns the largest [WindowSizeClass] that is within the bounds of ([widthDp], [heightDp]). This
+ * method prefers width and uses max height to break ties. If there is no match a default of
+ * `WindowSizeClass(0,0)` is returned. Examples: Input: Set: `setOf(WindowSizeClass(300, 300),
+ * WindowSizeClass(300, 600)` widthDp: `300` heightDp: `800` Output: `WindowSizeClass(300, 600)`
+ * Input: Set: `setOf(WindowSizeClass(300, 300), WindowSizeClass(300, 600)` widthDp: `300` heightDp:
+ * `400` Output: `WindowSizeClass(300, 300)`
+ *
+ * @param widthDp the width of the window to match a [WindowSizeClass] to.
+ * @param heightDp the height of the window to match a [WindowSizeClass] to.
+ */
+fun Set<WindowSizeClass>.computeWindowSizeClass(widthDp: Int, heightDp: Int): WindowSizeClass {
+ var maxWidth = 0
+ forEach { bucket ->
+ if (bucket.minWidthDp <= widthDp && bucket.minWidthDp > maxWidth) {
+ maxWidth = bucket.minWidthDp
+ }
+ }
+ var match = WindowSizeClass(0, 0)
+ forEach { bucket ->
+ if (
+ bucket.minWidthDp == maxWidth &&
+ bucket.minHeightDp <= heightDp &&
+ match.minHeightDp < bucket.minHeightDp
+ ) {
+ match = bucket
+ }
+ }
+ return match
+}
+
+/**
+ * Returns the largest [WindowSizeClass] that is within the bounds of ([widthDp], [heightDp]). This
+ * method prefers height and uses max width to break ties. If there is no match a default of
+ * `WindowSizeClass(0,0)` is returned. Examples: Input: Set: `setOf(WindowSizeClass(300, 300),
+ * WindowSizeClass(600, 300)` widthDp: `800` heightDp: `300` Output: `WindowSizeClass(600, 300)`
+ * Input: Set: `setOf(WindowSizeClass(300, 300), WindowSizeClass(600, 300)` widthDp: `400` heightDp:
+ * `300` Output: `WindowSizeClass(300, 300)`
+ *
+ * @param widthDp the width of the window to match a [WindowSizeClass] to.
+ * @param heightDp the height of the window to match a [WindowSizeClass] to.
+ */
+fun Set<WindowSizeClass>.computeWindowSizeClassPreferHeight(
+ widthDp: Int,
+ heightDp: Int
+): WindowSizeClass {
+ var maxHeight = 0
+ forEach { bucket ->
+ if (bucket.minHeightDp <= heightDp && bucket.minHeightDp > maxHeight) {
+ maxHeight = bucket.minHeightDp
+ }
+ }
+ var match = WindowSizeClass(0, 0)
+ forEach { bucket ->
+ if (
+ bucket.minHeightDp == maxHeight &&
+ bucket.minWidthDp <= widthDp &&
+ match.minWidthDp < bucket.minWidthDp
+ ) {
+ match = bucket
+ }
+ }
+ return match
+}
diff --git a/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowWidthSizeClass.kt b/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowWidthSizeClass.kt
index 11af3a0..3097d8f 100644
--- a/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowWidthSizeClass.kt
+++ b/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowWidthSizeClass.kt
@@ -24,7 +24,10 @@
* type. It is possible to have resizeable windows in different device types. The viewport might
* change from a [COMPACT] all the way to an [EXPANDED] size class.
*/
+@Suppress("DEPRECATION")
+@Deprecated("WindowWidthSizeClass will not be developed further, use WindowSizeClass instead.")
class WindowWidthSizeClass private constructor(private val rawValue: Int) {
+
override fun toString(): String {
val name =
when (this) {
@@ -52,19 +55,25 @@
companion object {
/** A bucket to represent a compact width window, typical for a phone in portrait. */
- @JvmField val COMPACT: WindowWidthSizeClass = WindowWidthSizeClass(0)
+ @Deprecated("WindowWidthSizeClass not be developed further.")
+ @JvmField
+ val COMPACT: WindowWidthSizeClass = WindowWidthSizeClass(0)
/**
* A bucket to represent a medium width window, typical for a phone in landscape or a
* tablet.
*/
- @JvmField val MEDIUM: WindowWidthSizeClass = WindowWidthSizeClass(1)
+ @Deprecated("WindowWidthSizeClass not be developed further.")
+ @JvmField
+ val MEDIUM: WindowWidthSizeClass = WindowWidthSizeClass(1)
/**
* A bucket to represent an expanded width window, typical for a large tablet or desktop
* form-factor.
*/
- @JvmField val EXPANDED: WindowWidthSizeClass = WindowWidthSizeClass(2)
+ @Deprecated("WindowWidthSizeClass not be developed further.")
+ @JvmField
+ val EXPANDED: WindowWidthSizeClass = WindowWidthSizeClass(2)
/**
* Returns a recommended [WindowWidthSizeClass] for the width of a window given the width in
@@ -74,6 +83,7 @@
* @return A recommended size class for the width
* @throws IllegalArgumentException if the width is negative
*/
+ @Deprecated("WindowWidthSizeClass not be developed further.")
internal fun compute(dpWidth: Float): WindowWidthSizeClass {
require(dpWidth >= 0) { "Width must be positive, received $dpWidth" }
return when {
diff --git a/window/window-core/src/commonTest/kotlin/androidx/window/core/layout/WindowHeightSizeClassTest.kt b/window/window-core/src/commonTest/kotlin/androidx/window/core/layout/WindowHeightSizeClassTest.kt
index 80868cf..7183670 100644
--- a/window/window-core/src/commonTest/kotlin/androidx/window/core/layout/WindowHeightSizeClassTest.kt
+++ b/window/window-core/src/commonTest/kotlin/androidx/window/core/layout/WindowHeightSizeClassTest.kt
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+@file:Suppress("DEPRECATION")
+
package androidx.window.core.layout
import androidx.window.core.layout.WindowHeightSizeClass.Companion.COMPACT
diff --git a/window/window-core/src/commonTest/kotlin/androidx/window/core/layout/WindowSizeClassSelectorsTest.kt b/window/window-core/src/commonTest/kotlin/androidx/window/core/layout/WindowSizeClassSelectorsTest.kt
new file mode 100644
index 0000000..f72e378
--- /dev/null
+++ b/window/window-core/src/commonTest/kotlin/androidx/window/core/layout/WindowSizeClassSelectorsTest.kt
@@ -0,0 +1,155 @@
+/*
+ * 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.window.core.layout
+
+import androidx.window.core.layout.WindowSizeClass.Companion.HEIGHT_DP_MEDIUM_LOWER_BOUND
+import androidx.window.core.layout.WindowSizeClass.Companion.WIDTH_DP_MEDIUM_LOWER_BOUND
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class WindowSizeClassSelectorsTest {
+
+ val coreSet = WindowSizeClass.BREAKPOINTS_V1
+
+ @Test
+ fun compute_window_size_class_returns_zero_for_default() {
+ // coreSet does not contain 10, 10
+ val actual = coreSet.computeWindowSizeClass(10, 10)
+
+ assertEquals(WindowSizeClass(0, 0), actual)
+ }
+
+ @Test
+ fun compute_window_size_class_returns_exact_match() {
+ val expected = WindowSizeClass(WIDTH_DP_MEDIUM_LOWER_BOUND, HEIGHT_DP_MEDIUM_LOWER_BOUND)
+
+ // coreSet contains WindowSizeClass(MEDIUM, MEDIUM)
+ val actual =
+ coreSet.computeWindowSizeClass(
+ WIDTH_DP_MEDIUM_LOWER_BOUND,
+ HEIGHT_DP_MEDIUM_LOWER_BOUND
+ )
+
+ assertEquals(expected, actual)
+ }
+
+ @Test
+ fun compute_window_size_class_returns_bounded_match() {
+ val expected = WindowSizeClass(WIDTH_DP_MEDIUM_LOWER_BOUND, HEIGHT_DP_MEDIUM_LOWER_BOUND)
+
+ // coreSet contains WindowSizeClass(MEDIUM, MEDIUM)
+ val actual =
+ coreSet.computeWindowSizeClass(
+ WIDTH_DP_MEDIUM_LOWER_BOUND + 1,
+ HEIGHT_DP_MEDIUM_LOWER_BOUND + 1
+ )
+
+ assertEquals(expected, actual)
+ }
+
+ @Test
+ fun compute_window_size_class_prefers_width() {
+ val expected = WindowSizeClass(minWidthDp = 100, minHeightDp = 50)
+
+ val actual =
+ setOf(
+ WindowSizeClass(minWidthDp = 100, minHeightDp = 50),
+ WindowSizeClass(minWidthDp = 50, minHeightDp = 100)
+ )
+ .computeWindowSizeClass(100, 100)
+
+ assertEquals(expected, actual)
+ }
+
+ @Test
+ fun compute_window_size_class_breaks_tie_with_height() {
+ val expected = WindowSizeClass(minWidthDp = 100, minHeightDp = 100)
+
+ val actual =
+ setOf(
+ WindowSizeClass(minWidthDp = 100, minHeightDp = 50),
+ WindowSizeClass(minWidthDp = 100, minHeightDp = 100)
+ )
+ .computeWindowSizeClass(200, 200)
+
+ assertEquals(expected, actual)
+ }
+
+ @Test
+ fun compute_window_size_class_preferring_height_returns_zero_for_default() {
+ // coreSet does not contain 10, 10
+ val actual = coreSet.computeWindowSizeClassPreferHeight(10, 10)
+
+ assertEquals(WindowSizeClass(0, 0), actual)
+ }
+
+ @Test
+ fun compute_window_size_class_preferring_height_returns_exact_match() {
+ val expected = WindowSizeClass(WIDTH_DP_MEDIUM_LOWER_BOUND, HEIGHT_DP_MEDIUM_LOWER_BOUND)
+
+ // coreSet contains WindowSizeClass(MEDIUM, MEDIUM)
+ val actual =
+ coreSet.computeWindowSizeClassPreferHeight(
+ WIDTH_DP_MEDIUM_LOWER_BOUND,
+ HEIGHT_DP_MEDIUM_LOWER_BOUND
+ )
+
+ assertEquals(expected, actual)
+ }
+
+ @Test
+ fun compute_window_size_class_preferring_height_returns_bounded_match() {
+ val expected = WindowSizeClass(WIDTH_DP_MEDIUM_LOWER_BOUND, HEIGHT_DP_MEDIUM_LOWER_BOUND)
+
+ // coreSet contains WindowSizeClass(MEDIUM, MEDIUM)
+ val actual =
+ coreSet.computeWindowSizeClassPreferHeight(
+ WIDTH_DP_MEDIUM_LOWER_BOUND + 1,
+ HEIGHT_DP_MEDIUM_LOWER_BOUND + 1
+ )
+
+ assertEquals(expected, actual)
+ }
+
+ @Test
+ fun compute_window_size_class_preferring_height_prefers_height() {
+ val expected = WindowSizeClass(minWidthDp = 50, minHeightDp = 100)
+
+ val actual =
+ setOf(
+ WindowSizeClass(minWidthDp = 100, minHeightDp = 50),
+ WindowSizeClass(minWidthDp = 50, minHeightDp = 100)
+ )
+ .computeWindowSizeClassPreferHeight(100, 100)
+
+ assertEquals(expected, actual)
+ }
+
+ @Test
+ fun compute_window_size_class_preferring_height_breaks_tie_with_width() {
+ val expected = WindowSizeClass(minWidthDp = 100, minHeightDp = 100)
+
+ val actual =
+ setOf(
+ WindowSizeClass(minWidthDp = 50, minHeightDp = 100),
+ WindowSizeClass(minWidthDp = 100, minHeightDp = 100)
+ )
+ .computeWindowSizeClass(200, 200)
+
+ assertEquals(expected, actual)
+ }
+}
diff --git a/window/window-core/src/commonTest/kotlin/androidx/window/core/layout/WindowSizeClassTest.kt b/window/window-core/src/commonTest/kotlin/androidx/window/core/layout/WindowSizeClassTest.kt
index 46607ab..816e40c 100644
--- a/window/window-core/src/commonTest/kotlin/androidx/window/core/layout/WindowSizeClassTest.kt
+++ b/window/window-core/src/commonTest/kotlin/androidx/window/core/layout/WindowSizeClassTest.kt
@@ -16,16 +16,18 @@
package androidx.window.core.layout
-import androidx.window.core.ExperimentalWindowCoreApi
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
/** Tests for [WindowSizeClass] that verify construction. */
class WindowSizeClassTest {
+ @Suppress("DEPRECATION")
@Test
- fun testWidthSizeClass_construction() {
+ fun testWindowWidthSizeClass_compatibility() {
val expected =
listOf(
WindowWidthSizeClass.COMPACT,
@@ -41,6 +43,7 @@
assertEquals(expected, actual)
}
+ @Suppress("DEPRECATION")
@Test
fun testWindowSizeClass_computeRounds() {
val expected = WindowSizeClass.compute(0f, 0f)
@@ -50,18 +53,9 @@
assertEquals(expected, actual)
}
- @OptIn(ExperimentalWindowCoreApi::class)
+ @Suppress("DEPRECATION")
@Test
- fun testConstruction_usingPx() {
- val expected = WindowSizeClass.compute(600f, 600f)
-
- val actual = WindowSizeClass.compute(600, 600, 1f)
-
- assertEquals(expected, actual)
- }
-
- @Test
- fun testHeightSizeClass_construction() {
+ fun testWindowHeightSizeClass_compatibility() {
val expected =
listOf(
WindowHeightSizeClass.COMPACT,
@@ -79,16 +73,17 @@
@Test
fun testEqualsImpliesHashCode() {
- val first = WindowSizeClass.compute(100f, 500f)
- val second = WindowSizeClass.compute(100f, 500f)
+ val first = WindowSizeClass(100, 500)
+ val second = WindowSizeClass(100, 500)
assertEquals(first, second)
assertEquals(first.hashCode(), second.hashCode())
}
+ @Suppress("DEPRECATION")
@Test
fun truncated_float_does_not_throw() {
- val sizeClass = WindowSizeClass.compute(0.5f, 0.5f)
+ val sizeClass = WindowSizeClass(0.5f, 0.5f)
val widthSizeClass = sizeClass.windowWidthSizeClass
val heightSizeClass = sizeClass.windowHeightSizeClass
@@ -97,9 +92,10 @@
assertEquals(WindowHeightSizeClass.COMPACT, heightSizeClass)
}
+ @Suppress("DEPRECATION")
@Test
fun zero_size_class_does_not_throw() {
- val sizeClass = WindowSizeClass.compute(0f, 0f)
+ val sizeClass = WindowSizeClass(0, 0)
val widthSizeClass = sizeClass.windowWidthSizeClass
val heightSizeClass = sizeClass.windowHeightSizeClass
@@ -110,11 +106,94 @@
@Test
fun negative_width_throws() {
- assertFailsWith(IllegalArgumentException::class) { WindowSizeClass.compute(-1f, 0f) }
+ assertFailsWith(IllegalArgumentException::class) { WindowSizeClass(-1, 0) }
}
@Test
fun negative_height_throws() {
- assertFailsWith(IllegalArgumentException::class) { WindowSizeClass.compute(0f, -1f) }
+ assertFailsWith(IllegalArgumentException::class) { WindowSizeClass(0, -1) }
+ }
+
+ @Test
+ fun is_width_at_least_returns_true_when_input_is_greater() {
+ val width = 200
+ val height = 100
+ val sizeClass = WindowSizeClass(width, height)
+
+ assertTrue(sizeClass.isWidthAtLeast(width + 1))
+ }
+
+ @Test
+ fun is_width_at_least_returns_true_when_input_is_equal() {
+ val width = 200
+ val height = 100
+ val sizeClass = WindowSizeClass(width, height)
+
+ assertTrue(sizeClass.isWidthAtLeast(width))
+ }
+
+ @Test
+ fun is_width_at_least_returns_false_when_input_is_smaller() {
+ val width = 200
+ val height = 100
+ val sizeClass = WindowSizeClass(width, height)
+
+ assertFalse(sizeClass.isWidthAtLeast(width - 1))
+ }
+
+ @Test
+ fun is_height_at_least_returns_true_when_input_is_greater() {
+ val width = 200
+ val height = 100
+ val sizeClass = WindowSizeClass(width, height)
+
+ assertTrue(sizeClass.isHeightAtLeast(height + 1))
+ }
+
+ @Test
+ fun is_height_at_least_returns_true_when_input_is_equal() {
+ val width = 200
+ val height = 100
+ val sizeClass = WindowSizeClass(width, height)
+
+ assertTrue(sizeClass.isHeightAtLeast(height))
+ }
+
+ @Test
+ fun is_height_at_least_returns_false_when_input_is_smaller() {
+ val width = 200
+ val height = 100
+ val sizeClass = WindowSizeClass(width, height)
+
+ assertFalse(sizeClass.isHeightAtLeast(height - 1))
+ }
+
+ @Test
+ fun is_at_least_returns_true_when_input_is_greater() {
+ val width = 200
+ val height = 100
+ val sizeClass = WindowSizeClass(width, height)
+
+ assertTrue(sizeClass.isAtLeast(width, height + 1))
+ assertTrue(sizeClass.isAtLeast(width + 1, height))
+ }
+
+ @Test
+ fun is_at_least_returns_true_when_input_is_equal() {
+ val width = 200
+ val height = 100
+ val sizeClass = WindowSizeClass(width, height)
+
+ assertTrue(sizeClass.isAtLeast(width, height))
+ }
+
+ @Test
+ fun is_at_least_returns_false_when_input_is_smaller() {
+ val width = 200
+ val height = 100
+ val sizeClass = WindowSizeClass(width, height)
+
+ assertFalse(sizeClass.isAtLeast(width, height - 1))
+ assertFalse(sizeClass.isAtLeast(width - 1, height))
}
}
diff --git a/window/window-core/src/commonTest/kotlin/androidx/window/core/layout/WindowWidthSizeClassTest.kt b/window/window-core/src/commonTest/kotlin/androidx/window/core/layout/WindowWidthSizeClassTest.kt
index 85ab99b..520b9bf 100644
--- a/window/window-core/src/commonTest/kotlin/androidx/window/core/layout/WindowWidthSizeClassTest.kt
+++ b/window/window-core/src/commonTest/kotlin/androidx/window/core/layout/WindowWidthSizeClassTest.kt
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+@file:Suppress("DEPRECATION")
+
package androidx.window.core.layout
import androidx.window.core.layout.WindowWidthSizeClass.Companion.COMPACT
diff --git a/window/window-demos/demo/src/main/java/androidx/window/demo/coresdk/WindowStateScreen.kt b/window/window-demos/demo/src/main/java/androidx/window/demo/coresdk/WindowStateScreen.kt
index 92e85eb..3ff2766 100644
--- a/window/window-demos/demo/src/main/java/androidx/window/demo/coresdk/WindowStateScreen.kt
+++ b/window/window-demos/demo/src/main/java/androidx/window/demo/coresdk/WindowStateScreen.kt
@@ -154,7 +154,7 @@
activityDisplayBounds = Rect(0, 0, 960, 2142),
),
)
- DemoTheme { WindowStateScreen(viewModel = WindowStateViewModel(windowStates)) }
+ DemoTheme { WindowStateScreen(viewModel = viewModel { WindowStateViewModel(windowStates) }) }
}
/**