Merge "Rename setIsScalable to setScalable" into androidx-main
diff --git a/biometric/biometric/src/main/res/values-ja/strings.xml b/biometric/biometric/src/main/res/values-ja/strings.xml
index 8fb1180..b0742f2 100644
--- a/biometric/biometric/src/main/res/values-ja/strings.xml
+++ b/biometric/biometric/src/main/res/values-ja/strings.xml
@@ -23,7 +23,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="7520712796891883488">"指紋が登録されていません。"</string>
<string name="fingerprint_error_hw_not_present" msgid="6306988885793029438">"このデバイスには指紋認証センサーがありません"</string>
<string name="fingerprint_error_user_canceled" msgid="7627716295344353987">"指紋認証操作がユーザーによりキャンセルされました。"</string>
- <string name="fingerprint_error_lockout" msgid="7291787166416782245">"入力回数が上限を超えました。しばらくしてからもう一度お試しください。"</string>
+ <string name="fingerprint_error_lockout" msgid="7291787166416782245">"試行回数が上限に達しました。しばらくしてからもう一度お試しください。"</string>
<string name="default_error_msg" msgid="4776854077120974966">"不明なエラーです"</string>
<string name="generic_error_user_canceled" msgid="7309881387583143581">"認証はユーザーによりキャンセルされました。"</string>
<string name="confirm_device_credential_password" msgid="5912733858573823945">"パスワードを使用"</string>
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
index c6337e0..8adad31 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXImplPlugin.kt
@@ -813,10 +813,12 @@
publishing { singleVariant(DEFAULT_PUBLISH_CONFIG) }
configureAndroidBaseOptions(project, androidXExtension)
- project.defaultAndroidConfig.targetSdk.let {
- lint.targetSdk = it
- testOptions.targetSdk = it
- }
+
+ // Move to api that allows settings of targetSdk on each variant's androidTest when
+ // b/335257447 is fixed
+ @Suppress("DEPRECATION")
+ defaultConfig.targetSdk = project.defaultAndroidConfig.targetSdk
+
val debugSigningConfig = signingConfigs.getByName("debug")
// Use a local debug keystore to avoid build server issues.
debugSigningConfig.storeFile = project.getKeystore()
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/dackka/DackkaTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/dackka/DackkaTask.kt
index 3eaafa9..ccc74a1 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/dackka/DackkaTask.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/dackka/DackkaTask.kt
@@ -67,9 +67,18 @@
@get:[InputFiles PathSensitive(PathSensitivity.RELATIVE)]
abstract val frameworkSamplesDir: DirectoryProperty
- // Directory containing the code samples
+ // Directory containing the code samples derived via the old method. This will be removed
+ // as soon as all libraries have been published with samples. b/329424152
@get:[InputFiles PathSensitive(PathSensitivity.RELATIVE)]
- abstract val samplesDir: DirectoryProperty
+ abstract val samplesDeprecatedDir: DirectoryProperty
+
+ // Directory containing the code samples for non-KMP libraries
+ @get:[InputFiles PathSensitive(PathSensitivity.RELATIVE)]
+ abstract val samplesJvmDir: DirectoryProperty
+
+ // Directory containing the code samples for KMP libraries
+ @get:[InputFiles PathSensitive(PathSensitivity.RELATIVE)]
+ abstract val samplesKmpDir: DirectoryProperty
// Directory containing the JVM source code for Dackka to process
@get:[InputFiles PathSensitive(PathSensitivity.RELATIVE)]
@@ -153,7 +162,9 @@
// samples are in common
samples = if (analysisPlatform == DokkaAnalysisPlatform.COMMON) {
objects.fileCollection().from(
- samplesDir,
+ samplesDeprecatedDir,
+ samplesJvmDir,
+ samplesKmpDir,
frameworkSamplesDir.get().asFile
)
} else {
@@ -179,7 +190,9 @@
analysisPlatform = "jvm",
sourceRoots = objects.fileCollection().from(jvmSourcesDir),
samples = objects.fileCollection().from(
- samplesDir,
+ samplesDeprecatedDir,
+ samplesJvmDir,
+ samplesKmpDir,
frameworkSamplesDir.get().asFile
),
includes = objects.fileCollection().from(includesFiles(jvmSourcesDir.get().asFile)),
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/docs/AndroidXDocsImplPlugin.kt b/buildSrc/private/src/main/kotlin/androidx/build/docs/AndroidXDocsImplPlugin.kt
index 66e99dc..c73c126 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/docs/AndroidXDocsImplPlugin.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/docs/AndroidXDocsImplPlugin.kt
@@ -39,6 +39,7 @@
import java.io.FileNotFoundException
import java.time.Duration
import java.time.LocalDateTime
+import java.util.concurrent.TimeUnit
import javax.inject.Inject
import org.gradle.api.DefaultTask
import org.gradle.api.Plugin
@@ -126,15 +127,19 @@
distributionDirectory = project.getDistributionDirectory()
}
- val unzippedSamplesSources = project.layout.buildDirectory.dir("unzippedSampleSources")
- val unzipSamplesTask =
+ val unzippedDeprecatedSamplesSources =
+ project.layout.buildDirectory.dir("unzippedDeprecatedSampleSources")
+ val deprecatedUnzipSamplesTask =
configureUnzipTask(
project,
- "unzipSampleSources",
- unzippedSamplesSources,
+ "unzipSampleSourcesDeprecated",
+ unzippedDeprecatedSamplesSources,
samplesSourcesConfiguration
)
-
+ val unzippedKmpSamplesSourcesDirectory =
+ project.layout.buildDirectory.dir("unzippedMultiplatformSampleSources")
+ val unzippedJvmSamplesSourcesDirectory =
+ project.layout.buildDirectory.dir("unzippedJvmSampleSources")
val unzippedJvmSourcesDirectory = project.layout.buildDirectory.dir("unzippedJvmSources")
val unzippedMultiplatformSourcesDirectory =
project.layout.buildDirectory.dir("unzippedMultiplatformSources")
@@ -142,33 +147,38 @@
project.layout.buildDirectory.file(
"project_metadata/$PROJECT_STRUCTURE_METADATA_FILENAME"
)
- val unzipJvmSourcesTask =
+ val (unzipJvmSourcesTask, unzipJvmSamplesTask) =
configureUnzipJvmSourcesTasks(
project,
unzippedJvmSourcesDirectory,
+ unzippedJvmSamplesSourcesDirectory,
docsSourcesConfiguration
)
val configureMultiplatformSourcesTask =
configureMultiplatformInputsTasks(
project,
unzippedMultiplatformSourcesDirectory,
+ unzippedKmpSamplesSourcesDirectory,
multiplatformDocsSourcesConfiguration,
mergedProjectMetadata
)
configureDackka(
- project,
- unzippedJvmSourcesDirectory,
- unzippedMultiplatformSourcesDirectory,
- unzipJvmSourcesTask,
- configureMultiplatformSourcesTask,
- unzippedSamplesSources,
- unzipSamplesTask,
- dependencyClasspath,
- buildOnServer,
- docsSourcesConfiguration,
- multiplatformDocsSourcesConfiguration,
- mergedProjectMetadata
+ project = project,
+ unzippedJvmSourcesDirectory = unzippedJvmSourcesDirectory,
+ unzippedMultiplatformSourcesDirectory = unzippedMultiplatformSourcesDirectory,
+ unzipJvmSourcesTask = unzipJvmSourcesTask,
+ configureMultiplatformSourcesTask = configureMultiplatformSourcesTask,
+ unzippedDeprecatedSamplesSources = unzippedDeprecatedSamplesSources,
+ unzipDeprecatedSamplesTask = deprecatedUnzipSamplesTask,
+ unzippedJvmSamplesSources = unzippedJvmSamplesSourcesDirectory,
+ unzipJvmSamplesTask = unzipJvmSamplesTask,
+ unzippedKmpSamplesSources = unzippedKmpSamplesSourcesDirectory,
+ dependencyClasspath = dependencyClasspath,
+ buildOnServer = buildOnServer,
+ docsConfiguration = docsSourcesConfiguration,
+ multiplatformDocsConfiguration = multiplatformDocsSourcesConfiguration,
+ mergedProjectMetadata = mergedProjectMetadata
)
project.configureTaskTimeouts()
@@ -214,36 +224,45 @@
}
/**
- * Creates and configures a task that will build a list of select sources from jars and places
- * them in [destinationDirectory].
+ * Creates and configures a task that builds a list of select sources from jars and places them
+ * in [sourcesDestinationDirectory], partitioning samples into [samplesDestinationDirectory].
*
* This is a modified version of [configureUnzipTask], customized for Dackka usage.
*/
private fun configureUnzipJvmSourcesTasks(
project: Project,
- destinationDirectory: Provider<Directory>,
+ sourcesDestinationDirectory: Provider<Directory>,
+ samplesDestinationDirectory: Provider<Directory>,
docsConfiguration: Configuration
- ): TaskProvider<Sync> {
+ ): Pair<TaskProvider<Sync>, TaskProvider<Sync>> {
+ val pairProvider = docsConfiguration.incoming.artifactView {}.files.elements.map {
+ it.map { it.asFile }.toSortedSet().partition { "samples" !in it.toString() }
+ }
return project.tasks.register("unzipJvmSources", Sync::class.java) { task ->
- val sources = docsConfiguration.incoming.artifactView {}.files
-
// Store archiveOperations into a local variable to prevent access to the plugin
// during the task execution, as that breaks configuration caching.
val localVar = archiveOperations
- task.into(destinationDirectory)
+ task.into(sourcesDestinationDirectory)
task.from(
- sources.elements.map { jars ->
- // Now that we publish sample jars, they can get confused with normal source
- // jars. We want to handle sample jars separately, so filter by the name.
- jars.filter { "samples" !in it.toString() }
- .map { it.asFile }.toSortedSet().map { jar ->
- localVar.zipTree(jar).matching { it.exclude("**/META-INF/MANIFEST.MF") }
- }
- }
+ pairProvider.map { it.first }.map { it.map { jar ->
+ localVar.zipTree(jar).matching { it.exclude("**/META-INF/MANIFEST.MF") }
+ } }
)
// Files with the same path in different source jars of the same library will lead to
// some classes/methods not appearing in the docs.
task.duplicatesStrategy = DuplicatesStrategy.WARN
+ } to project.tasks.register("unzipSampleSources", Sync::class.java) { task ->
+ // Store archiveOperations into a local variable to prevent access to the plugin
+ // during the task execution, as that breaks configuration caching.
+ val localVar = archiveOperations
+ task.into(samplesDestinationDirectory)
+ task.from(
+ pairProvider.map { it.second }.map { it.map { jar ->
+ localVar.zipTree(jar).matching { it.exclude("**/META-INF/MANIFEST.MF") }
+ } }
+ )
+ // We expect this to happen when multiple libraries use the same sample, e.g. paging.
+ task.duplicatesStrategy = DuplicatesStrategy.INCLUDE
}
}
@@ -254,6 +273,7 @@
private fun configureMultiplatformInputsTasks(
project: Project,
unzippedMultiplatformSourcesDirectory: Provider<Directory>,
+ unzippedMultiplatformSamplesDirectory: Provider<Directory>,
multiplatformDocsSourcesConfiguration: Configuration,
mergedProjectMetadata: Provider<RegularFile>
): TaskProvider<MergeMultiplatformMetadataTask> {
@@ -270,6 +290,7 @@
)
it.metadataOutput.set(tempMultiplatformMetadataDirectory)
it.sourceOutput.set(unzippedMultiplatformSourcesDirectory)
+ it.samplesOutput.set(unzippedMultiplatformSamplesDirectory)
}
// merge all the metadata files from the individual project dirs
return project.tasks.register(
@@ -457,8 +478,11 @@
unzippedMultiplatformSourcesDirectory: Provider<Directory>,
unzipJvmSourcesTask: TaskProvider<Sync>,
configureMultiplatformSourcesTask: TaskProvider<MergeMultiplatformMetadataTask>,
- unzippedSamplesSources: Provider<Directory>,
- unzipSamplesTask: TaskProvider<Sync>,
+ unzippedDeprecatedSamplesSources: Provider<Directory>,
+ unzipDeprecatedSamplesTask: TaskProvider<Sync>,
+ unzippedJvmSamplesSources: Provider<Directory>,
+ unzipJvmSamplesTask: TaskProvider<Sync>,
+ unzippedKmpSamplesSources: Provider<Directory>,
dependencyClasspath: FileCollection,
buildOnServer: TaskProvider<*>,
docsConfiguration: Configuration,
@@ -506,7 +530,8 @@
// Use samplesDir.set(unzipSamplesTask.flatMap { it.destinationDirectory })
// https://github.com/gradle/gradle/issues/25824
dependsOn(unzipJvmSourcesTask)
- dependsOn(unzipSamplesTask)
+ dependsOn(unzipJvmSamplesTask)
+ dependsOn(unzipDeprecatedSamplesTask)
dependsOn(configureMultiplatformSourcesTask)
description =
@@ -519,7 +544,9 @@
frameworkSamplesDir.set(
project.rootProject.layout.projectDirectory.dir("samples")
)
- samplesDir.set(unzippedSamplesSources)
+ samplesDeprecatedDir.set(unzippedDeprecatedSamplesSources)
+ samplesJvmDir.set(unzippedJvmSamplesSources)
+ samplesKmpDir.set(unzippedKmpSamplesSources)
jvmSourcesDir.set(unzippedJvmSourcesDirectory)
multiplatformSourcesDir.set(unzippedMultiplatformSourcesDirectory)
projectListsDirectory.set(
@@ -551,6 +578,18 @@
)
task.doFirst { taskStartTime = LocalDateTime.now() }
task.doLast {
+ val cpus = try {
+ ProcessBuilder("lscpu").start()
+ .apply { waitFor(100L, TimeUnit.MILLISECONDS) }
+ .inputStream.bufferedReader().readLines()
+ .filter { it.startsWith("CPU(s):") }.singleOrNull()
+ ?.split(" ")?.last()?.toInt()
+ } catch (e: java.io.IOException) { null } // not running on linux
+ if (cpus != 64) { // Keep stddev of build metrics low b/334867245
+ println("$cpus cpus, so not storing build metrics.")
+ return@doLast
+ }
+ println("$cpus cpus, so storing build metrics.")
val taskEndTime = LocalDateTime.now()
val duration = Duration.between(taskStartTime, taskEndTime).toMillis()
metricsFile
@@ -759,24 +798,33 @@
@get:OutputDirectory abstract val metadataOutput: DirectoryProperty
@get:OutputDirectory abstract val sourceOutput: DirectoryProperty
+ @get:OutputDirectory abstract val samplesOutput: DirectoryProperty
@get:Inject abstract val fileSystemOperations: FileSystemOperations
@get:Inject abstract val archiveOperations: ArchiveOperations
@TaskAction
fun execute() {
- val sources = inputJars.get()
+ val (sources, samples) = inputJars.get()
+ .associate { it.name to archiveOperations.zipTree(it) }.toSortedMap()
// Now that we publish sample jars, they can get confused with normal source
// jars. We want to handle sample jars separately, so filter by the name.
- .filter { "samples" !in it.toString() }
- .associate { it.name to archiveOperations.zipTree(it) }
- .toSortedMap()
+ .partition { name -> "samples" !in name }
fileSystemOperations.sync {
it.duplicatesStrategy = DuplicatesStrategy.FAIL
it.from(sources.values)
it.into(sourceOutput)
it.exclude("META-INF/*")
}
+ fileSystemOperations.sync {
+ // Some libraries share samples, e.g. paging. This can be an issue if and only if the
+ // consumer libraries have pinned samples version or are not in an atomic group.
+ // We don't have anything matching this case now, but should enforce better. b/334825580
+ it.duplicatesStrategy = DuplicatesStrategy.INCLUDE
+ it.from(samples.values)
+ it.into(samplesOutput)
+ it.exclude("META-INF/*")
+ }
sources.forEach { (name, fileTree) ->
fileSystemOperations.sync {
it.from(fileTree)
@@ -786,6 +834,8 @@
}
}
}
+private fun <K, V> Map<K, V>.partition(condition: (K) -> Boolean): Pair<Map<K, V>, Map<K, V>> =
+ this.toList().partition { (k, _) -> condition(k) }.let { it.first.toMap() to it.second.toMap() }
/** Merges multiplatform metadata files created by [CreateMultiplatformMetadata] */
@CacheableTask
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/docs/CheckTipOfTreeDocsTask.kt b/buildSrc/private/src/main/kotlin/androidx/build/docs/CheckTipOfTreeDocsTask.kt
index 46685ce..1d52976 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/docs/CheckTipOfTreeDocsTask.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/docs/CheckTipOfTreeDocsTask.kt
@@ -138,7 +138,6 @@
* unless opted-out with [AndroidXExtension.doNotDocumentReason]
*/
fun AndroidXExtension.requiresDocs() =
- (shouldConfigureApiTasks() || type == LibraryType.SAMPLES) &&
- doNotDocumentReason == null
+ shouldConfigureApiTasks() && doNotDocumentReason == null
}
}
diff --git a/buildSrc/public/src/main/kotlin/androidx/build/SdkHelper.kt b/buildSrc/public/src/main/kotlin/androidx/build/SdkHelper.kt
index 393b5db..9b9c338 100644
--- a/buildSrc/public/src/main/kotlin/androidx/build/SdkHelper.kt
+++ b/buildSrc/public/src/main/kotlin/androidx/build/SdkHelper.kt
@@ -99,7 +99,7 @@
/** Sets the path to the canonical root project directory, e.g. {@code frameworks/support}. */
fun Project.setSupportRootFolder(rootDir: File?) {
- val extension = project.rootProject.property("ext") as ExtraPropertiesExtension
+ val extension = project.property("ext") as ExtraPropertiesExtension
return extension.set("supportRootFolder", rootDir)
}
@@ -110,7 +110,7 @@
* because it is generalized to also work for the "ui" project and playground projects.
*/
fun Project.getSupportRootFolder(): File {
- val extension = project.rootProject.property("ext") as ExtraPropertiesExtension
+ val extension = project.property("ext") as ExtraPropertiesExtension
return extension.get("supportRootFolder") as File
}
diff --git a/buildSrc/settingsScripts/project-dependency-graph.groovy b/buildSrc/settingsScripts/project-dependency-graph.groovy
index 34e95d3..1590a62 100644
--- a/buildSrc/settingsScripts/project-dependency-graph.groovy
+++ b/buildSrc/settingsScripts/project-dependency-graph.groovy
@@ -31,6 +31,7 @@
class ProjectDependencyGraph {
private Settings settings;
private boolean isPlayground;
+ private boolean constraintsEnabled
/**
* A map of project path to a set of project paths referenced directly by this project.
*/
@@ -41,14 +42,17 @@
*/
private Map<String, Set<String>> projectConsumers = new HashMap<String, Set<String>>()
+ private Set<String> publishedLibraryProjects = new HashSet<>()
+
/**
* A map of all project paths to their project directory.
*/
private Map<String, File> allProjects = new HashMap<String, File>()
- ProjectDependencyGraph(Settings settings, boolean isPlayground) {
+ ProjectDependencyGraph(Settings settings, boolean isPlayground, boolean constraintsEnabled) {
this.settings = settings
this.isPlayground = isPlayground
+ this.constraintsEnabled = constraintsEnabled
}
Set<String> allProjectPaths() {
@@ -136,7 +140,30 @@
"and update the project dependencies.")
}
def implicitReferences = findImplicitReferences(projectPath)
- return references + implicitReferences
+ def constraintReferences = findConstraintReferences(projectPath)
+ return references + implicitReferences + constraintReferences
+ }
+
+ /**
+ * Finds sibling projects that will be needed for constraint publishing. This is necessary
+ * for when androidx.constraints=true is set and automatic atomic group constraints are enabled
+ * meaning that :foo:foo and :foo:foo-bar projects are required even if they don't reference
+ * each other.
+ *
+ * @param projectPath The project path whose sibling projects will be found
+ * @return The set of sibling projects that will be needed for constraint publishing
+ */
+ private Set<String> findConstraintReferences(String projectPath) {
+ Set<String> constraintReferences = new HashSet()
+ if (!constraintsEnabled || !publishedLibraryProjects.contains(projectPath)) return constraintReferences
+ def lastColon = projectPath.lastIndexOf(":")
+ if (lastColon == -1) return constraintReferences
+ allProjectPaths().forEach {
+ if (it.startsWith(projectPath.substring(0, lastColon)) && publishedLibraryProjects.contains(it)) {
+ constraintReferences.add(it)
+ }
+ }
+ return constraintReferences
}
@@ -242,6 +269,9 @@
if (iconGenerator.matcher(line).find()) {
links.add(":compose:material:material:icons:generator")
}
+ if (publishedLibrary.matcher(line).find()) {
+ publishedLibraryProjects.add(projectPath)
+ }
}
} else if (!projectDir.exists()) {
// Remove file existence checking when https://github.com/gradle/gradle/issues/25531 is
@@ -264,10 +294,14 @@
private static Pattern inspection = Pattern.compile("packageInspector\\(project, \"(.*)\"\\)")
private static Pattern composePlugin = Pattern.compile("id\\(\"AndroidXComposePlugin\"\\)")
private static Pattern iconGenerator = Pattern.compile("IconGenerationTask\\.register")
+ private static Pattern publishedLibrary = Pattern.compile(
+ "(type = LibraryType\\.(PUBLISHED_LIBRARY|GRADLE_PLUGIN|ANNOTATION_PROCESSOR|PUBLISHED_KOTLIN_ONLY_LIBRARY)|" +
+ "publish = Publish\\.SNAPSHOT_AND_RELEASE)"
+ )
}
-ProjectDependencyGraph createProjectDependencyGraph(Settings settings) {
- return new ProjectDependencyGraph(settings, false /** isPlayground **/)
+ProjectDependencyGraph createProjectDependencyGraph(Settings settings, boolean constraintsEnabled) {
+ return new ProjectDependencyGraph(settings, false /** isPlayground **/, constraintsEnabled)
}
// export a function to create ProjectDependencyGraph
ext.createProjectDependencyGraph = this.&createProjectDependencyGraph
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/PhysicalCameraInfoAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/PhysicalCameraInfoAdapter.kt
index da5bda2..a8b4d15 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/PhysicalCameraInfoAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/PhysicalCameraInfoAdapter.kt
@@ -17,7 +17,9 @@
package androidx.camera.camera2.pipe.integration.adapter
import android.annotation.SuppressLint
+import android.hardware.camera2.CameraCharacteristics
import android.util.Range
+import android.view.Surface
import androidx.annotation.RequiresApi
import androidx.camera.camera2.pipe.UnsafeWrapper
import androidx.camera.camera2.pipe.integration.impl.CameraProperties
@@ -31,6 +33,7 @@
import androidx.camera.core.ExposureState
import androidx.camera.core.FocusMeteringAction
import androidx.camera.core.ZoomState
+import androidx.camera.core.impl.utils.CameraOrientationUtil
import androidx.lifecycle.LiveData
import kotlin.reflect.KClass
@@ -38,6 +41,9 @@
* Implementation of [CameraInfo] for physical camera. In comparison,
* [CameraInfoAdapter] is the version of logical camera.
*/
+@SuppressLint(
+ "UnsafeOptInUsageError" // Suppressed due to experimental API
+)
@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
class PhysicalCameraInfoAdapter(
private val cameraProperties: CameraProperties
@@ -48,12 +54,23 @@
Camera2CameraInfo.create(cameraProperties)
}
- override fun getSensorRotationDegrees(): Int {
- throw UnsupportedOperationException("Physical camera doesn't support this function")
- }
+ override fun getSensorRotationDegrees(): Int = getSensorRotationDegrees(Surface.ROTATION_0)
override fun getSensorRotationDegrees(relativeRotation: Int): Int {
- throw UnsupportedOperationException("Physical camera doesn't support this function")
+ val sensorOrientation: Int =
+ cameraProperties.metadata[CameraCharacteristics.SENSOR_ORIENTATION]!!
+ val relativeRotationDegrees =
+ CameraOrientationUtil.surfaceRotationToDegrees(relativeRotation)
+ // Currently this assumes that a back-facing camera is always opposite to the screen.
+ // This may not be the case for all devices, so in the future we may need to handle that
+ // scenario.
+ val lensFacing = lensFacing
+ val isOppositeFacingScreen = CameraSelector.LENS_FACING_BACK == lensFacing
+ return CameraOrientationUtil.getRelativeImageRotation(
+ relativeRotationDegrees,
+ sensorOrientation,
+ isOppositeFacingScreen
+ )
}
override fun hasFlashUnit(): Boolean {
@@ -84,9 +101,8 @@
throw UnsupportedOperationException("Physical camera doesn't support this function")
}
- override fun getLensFacing(): Int {
- throw UnsupportedOperationException("Physical camera doesn't support this function")
- }
+ override fun getLensFacing(): Int =
+ getCameraSelectorLensFacing(cameraProperties.metadata[CameraCharacteristics.LENS_FACING]!!)
override fun getIntrinsicZoomRatio(): Float {
throw UnsupportedOperationException("Physical camera doesn't support this function")
@@ -132,4 +148,16 @@
else -> cameraProperties.metadata.unwrapAs(type)
}
}
+
+ @CameraSelector.LensFacing
+ private fun getCameraSelectorLensFacing(lensFacingInt: Int): Int {
+ return when (lensFacingInt) {
+ CameraCharacteristics.LENS_FACING_FRONT -> CameraSelector.LENS_FACING_FRONT
+ CameraCharacteristics.LENS_FACING_BACK -> CameraSelector.LENS_FACING_BACK
+ CameraCharacteristics.LENS_FACING_EXTERNAL -> CameraSelector.LENS_FACING_EXTERNAL
+ else -> throw IllegalArgumentException(
+ "The specified lens facing integer $lensFacingInt can not be recognized."
+ )
+ }
+ }
}
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 4cf9a99..8ff0c5fc 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
@@ -332,6 +332,7 @@
override fun close() {
closed = true
+ debug { "UseCaseCameraRequestControl: closed" }
}
private fun failedResults(count: Int, message: String): List<Deferred<Void?>> =
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraState.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraState.kt
index 2fe0e04..6b52ee4 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraState.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraState.kt
@@ -34,6 +34,7 @@
import androidx.camera.camera2.pipe.RequestTemplate
import androidx.camera.camera2.pipe.StreamId
import androidx.camera.camera2.pipe.core.Log
+import androidx.camera.camera2.pipe.core.Log.debug
import androidx.camera.camera2.pipe.integration.config.UseCaseCameraScope
import androidx.camera.camera2.pipe.integration.config.UseCaseGraphConfig
import androidx.camera.core.Preview
@@ -209,6 +210,10 @@
) {
// TODO: Consider if this should detect changes and only invoke an update if state has
// actually changed.
+ debug {
+ "UseCaseCameraState#updateState: parameters = $parameters, internalParameters = " +
+ "$internalParameters, streams = $streams, template = $template"
+ }
if (parameters != null) {
if (!appendParameters) {
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/interop/Camera2CameraControl.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/interop/Camera2CameraControl.kt
index a733d72..a2e78d5 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/interop/Camera2CameraControl.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/interop/Camera2CameraControl.kt
@@ -31,6 +31,7 @@
import androidx.camera.core.impl.utils.futures.Futures
import androidx.core.util.Preconditions
import com.google.common.util.concurrent.ListenableFuture
+import kotlinx.coroutines.async
/**
* An class that provides ability to interoperate with the [android.hardware.camera2] APIs.
@@ -151,7 +152,9 @@
private fun updateAsync(tag: String): ListenableFuture<Void?> =
Futures.nonCancellationPropagating(
- compat.applyAsync(useCaseCamera).asListenableFuture(tag)
+ threads.sequentialScope.async {
+ compat.applyAsync(useCaseCamera).await()
+ }.asListenableFuture(tag)
)
companion object {
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2PhysicalCameraInfoImpl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2PhysicalCameraInfoImpl.java
index a0050b6..d28e299 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2PhysicalCameraInfoImpl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2PhysicalCameraInfoImpl.java
@@ -17,7 +17,9 @@
package androidx.camera.camera2.internal;
import android.annotation.SuppressLint;
+import android.hardware.camera2.CameraCharacteristics;
import android.util.Range;
+import android.view.Surface;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
@@ -34,6 +36,8 @@
import androidx.camera.core.ExposureState;
import androidx.camera.core.FocusMeteringAction;
import androidx.camera.core.ZoomState;
+import androidx.camera.core.impl.utils.CameraOrientationUtil;
+import androidx.core.util.Preconditions;
import androidx.lifecycle.LiveData;
import java.util.Set;
@@ -73,12 +77,23 @@
@Override
public int getSensorRotationDegrees() {
- throw new UnsupportedOperationException("Physical camera doesn't support this function");
+ return getSensorRotationDegrees(Surface.ROTATION_0);
}
@Override
public int getSensorRotationDegrees(int relativeRotation) {
- throw new UnsupportedOperationException("Physical camera doesn't support this function");
+ int sensorOrientation = getSensorOrientation();
+ int relativeRotationDegrees =
+ CameraOrientationUtil.surfaceRotationToDegrees(relativeRotation);
+ // Currently this assumes that a back-facing camera is always opposite to the screen.
+ // This may not be the case for all devices, so in the future we may need to handle that
+ // scenario.
+ final int lensFacing = getLensFacing();
+ boolean isOppositeFacingScreen = CameraSelector.LENS_FACING_BACK == lensFacing;
+ return CameraOrientationUtil.getRelativeImageRotation(
+ relativeRotationDegrees,
+ sensorOrientation,
+ isOppositeFacingScreen);
}
@Override
@@ -124,7 +139,10 @@
@Override
public int getLensFacing() {
- throw new UnsupportedOperationException("Physical camera doesn't support this function");
+ Integer lensFacing = mCameraCharacteristicsCompat.get(CameraCharacteristics.LENS_FACING);
+ Preconditions.checkArgument(lensFacing != null, "Unable to get the lens facing of the "
+ + "camera.");
+ return LensFacingUtil.getCameraSelectorLensFacing(lensFacing);
}
@Override
@@ -172,4 +190,11 @@
public Set<CameraInfo> getPhysicalCameraInfos() {
throw new UnsupportedOperationException("Physical camera doesn't support this function");
}
+
+ int getSensorOrientation() {
+ Integer sensorOrientation =
+ mCameraCharacteristicsCompat.get(CameraCharacteristics.SENSOR_ORIENTATION);
+ Preconditions.checkNotNull(sensorOrientation);
+ return sensorOrientation;
+ }
}
diff --git a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/viewpager/ViewPager2ActivityTest.kt b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/viewpager/ViewPager2ActivityTest.kt
index 39ad0ec..36209f1 100644
--- a/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/viewpager/ViewPager2ActivityTest.kt
+++ b/camera/integration-tests/uiwidgetstestapp/src/androidTest/java/androidx/camera/integration/uiwidgets/viewpager/ViewPager2ActivityTest.kt
@@ -19,6 +19,7 @@
import android.content.Context
import android.content.Intent
import android.graphics.SurfaceTexture
+import android.os.Build
import android.view.TextureView
import android.view.View
import androidx.camera.camera2.Camera2Config
@@ -28,6 +29,7 @@
import androidx.camera.integration.uiwidgets.viewpager.BaseActivity.Companion.COMPATIBLE_MODE
import androidx.camera.integration.uiwidgets.viewpager.BaseActivity.Companion.PERFORMANCE_MODE
import androidx.camera.lifecycle.ProcessCameraProvider
+import androidx.camera.testing.impl.AndroidUtil.isEmulator
import androidx.camera.testing.impl.CameraPipeConfigTestRule
import androidx.camera.testing.impl.CameraUtil
import androidx.camera.testing.impl.CoreAppTestUtil
@@ -41,7 +43,6 @@
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.filters.LargeTest
-import androidx.test.filters.SdkSuppress
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.google.common.truth.Truth.assertThat
@@ -52,6 +53,7 @@
import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Assume
+import org.junit.Assume.assumeFalse
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -157,9 +159,9 @@
}
// The test makes sure the TextureView surface texture keeps the same after switch.
- @SdkSuppress(maxSdkVersion = 33) // b/331933633
@Test
fun testPreviewViewUpdateAfterSwitch() {
+ assumeFalse(shouldSkipTest()) // b/331933633
launchActivity(lensFacing, cameraXConfig).use { scenario ->
// At first, check Preview in stream state
@@ -179,6 +181,15 @@
}
}
+ /**
+ * The testPreviewViewUpdateAfterSwitch test will run failed in API 34 emulator's front camera
+ * when using SurfaceView implementation. See b/331933633.
+ */
+ private fun shouldSkipTest() = isEmulator() &&
+ Build.VERSION.SDK_INT == Build.VERSION_CODES.UPSIDE_DOWN_CAKE &&
+ lensFacing == CameraSelector.LENS_FACING_FRONT &&
+ implementationMode == PERFORMANCE_MODE
+
@Test
fun testPreviewViewUpdateAfterSwitchAndStop_ResumeAndSwitchBack() {
launchActivity(lensFacing, cameraXConfig).use { scenario ->
diff --git a/car/app/app/src/main/java/androidx/car/app/model/AlertCallbackDelegateImpl.java b/car/app/app/src/main/java/androidx/car/app/model/AlertCallbackDelegateImpl.java
index 7b52a05..51d98d1 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/AlertCallbackDelegateImpl.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/AlertCallbackDelegateImpl.java
@@ -43,7 +43,7 @@
@NonNull
@SuppressLint("ExecutorRegistration")
- static AlertCallbackDelegate create(@NonNull AlertCallback callback) {
+ public static AlertCallbackDelegate create(@NonNull AlertCallback callback) {
return new AlertCallbackDelegateImpl(callback);
}
diff --git a/car/app/app/src/main/java/androidx/car/app/model/OnCheckedChangeDelegateImpl.java b/car/app/app/src/main/java/androidx/car/app/model/OnCheckedChangeDelegateImpl.java
index 3e831d4..74f08a3 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/OnCheckedChangeDelegateImpl.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/OnCheckedChangeDelegateImpl.java
@@ -66,7 +66,7 @@
@NonNull
// This listener relates to UI event and is expected to be triggered on the main thread.
@SuppressLint("ExecutorRegistration")
- static OnCheckedChangeDelegate create(@NonNull OnCheckedChangeListener listener) {
+ public static OnCheckedChangeDelegate create(@NonNull OnCheckedChangeListener listener) {
return new OnCheckedChangeDelegateImpl(listener);
}
diff --git a/car/app/app/src/main/java/androidx/car/app/model/OnClickDelegateImpl.java b/car/app/app/src/main/java/androidx/car/app/model/OnClickDelegateImpl.java
index f226308..239960b 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/OnClickDelegateImpl.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/OnClickDelegateImpl.java
@@ -64,7 +64,7 @@
@NonNull
// This listener relates to UI event and is expected to be triggered on the main thread.
@SuppressLint("ExecutorRegistration")
- static OnClickDelegate create(@NonNull OnClickListener listener) {
+ public static OnClickDelegate create(@NonNull OnClickListener listener) {
return new OnClickDelegateImpl(
listener,
listener instanceof ParkedOnlyOnClickListener);
diff --git a/car/app/app/src/main/java/androidx/car/app/model/OnItemVisibilityChangedDelegateImpl.java b/car/app/app/src/main/java/androidx/car/app/model/OnItemVisibilityChangedDelegateImpl.java
index 165c1fc..a515a3f 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/OnItemVisibilityChangedDelegateImpl.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/OnItemVisibilityChangedDelegateImpl.java
@@ -69,7 +69,7 @@
@NonNull
// This listener relates to UI event and is expected to be triggered on the main thread.
@SuppressLint("ExecutorRegistration")
- static OnItemVisibilityChangedDelegate create(
+ public static OnItemVisibilityChangedDelegate create(
@NonNull OnItemVisibilityChangedListener listener) {
return new OnItemVisibilityChangedDelegateImpl(listener);
}
diff --git a/car/app/app/src/main/java/androidx/car/app/model/OnSelectedDelegateImpl.java b/car/app/app/src/main/java/androidx/car/app/model/OnSelectedDelegateImpl.java
index 25cc745..ee9019b 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/OnSelectedDelegateImpl.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/OnSelectedDelegateImpl.java
@@ -64,7 +64,7 @@
@NonNull
// This listener relates to UI event and is expected to be triggered on the main thread.
@SuppressLint("ExecutorRegistration")
- static OnSelectedDelegate create(@NonNull OnSelectedListener listener) {
+ public static OnSelectedDelegate create(@NonNull OnSelectedListener listener) {
return new OnSelectedDelegateImpl(listener);
}
diff --git a/car/app/app/src/main/java/androidx/car/app/model/SearchCallbackDelegateImpl.java b/car/app/app/src/main/java/androidx/car/app/model/SearchCallbackDelegateImpl.java
index f4879e7..480eb48 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/SearchCallbackDelegateImpl.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/SearchCallbackDelegateImpl.java
@@ -78,7 +78,7 @@
@NonNull
// This listener relates to UI event and is expected to be triggered on the main thread.
@SuppressLint("ExecutorRegistration")
- static SearchCallbackDelegate create(@NonNull SearchCallback callback) {
+ public static SearchCallbackDelegate create(@NonNull SearchCallback callback) {
return new SearchCallbackDelegateImpl(callback);
}
diff --git a/car/app/app/src/main/java/androidx/car/app/model/TabCallbackDelegateImpl.java b/car/app/app/src/main/java/androidx/car/app/model/TabCallbackDelegateImpl.java
index 3951f74..458f929 100644
--- a/car/app/app/src/main/java/androidx/car/app/model/TabCallbackDelegateImpl.java
+++ b/car/app/app/src/main/java/androidx/car/app/model/TabCallbackDelegateImpl.java
@@ -66,7 +66,7 @@
@NonNull
// This listener relates to UI event and is expected to be triggered on the main thread.
@SuppressLint("ExecutorRegistration")
- static TabCallbackDelegate create(@NonNull TabTemplate.TabCallback callback) {
+ public static TabCallbackDelegate create(@NonNull TabTemplate.TabCallback callback) {
return new TabCallbackDelegateImpl(callback);
}
diff --git a/car/app/app/src/main/java/androidx/car/app/navigation/model/PanModeDelegateImpl.java b/car/app/app/src/main/java/androidx/car/app/navigation/model/PanModeDelegateImpl.java
index 72ef523..bf15b4d 100644
--- a/car/app/app/src/main/java/androidx/car/app/navigation/model/PanModeDelegateImpl.java
+++ b/car/app/app/src/main/java/androidx/car/app/navigation/model/PanModeDelegateImpl.java
@@ -65,7 +65,7 @@
@NonNull
// This listener relates to UI event and is expected to be triggered on the main thread.
@SuppressLint("ExecutorRegistration")
- static PanModeDelegate create(@NonNull PanModeListener listener) {
+ public static PanModeDelegate create(@NonNull PanModeListener listener) {
return new PanModeDelegateImpl(listener);
}
diff --git a/collection/collection/api/current.txt b/collection/collection/api/current.txt
index 61aef58..d9b4d13 100644
--- a/collection/collection/api/current.txt
+++ b/collection/collection/api/current.txt
@@ -115,6 +115,7 @@
method public final inline void forEachReversedIndexed(kotlin.jvm.functions.Function2<? super java.lang.Integer,? super java.lang.Double,kotlin.Unit> block);
method public final operator double get(@IntRange(from=0L) int index);
method public final inline kotlin.ranges.IntRange getIndices();
+ method public final double[] getInternalArray();
method @IntRange(from=-1L) public final inline int getLastIndex();
method @IntRange(from=0L) public final int getSize();
method public final int indexOf(double element);
@@ -140,6 +141,7 @@
method public final boolean none();
method public final inline boolean reversedAny(kotlin.jvm.functions.Function1<? super java.lang.Double,java.lang.Boolean> predicate);
property public final inline kotlin.ranges.IntRange indices;
+ property public final double[] internalArray;
property @IntRange(from=-1L) public final inline int lastIndex;
property @IntRange(from=0L) public final int size;
}
@@ -293,6 +295,7 @@
method public final inline void forEachReversedIndexed(kotlin.jvm.functions.Function2<? super java.lang.Integer,? super java.lang.Float,kotlin.Unit> block);
method public final operator float get(@IntRange(from=0L) int index);
method public final inline kotlin.ranges.IntRange getIndices();
+ method public final float[] getInternalArray();
method @IntRange(from=-1L) public final inline int getLastIndex();
method @IntRange(from=0L) public final int getSize();
method public final int indexOf(float element);
@@ -318,6 +321,7 @@
method public final boolean none();
method public final inline boolean reversedAny(kotlin.jvm.functions.Function1<? super java.lang.Float,java.lang.Boolean> predicate);
property public final inline kotlin.ranges.IntRange indices;
+ property public final float[] internalArray;
property @IntRange(from=-1L) public final inline int lastIndex;
property @IntRange(from=0L) public final int size;
}
@@ -620,6 +624,7 @@
method public final inline void forEachReversedIndexed(kotlin.jvm.functions.Function2<? super java.lang.Integer,? super java.lang.Integer,kotlin.Unit> block);
method public final operator int get(@IntRange(from=0L) int index);
method public final inline kotlin.ranges.IntRange getIndices();
+ method public final int[] getInternalArray();
method @IntRange(from=-1L) public final inline int getLastIndex();
method @IntRange(from=0L) public final int getSize();
method public final int indexOf(int element);
@@ -645,6 +650,7 @@
method public final boolean none();
method public final inline boolean reversedAny(kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Boolean> predicate);
property public final inline kotlin.ranges.IntRange indices;
+ property public final int[] internalArray;
property @IntRange(from=-1L) public final inline int lastIndex;
property @IntRange(from=0L) public final int size;
}
@@ -937,6 +943,7 @@
method public final inline void forEachReversedIndexed(kotlin.jvm.functions.Function2<? super java.lang.Integer,? super java.lang.Long,kotlin.Unit> block);
method public final operator long get(@IntRange(from=0L) int index);
method public final inline kotlin.ranges.IntRange getIndices();
+ method public final long[] getInternalArray();
method @IntRange(from=-1L) public final inline int getLastIndex();
method @IntRange(from=0L) public final int getSize();
method public final int indexOf(long element);
@@ -962,6 +969,7 @@
method public final boolean none();
method public final inline boolean reversedAny(kotlin.jvm.functions.Function1<? super java.lang.Long,java.lang.Boolean> predicate);
property public final inline kotlin.ranges.IntRange indices;
+ property public final long[] internalArray;
property @IntRange(from=-1L) public final inline int lastIndex;
property @IntRange(from=0L) public final int size;
}
diff --git a/collection/collection/api/restricted_current.txt b/collection/collection/api/restricted_current.txt
index 0c8d12c..9cded76 100644
--- a/collection/collection/api/restricted_current.txt
+++ b/collection/collection/api/restricted_current.txt
@@ -115,6 +115,7 @@
method public final inline void forEachReversedIndexed(kotlin.jvm.functions.Function2<? super java.lang.Integer,? super java.lang.Double,kotlin.Unit> block);
method public final operator double get(@IntRange(from=0L) int index);
method public final inline kotlin.ranges.IntRange getIndices();
+ method public final double[] getInternalArray();
method @IntRange(from=-1L) public final inline int getLastIndex();
method @IntRange(from=0L) public final int getSize();
method public final int indexOf(double element);
@@ -140,6 +141,7 @@
method public final boolean none();
method public final inline boolean reversedAny(kotlin.jvm.functions.Function1<? super java.lang.Double,java.lang.Boolean> predicate);
property public final inline kotlin.ranges.IntRange indices;
+ property public final double[] internalArray;
property @IntRange(from=-1L) public final inline int lastIndex;
property @IntRange(from=0L) public final int size;
field @kotlin.PublishedApi internal int _size;
@@ -305,6 +307,7 @@
method public final inline void forEachReversedIndexed(kotlin.jvm.functions.Function2<? super java.lang.Integer,? super java.lang.Float,kotlin.Unit> block);
method public final operator float get(@IntRange(from=0L) int index);
method public final inline kotlin.ranges.IntRange getIndices();
+ method public final float[] getInternalArray();
method @IntRange(from=-1L) public final inline int getLastIndex();
method @IntRange(from=0L) public final int getSize();
method public final int indexOf(float element);
@@ -330,6 +333,7 @@
method public final boolean none();
method public final inline boolean reversedAny(kotlin.jvm.functions.Function1<? super java.lang.Float,java.lang.Boolean> predicate);
property public final inline kotlin.ranges.IntRange indices;
+ property public final float[] internalArray;
property @IntRange(from=-1L) public final inline int lastIndex;
property @IntRange(from=0L) public final int size;
field @kotlin.PublishedApi internal int _size;
@@ -656,6 +660,7 @@
method public final inline void forEachReversedIndexed(kotlin.jvm.functions.Function2<? super java.lang.Integer,? super java.lang.Integer,kotlin.Unit> block);
method public final operator int get(@IntRange(from=0L) int index);
method public final inline kotlin.ranges.IntRange getIndices();
+ method public final int[] getInternalArray();
method @IntRange(from=-1L) public final inline int getLastIndex();
method @IntRange(from=0L) public final int getSize();
method public final int indexOf(int element);
@@ -681,6 +686,7 @@
method public final boolean none();
method public final inline boolean reversedAny(kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Boolean> predicate);
property public final inline kotlin.ranges.IntRange indices;
+ property public final int[] internalArray;
property @IntRange(from=-1L) public final inline int lastIndex;
property @IntRange(from=0L) public final int size;
field @kotlin.PublishedApi internal int _size;
@@ -997,6 +1003,7 @@
method public final inline void forEachReversedIndexed(kotlin.jvm.functions.Function2<? super java.lang.Integer,? super java.lang.Long,kotlin.Unit> block);
method public final operator long get(@IntRange(from=0L) int index);
method public final inline kotlin.ranges.IntRange getIndices();
+ method public final long[] getInternalArray();
method @IntRange(from=-1L) public final inline int getLastIndex();
method @IntRange(from=0L) public final int getSize();
method public final int indexOf(long element);
@@ -1022,6 +1029,7 @@
method public final boolean none();
method public final inline boolean reversedAny(kotlin.jvm.functions.Function1<? super java.lang.Long,java.lang.Boolean> predicate);
property public final inline kotlin.ranges.IntRange indices;
+ property public final long[] internalArray;
property @IntRange(from=-1L) public final inline int lastIndex;
property @IntRange(from=0L) public final int size;
field @kotlin.PublishedApi internal int _size;
diff --git a/collection/collection/src/commonMain/kotlin/androidx/collection/DoubleList.kt b/collection/collection/src/commonMain/kotlin/androidx/collection/DoubleList.kt
index 9ba38be..bd3c0da 100644
--- a/collection/collection/src/commonMain/kotlin/androidx/collection/DoubleList.kt
+++ b/collection/collection/src/commonMain/kotlin/androidx/collection/DoubleList.kt
@@ -34,7 +34,8 @@
/**
* [DoubleList] is a [List]-like collection for [Double] values. It allows retrieving
* the elements without boxing. [DoubleList] is always backed by a [MutableDoubleList],
- * its [MutableList]-like subclass.
+ * its [MutableList]-like subclass. The purpose of this class is to avoid the performance
+ * overhead of auto-boxing due to generics since [Collection] classes all operate on objects.
*
* This implementation is not thread-safe: if multiple threads access this
* container concurrently, and one or more threads modify the structure of
@@ -64,6 +65,18 @@
get() = _size
/**
+ * The current backing [DoubleArray] for the contents of [DoubleList].
+ *
+ * Modifying this array may affect the contents of the [DoubleList]. The values are stored in
+ * indices 0 to [lastIndex], but any values after [lastIndex] can be any value.
+ *
+ * This should only be used for highly-optimized code that needs direct access to the backing
+ * array.
+ */
+ public val internalArray: DoubleArray
+ get() = content
+
+ /**
* Returns the last valid index in the [DoubleList]. This can be `-1` when the list is empty.
*/
@get:androidx.annotation.IntRange(from = -1)
diff --git a/collection/collection/src/commonMain/kotlin/androidx/collection/FloatList.kt b/collection/collection/src/commonMain/kotlin/androidx/collection/FloatList.kt
index 391aa06..2591ca7 100644
--- a/collection/collection/src/commonMain/kotlin/androidx/collection/FloatList.kt
+++ b/collection/collection/src/commonMain/kotlin/androidx/collection/FloatList.kt
@@ -34,7 +34,8 @@
/**
* [FloatList] is a [List]-like collection for [Float] values. It allows retrieving
* the elements without boxing. [FloatList] is always backed by a [MutableFloatList],
- * its [MutableList]-like subclass.
+ * its [MutableList]-like subclass. The purpose of this class is to avoid the performance
+ * overhead of auto-boxing due to generics since [Collection] classes all operate on objects.
*
* This implementation is not thread-safe: if multiple threads access this
* container concurrently, and one or more threads modify the structure of
@@ -64,6 +65,18 @@
get() = _size
/**
+ * The current backing [FloatArray] for the contents of [FloatList].
+ *
+ * Modifying this array may affect the contents of the [FloatList]. The values are stored in
+ * indices 0 to [lastIndex], but any values after [lastIndex] can be any value.
+ *
+ * This should only be used for highly-optimized code that needs direct access to the backing
+ * array.
+ */
+ public val internalArray: FloatArray
+ get() = content
+
+ /**
* Returns the last valid index in the [FloatList]. This can be `-1` when the list is empty.
*/
@get:androidx.annotation.IntRange(from = -1)
diff --git a/collection/collection/src/commonMain/kotlin/androidx/collection/IntList.kt b/collection/collection/src/commonMain/kotlin/androidx/collection/IntList.kt
index 0709d1c..def7ff7 100644
--- a/collection/collection/src/commonMain/kotlin/androidx/collection/IntList.kt
+++ b/collection/collection/src/commonMain/kotlin/androidx/collection/IntList.kt
@@ -34,7 +34,8 @@
/**
* [IntList] is a [List]-like collection for [Int] values. It allows retrieving
* the elements without boxing. [IntList] is always backed by a [MutableIntList],
- * its [MutableList]-like subclass.
+ * its [MutableList]-like subclass. The purpose of this class is to avoid the performance
+ * overhead of auto-boxing due to generics since [Collection] classes all operate on objects.
*
* This implementation is not thread-safe: if multiple threads access this
* container concurrently, and one or more threads modify the structure of
@@ -64,6 +65,18 @@
get() = _size
/**
+ * The current backing [IntArray] for the contents of [IntList].
+ *
+ * Modifying this array may affect the contents of the [IntList]. The values are stored in
+ * indices 0 to [lastIndex], but any values after [lastIndex] can be any value.
+ *
+ * This should only be used for highly-optimized code that needs direct access to the backing
+ * array.
+ */
+ public val internalArray: IntArray
+ get() = content
+
+ /**
* Returns the last valid index in the [IntList]. This can be `-1` when the list is empty.
*/
@get:androidx.annotation.IntRange(from = -1)
diff --git a/collection/collection/src/commonMain/kotlin/androidx/collection/LongList.kt b/collection/collection/src/commonMain/kotlin/androidx/collection/LongList.kt
index 56e15d1..508b922 100644
--- a/collection/collection/src/commonMain/kotlin/androidx/collection/LongList.kt
+++ b/collection/collection/src/commonMain/kotlin/androidx/collection/LongList.kt
@@ -34,7 +34,8 @@
/**
* [LongList] is a [List]-like collection for [Long] values. It allows retrieving
* the elements without boxing. [LongList] is always backed by a [MutableLongList],
- * its [MutableList]-like subclass.
+ * its [MutableList]-like subclass. The purpose of this class is to avoid the performance
+ * overhead of auto-boxing due to generics since [Collection] classes all operate on objects.
*
* This implementation is not thread-safe: if multiple threads access this
* container concurrently, and one or more threads modify the structure of
@@ -64,6 +65,18 @@
get() = _size
/**
+ * The current backing [LongArray] for the contents of [LongList].
+ *
+ * Modifying this array may affect the contents of the [LongList]. The values are stored in
+ * indices 0 to [lastIndex], but any values after [lastIndex] can be any value.
+ *
+ * This should only be used for highly-optimized code that needs direct access to the backing
+ * array.
+ */
+ public val internalArray: LongArray
+ get() = content
+
+ /**
* Returns the last valid index in the [LongList]. This can be `-1` when the list is empty.
*/
@get:androidx.annotation.IntRange(from = -1)
diff --git a/collection/collection/template/PKeyList.kt.template b/collection/collection/template/PKeyList.kt.template
index 1bf836e..62fa9f7 100644
--- a/collection/collection/template/PKeyList.kt.template
+++ b/collection/collection/template/PKeyList.kt.template
@@ -34,7 +34,8 @@
/**
* [PKeyList] is a [List]-like collection for [PKey] values. It allows retrieving
* the elements without boxing. [PKeyList] is always backed by a [MutablePKeyList],
- * its [MutableList]-like subclass.
+ * its [MutableList]-like subclass. The purpose of this class is to avoid the performance
+ * overhead of auto-boxing due to generics since [Collection] classes all operate on objects.
*
* This implementation is not thread-safe: if multiple threads access this
* container concurrently, and one or more threads modify the structure of
@@ -64,6 +65,18 @@
get() = _size
/**
+ * The current backing [PKeyArray] for the contents of [PKeyList].
+ *
+ * Modifying this array may affect the contents of the [PKeyList]. The values are stored in
+ * indices 0 to [lastIndex], but any values after [lastIndex] can be any value.
+ *
+ * This should only be used for highly-optimized code that needs direct access to the backing
+ * array.
+ */
+ public val internalArray: PKeyArray
+ get() = content
+
+ /**
* Returns the last valid index in the [PKeyList]. This can be `-1` when the list is empty.
*/
@get:androidx.annotation.IntRange(from = -1)
diff --git a/compose/foundation/foundation/api/current.txt b/compose/foundation/foundation/api/current.txt
index 012b6e4..e026507 100644
--- a/compose/foundation/foundation/api/current.txt
+++ b/compose/foundation/foundation/api/current.txt
@@ -362,29 +362,6 @@
}
-package androidx.compose.foundation.contextmenu {
-
- @androidx.compose.runtime.Stable public final class ContextMenuColors {
- ctor public ContextMenuColors(long backgroundColor, long textColor, long iconColor, long disabledTextColor, long disabledIconColor);
- method public long getBackgroundColor();
- method public long getDisabledIconColor();
- method public long getDisabledTextColor();
- method public long getIconColor();
- method public long getTextColor();
- property public final long backgroundColor;
- property public final long disabledIconColor;
- property public final long disabledTextColor;
- property public final long iconColor;
- property public final long textColor;
- }
-
- public final class ContextMenuUiKt {
- method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.foundation.contextmenu.ContextMenuColors?> getLocalContextMenuColors();
- property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.foundation.contextmenu.ContextMenuColors?> LocalContextMenuColors;
- }
-
-}
-
package androidx.compose.foundation.draganddrop {
public final class AndroidDragAndDropSource_androidKt {
@@ -1685,12 +1662,12 @@
package androidx.compose.foundation.text.handwriting {
- public final class HandwritingDelegate_androidKt {
- method public static androidx.compose.ui.Modifier handwritingDelegate(androidx.compose.ui.Modifier);
+ public final class HandwritingDetector_androidKt {
+ method public static androidx.compose.ui.Modifier handwritingDetector(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function0<kotlin.Unit> callback);
}
- public final class HandwritingDelegator_androidKt {
- method public static androidx.compose.ui.Modifier handwritingDelegator(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function0<kotlin.Unit> callback);
+ public final class HandwritingHandler_androidKt {
+ method public static androidx.compose.ui.Modifier handwritingHandler(androidx.compose.ui.Modifier);
}
}
diff --git a/compose/foundation/foundation/api/restricted_current.txt b/compose/foundation/foundation/api/restricted_current.txt
index 684d597..cc020da 100644
--- a/compose/foundation/foundation/api/restricted_current.txt
+++ b/compose/foundation/foundation/api/restricted_current.txt
@@ -364,29 +364,6 @@
}
-package androidx.compose.foundation.contextmenu {
-
- @androidx.compose.runtime.Stable public final class ContextMenuColors {
- ctor public ContextMenuColors(long backgroundColor, long textColor, long iconColor, long disabledTextColor, long disabledIconColor);
- method public long getBackgroundColor();
- method public long getDisabledIconColor();
- method public long getDisabledTextColor();
- method public long getIconColor();
- method public long getTextColor();
- property public final long backgroundColor;
- property public final long disabledIconColor;
- property public final long disabledTextColor;
- property public final long iconColor;
- property public final long textColor;
- }
-
- public final class ContextMenuUiKt {
- method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.foundation.contextmenu.ContextMenuColors?> getLocalContextMenuColors();
- property public static final androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.foundation.contextmenu.ContextMenuColors?> LocalContextMenuColors;
- }
-
-}
-
package androidx.compose.foundation.draganddrop {
public final class AndroidDragAndDropSource_androidKt {
@@ -1687,12 +1664,12 @@
package androidx.compose.foundation.text.handwriting {
- public final class HandwritingDelegate_androidKt {
- method public static androidx.compose.ui.Modifier handwritingDelegate(androidx.compose.ui.Modifier);
+ public final class HandwritingDetector_androidKt {
+ method public static androidx.compose.ui.Modifier handwritingDetector(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function0<kotlin.Unit> callback);
}
- public final class HandwritingDelegator_androidKt {
- method public static androidx.compose.ui.Modifier handwritingDelegator(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function0<kotlin.Unit> callback);
+ public final class HandwritingHandler_androidKt {
+ method public static androidx.compose.ui.Modifier handwritingHandler(androidx.compose.ui.Modifier);
}
}
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/TextDemos.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/TextDemos.kt
index 27b3971..c294a13 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/TextDemos.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/TextDemos.kt
@@ -25,7 +25,6 @@
import androidx.compose.foundation.demos.text2.BasicTextFieldOutputTransformationDemos
import androidx.compose.foundation.demos.text2.BasicTextFieldValueCallbackDemo
import androidx.compose.foundation.demos.text2.DecorationBoxDemos
-import androidx.compose.foundation.demos.text2.HandwritingDelegationDemo
import androidx.compose.foundation.demos.text2.KeyboardActionsDemos
import androidx.compose.foundation.demos.text2.KeyboardOptionsDemos
import androidx.compose.foundation.demos.text2.NestedReceiveContentDemo
@@ -36,6 +35,7 @@
import androidx.compose.foundation.demos.text2.TextFieldLineLimitsDemos
import androidx.compose.foundation.demos.text2.TextFieldReceiveContentDemo
import androidx.compose.foundation.samples.BasicTextFieldUndoSample
+import androidx.compose.foundation.samples.HandwritingDetectorSample
import androidx.compose.integration.demos.common.ComposableDemo
import androidx.compose.integration.demos.common.DemoCategory
import androidx.compose.ui.text.samples.AnnotatedStringFromHtml
@@ -158,7 +158,6 @@
"Text Input (BasicTextFieldv2)",
listOf(
ComposableDemo("Basic text input") { BasicTextFieldDemos() },
- ComposableDemo("Handwriting delegation") { HandwritingDelegationDemo() },
ComposableDemo("Value/callback overload") { BasicTextFieldValueCallbackDemo() },
ComposableDemo("Keyboard Options") { KeyboardOptionsDemos() },
ComposableDemo("Keyboard Actions") { KeyboardActionsDemos() },
@@ -182,7 +181,8 @@
ComposableDemo("Custom PIN field") { BasicTextFieldCustomPinFieldDemo() },
ComposableDemo("Undo/Redo") { BasicTextFieldUndoSample() },
ComposableDemo("Long text") { BasicTextFieldLongTextDemo() },
- ComposableDemo("Cursor") { TextFieldCursorNotBlinkingInUnfocusedWindowDemo() }
+ ComposableDemo("Cursor") { TextFieldCursorNotBlinkingInUnfocusedWindowDemo() },
+ ComposableDemo("Handwriting detector") { HandwritingDetectorSample() }
)
),
DemoCategory(
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/DecorationBoxDemos.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/DecorationBoxDemos.kt
index 0a9044e..07f07d2 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/DecorationBoxDemos.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/DecorationBoxDemos.kt
@@ -90,14 +90,14 @@
}
@Composable
-fun OutlinedBasicTextField(modifier: Modifier = Modifier) {
+fun OutlinedBasicTextField() {
val state = remember { TextFieldState() }
val cursorColor by TextFieldDefaults
.outlinedTextFieldColors()
.cursorColor(isError = false)
BasicTextField(
state = state,
- modifier = modifier.fillMaxWidth(),
+ modifier = Modifier.fillMaxWidth(),
textStyle = LocalTextStyle.current.copy(color = LocalContentColor.current),
cursorBrush = SolidColor(cursorColor),
lineLimits = TextFieldLineLimits.MultiLine(1, 3),
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/HandwritingDelegationDemo.kt b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/HandwritingDetectorSample.kt
similarity index 73%
rename from compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/HandwritingDelegationDemo.kt
rename to compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/HandwritingDetectorSample.kt
index a7b773c..1b329ff 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/HandwritingDelegationDemo.kt
+++ b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/HandwritingDetectorSample.kt
@@ -14,13 +14,12 @@
* limitations under the License.
*/
-@file:OptIn(ExperimentalFoundationApi::class, ExperimentalMaterialApi::class)
+package androidx.compose.foundation.samples
-package androidx.compose.foundation.demos.text2
-
-import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.annotation.Sampled
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
@@ -31,12 +30,13 @@
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.foundation.text.handwriting.handwritingDelegate
-import androidx.compose.foundation.text.handwriting.handwritingDelegator
+import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.foundation.text.handwriting.handwritingDetector
+import androidx.compose.foundation.text.handwriting.handwritingHandler
+import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Card
import androidx.compose.material.ContentAlpha
-import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
@@ -54,8 +54,9 @@
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
+@Sampled
@Composable
-fun HandwritingDelegationDemo() {
+fun HandwritingDetectorSample() {
var openDialog by remember { mutableStateOf(false) }
val focusRequester = remember { FocusRequester() }
@@ -68,14 +69,14 @@
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
- "This is not an actual text field, but it is a handwriting delegator so you can use " +
+ "This is not an actual text field, but it is a handwriting detector so you can use " +
"a stylus to write here."
)
Spacer(Modifier.size(16.dp))
Text("Fake text field",
Modifier
.fillMaxWidth()
- .handwritingDelegator { openDialog = !openDialog }
+ .handwritingDetector { openDialog = !openDialog }
.padding(4.dp)
.border(
1.dp,
@@ -96,13 +97,29 @@
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
- Text("This text field is a handwriting delegate.")
+ Text("This text field is a handwriting handler.")
Spacer(Modifier.size(16.dp))
- OutlinedBasicTextField(
- Modifier
+ val state = remember { TextFieldState() }
+ BasicTextField(
+ state = state,
+ modifier = Modifier
.fillMaxWidth()
.focusRequester(focusRequester)
- .handwritingDelegate()
+ .handwritingHandler(),
+ decorator = { innerTextField ->
+ Box(
+ Modifier
+ .padding(4.dp)
+ .border(
+ 1.dp,
+ MaterialTheme.colors.onSurface,
+ RoundedCornerShape(4.dp)
+ )
+ .padding(16.dp)
+ ) {
+ innerTextField()
+ }
+ }
)
}
}
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/contextmenu/ContextMenuCommon.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/contextmenu/ContextMenuCommon.kt
index 458ae84..386d59f 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/contextmenu/ContextMenuCommon.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/contextmenu/ContextMenuCommon.kt
@@ -17,13 +17,9 @@
package androidx.compose.foundation.contextmenu
import androidx.compose.foundation.contextmenu.ContextMenuState.Status
-import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.graphics.Color
-import com.google.common.truth.Correspondence
import com.google.common.truth.FailureMetadata
-import com.google.common.truth.IterableSubject
import com.google.common.truth.Subject
import com.google.common.truth.Subject.Factory
import com.google.common.truth.Truth.assertAbout
@@ -36,15 +32,11 @@
internal fun ContextMenuScope.testItem(
label: String = "Item",
modifier: Modifier = Modifier,
- enabled: Boolean = true,
- leadingIcon: @Composable ((iconColor: Color) -> Unit)? = null,
onClick: () -> Unit = {},
) {
item(
label = label,
modifier = modifier,
- enabled = enabled,
- leadingIcon = leadingIcon,
onClick = onClick,
)
}
@@ -71,23 +63,3 @@
assertThat(subject.status).isInstanceOf(Status.Closed::class.java)
}
}
-
-internal fun assertThatColors(
- colors: Set<Color>,
- tolerance: Double = 0.02,
-): IterableSubject.UsingCorrespondence<Color, Color> =
- assertThat(colors).comparingElementsUsing(colorCorrespondence(tolerance))
-
-internal fun colorCorrespondence(tolerance: Double = 0.02): Correspondence<Color, Color> {
- val floatingPointCorrespondence = Correspondence.tolerance(tolerance)
- return Correspondence.from(
- { actual: Color?, expected: Color? ->
- if (expected == null || actual == null) return@from actual == expected
- floatingPointCorrespondence.compare(actual.red, expected.red) &&
- floatingPointCorrespondence.compare(actual.green, expected.green) &&
- floatingPointCorrespondence.compare(actual.blue, expected.blue) &&
- floatingPointCorrespondence.compare(actual.alpha, expected.alpha)
- },
- /* description = */ "equals",
- )
-}
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/contextmenu/ContextMenuUiTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/contextmenu/ContextMenuUiTest.kt
index 1d8b970..99fd744 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/contextmenu/ContextMenuUiTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/contextmenu/ContextMenuUiTest.kt
@@ -17,7 +17,6 @@
package androidx.compose.foundation.contextmenu
import android.os.Build
-import androidx.annotation.RequiresApi
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.IntrinsicSize
@@ -28,13 +27,14 @@
import androidx.compose.foundation.text.ceilToIntPx
import androidx.compose.foundation.text.selection.fetchTextLayoutResult
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.testutils.assertIsEqualTo
+import androidx.compose.testutils.assertPixelColor
import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toPixelMap
@@ -75,14 +75,6 @@
private fun IntOffset.dx(dx: Int): IntOffset = copy(x = x + dx)
private fun IntOffset.dy(dy: Int): IntOffset = copy(y = y + dy)
-private val TestColors = ContextMenuColors(
- backgroundColor = Color.White,
- textColor = Color.Black,
- iconColor = Color.Blue,
- disabledTextColor = Color.Red,
- disabledIconColor = Color.Green,
-)
-
@RunWith(AndroidJUnit4::class)
@MediumTest
class ContextMenuUiTest {
@@ -92,27 +84,11 @@
private val tag = "testTag"
private val longText = "M ".repeat(200).trimEnd()
- @Composable
- private fun TestColumn(
- colors: ContextMenuColors = TestColors,
- contextMenuBuilderBlock: ContextMenuScope.() -> Unit,
- ) {
- ContextMenuColumn(colors, Modifier.testTag(tag)) {
- val scope = remember { ContextMenuScope() }
- with(scope) {
- clear()
- contextMenuBuilderBlock()
- Content(colors)
- }
- }
- }
-
// region ContextMenuItem Tests
@Composable
private fun TestItem(
label: String = "Item",
enabled: Boolean = true,
- colors: ContextMenuColors = TestColors,
modifier: Modifier = Modifier.testTag(tag),
leadingIcon: @Composable ((iconColor: Color) -> Unit)? = null,
onClick: () -> Unit = {},
@@ -120,7 +96,6 @@
ContextMenuItem(
label = label,
enabled = enabled,
- colors = colors,
modifier = modifier,
leadingIcon = leadingIcon,
onClick = onClick
@@ -306,12 +281,8 @@
textNode.assertExists("Text does not exist.")
val textStyle = textNode.fetchTextLayoutResult().layoutInput.style
- assertThat(textStyle.color).isEqualTo(TestColors.textColor)
+ assertThat(textStyle.color).isEqualTo(ContextMenuSpec.TextColor)
assertThat(textStyle.textAlign).isEqualTo(ContextMenuSpec.LabelHorizontalTextAlignment)
- assertThat(textStyle.fontSize).isEqualTo(ContextMenuSpec.FontSize)
- assertThat(textStyle.fontWeight).isEqualTo(ContextMenuSpec.FontWeight)
- assertThat(textStyle.lineHeight).isEqualTo(ContextMenuSpec.LineHeight)
- assertThat(textStyle.letterSpacing).isEqualTo(ContextMenuSpec.LetterSpacing)
}
@Test
@@ -322,19 +293,15 @@
textNode.assertExists("Text does not exist.")
val textStyle = textNode.fetchTextLayoutResult().layoutInput.style
- assertThat(textStyle.color).isEqualTo(TestColors.disabledTextColor)
+ assertThat(textStyle.color).isEqualTo(ContextMenuSpec.DisabledColor)
assertThat(textStyle.textAlign).isEqualTo(ContextMenuSpec.LabelHorizontalTextAlignment)
- assertThat(textStyle.fontSize).isEqualTo(ContextMenuSpec.FontSize)
- assertThat(textStyle.fontWeight).isEqualTo(ContextMenuSpec.FontWeight)
- assertThat(textStyle.lineHeight).isEqualTo(ContextMenuSpec.LineHeight)
- assertThat(textStyle.letterSpacing).isEqualTo(ContextMenuSpec.LetterSpacing)
}
@Test
fun whenContextMenuItem_enabled_correctIconColor() {
var iconColor: Color? = null
rule.setContent { TestItem(label = longText, leadingIcon = { iconColor = it }) }
- assertThat(iconColor).isEqualTo(TestColors.iconColor)
+ assertThat(iconColor).isEqualTo(ContextMenuSpec.IconColor)
}
@Test
@@ -343,11 +310,25 @@
rule.setContent {
TestItem(label = longText, enabled = false, leadingIcon = { iconColor = it })
}
- assertThat(iconColor).isEqualTo(TestColors.disabledIconColor)
+ assertThat(iconColor).isEqualTo(ContextMenuSpec.DisabledColor)
}
// endregion ContextMenuItem Tests
// region ContextMenuColumn Tests
+ @Composable
+ private fun TestColumn(
+ contextMenuBuilderBlock: ContextMenuScope.() -> Unit,
+ ) {
+ ContextMenuColumn(Modifier.testTag(tag)) {
+ val scope = remember { ContextMenuScope() }
+ with(scope) {
+ clear()
+ contextMenuBuilderBlock()
+ Content()
+ }
+ }
+ }
+
@Test
fun whenContextMenuColumn_usedNormally_allInputItemsAreRendered() {
val labelFunction: (Int) -> String = { "Item $it" }
@@ -363,6 +344,42 @@
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
@Test
+ fun whenContextMenuColumn_usedNormally_backgroundColorIsAsExpected() {
+ rule.setContent {
+ TestColumn {
+ // small label to keep text out of the way
+ testItem(
+ label = "Item",
+ modifier = Modifier.drawWithContent {
+ // Don't draw content, only the column will draw.
+ // Layout will still fill space.
+ }
+ )
+ }
+ }
+
+ val node = rule.onNodeWithTag(tag)
+ val bitmap = node.captureToImage()
+ val density = node.fetchSemanticsNode().layoutInfo.density
+
+ // Ignore some padding around the edges where the shadow/rounded corners are.
+ val padding = with(density) { ContextMenuSpec.CornerRadius.toPx().ceilToIntPx() }
+ val pixelMap = bitmap.toPixelMap(
+ startX = padding,
+ startY = padding,
+ width = bitmap.width - padding * 2,
+ height = bitmap.height - padding * 2,
+ )
+
+ for (x in 0 until pixelMap.width) {
+ for (y in 0 until pixelMap.height) {
+ pixelMap.assertPixelColor(ContextMenuSpec.BackgroundColor, x, y)
+ }
+ }
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+ @Test
fun whenContextMenuColumn_usedNormally_hasExpectedShadow() {
val containerTag = "containerTag"
rule.setContent {
@@ -371,7 +388,10 @@
.testTag(containerTag)
.padding(ContextMenuSpec.MenuContainerElevation)
) {
- TestColumn { testItem() }
+ TestColumn {
+ // small label to keep text out of the way
+ testItem(label = ".")
+ }
}
}
@@ -388,8 +408,9 @@
fun assertShadowAt(offset: IntOffset) {
val (x, y) = offset
val pixelColor = pixelMap[x, y]
- val message = "Expected pixel at [$x, $y] to not be ${TestColors.backgroundColor}."
- assertWithMessage(message).that(pixelColor).isNotEqualTo(TestColors.backgroundColor)
+ val message = "Expected pixel at [$x, $y] to not be ${ContextMenuSpec.BackgroundColor}."
+ assertWithMessage(message)
+ .that(pixelColor).isNotEqualTo(ContextMenuSpec.BackgroundColor)
}
assertShadowAt(columnBoundsInParent.centerLeft.dx(-1))
@@ -502,178 +523,6 @@
}
// endregion ContextMenuColumn Tests
- // region ContextMenuColor Tests
- private val testColor = Color.Red
-
- /** Without a baseline background color, the background color is non-deterministic. */
- private val testBackgroundColor = Color.White
-
- private fun transparentColors(
- backgroundColor: Color = testBackgroundColor,
- textColor: Color = Color.Transparent,
- iconColor: Color = Color.Transparent,
- disabledTextColor: Color = Color.Transparent,
- disabledIconColor: Color = Color.Transparent,
- ): ContextMenuColors = ContextMenuColors(
- backgroundColor = backgroundColor,
- textColor = textColor,
- iconColor = iconColor,
- disabledTextColor = disabledTextColor,
- disabledIconColor = disabledIconColor,
- )
-
- /**
- * Threshold % of pixels that must match a certain color.
- * This filters out outlier colors resulting from anti-aliasing.
- */
- private val filterThresholdPercentage = 0.02f
-
- /**
- * Gets all the colors in the tagged node.
- * Removes any padding and outlier colors from anti-aliasing from the result.
- */
- @RequiresApi(Build.VERSION_CODES.O)
- private fun getColors(): Set<Color> {
- val node = rule.onNodeWithTag(tag)
- val bitmap = node.captureToImage()
- val density = node.fetchSemanticsNode().layoutInfo.density
-
- // Ignore some padding around the edges where the shadow/rounded corners are.
- val padding = with(density) {
- ContextMenuSpec.CornerRadius.toPx().times(4f).ceilToIntPx()
- }
-
- val pixelMap = bitmap.toPixelMap(
- startX = padding,
- startY = padding,
- width = bitmap.width - padding,
- height = bitmap.height - padding,
- )
-
- val pixelColorHistogram = buildMap<Color, Int> {
- for (x in 0 until pixelMap.width) {
- for (y in 0 until pixelMap.height) {
- val pixelColor = pixelMap[x, y]
- merge(pixelColor, 1) { old, new -> old + new }
- }
- }
- }
-
- val threshold = pixelMap.width * pixelMap.height * filterThresholdPercentage
- return pixelColorHistogram.filterValues { it >= threshold }.keys
- }
-
- @RequiresApi(Build.VERSION_CODES.O)
- private fun getNonBackgroundColors(): Set<Color> = getColors() - testBackgroundColor
-
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
- @Test
- fun whenContextMenuColors_transparentExceptBackground_backgroundColorIsAsExpected() {
- val colors = transparentColors(backgroundColor = testColor)
- rule.setContent { TestColumn(colors) { testItem() } }
- assertThatColors(getColors()).containsExactly(testColor)
- }
-
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
- @Test
- fun whenContextMenuColors_transparentExceptText_textColorIsAsExpected() {
- val colors = transparentColors(textColor = testColor)
- rule.setContent { TestColumn(colors) { testItem(label = "M".repeat(10)) } }
- assertThatColors(getNonBackgroundColors()).containsExactly(testColor)
- }
-
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
- @Test
- fun whenContextMenuColors_transparentExceptDisabledText_textColorIsAsExpected() {
- val colors = transparentColors(disabledTextColor = testColor)
- rule.setContent {
- TestColumn(colors) { testItem(label = "M".repeat(10), enabled = false) }
- }
- assertThatColors(getNonBackgroundColors()).containsExactly(testColor)
- }
-
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
- @Test
- fun whenContextMenuColors_transparentExceptIcon_iconColorIsAsExpected() {
- val colors = transparentColors(iconColor = testColor)
- rule.setContent {
- TestColumn(colors) {
- testItem(
- leadingIcon = { iconColor ->
- Box(
- Modifier
- .background(iconColor)
- .fillMaxSize()
- )
- },
- )
- }
- }
- assertThatColors(getNonBackgroundColors()).containsExactly(testColor)
- }
-
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
- @Test
- fun whenContextMenuColors_transparentExceptDisabledIcon_iconColorIsAsExpected() {
- val colors = transparentColors(disabledIconColor = testColor)
- rule.setContent {
- TestColumn(colors) {
- testItem(
- enabled = false,
- leadingIcon = { iconColor ->
- Box(
- Modifier
- .background(iconColor)
- .fillMaxSize()
- )
- },
- )
- }
- }
- assertThatColors(getNonBackgroundColors()).containsExactly(testColor)
- }
-
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
- @Test
- fun whenContextMenuColors_enabledToggled_colorsChangeAsExpected() {
- val enabledColor = Color.Red
- val disabledColor = Color.Blue
- val colors = transparentColors(
- textColor = enabledColor,
- iconColor = enabledColor,
- disabledTextColor = disabledColor,
- disabledIconColor = disabledColor,
- )
-
- var enabled by mutableStateOf(true)
- rule.setContent {
- TestColumn(colors) {
- testItem(
- label = "M".repeat(5),
- enabled = enabled,
- leadingIcon = { iconColor ->
- Box(
- Modifier
- .background(iconColor)
- .fillMaxSize()
- )
- },
- )
- }
- }
-
- repeat(2) {
- enabled = true
- rule.waitForIdle()
- assertThatColors(getNonBackgroundColors()).containsExactly(enabledColor)
-
- enabled = false
- rule.waitForIdle()
- assertThatColors(getNonBackgroundColors()).containsExactly(disabledColor)
- }
- }
- // endregion ContextMenuColor Tests
-
// region ContextMenuPopup Tests
private val centeringPopupPositionProvider = object : PopupPositionProvider {
override fun calculatePosition(
@@ -714,50 +563,5 @@
rule.onNodeWithTag(itemTag).assertExists()
}
}
-
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
- @Test
- fun whenContextMenuPopup_enabledDisabled_colorsAreAsExpected() {
- var enabled by mutableStateOf(true)
- rule.setContent {
- CompositionLocalProvider(LocalContextMenuColors provides TestColors) {
- ContextMenuPopup(
- modifier = Modifier.testTag(tag),
- popupPositionProvider = centeringPopupPositionProvider,
- onDismiss = {},
- ) {
- testItem(
- label = "M".repeat(10),
- enabled = enabled,
- leadingIcon = { iconColor ->
- Box(
- Modifier
- .background(iconColor)
- .fillMaxSize()
- )
- },
- )
- }
- }
- }
-
- repeat(2) {
- enabled = true
- rule.waitForIdle()
- assertThatColors(getColors()).containsExactly(
- TestColors.backgroundColor,
- TestColors.textColor,
- TestColors.iconColor
- )
-
- enabled = false
- rule.waitForIdle()
- assertThatColors(getColors()).containsExactly(
- TestColors.backgroundColor,
- TestColors.disabledTextColor,
- TestColors.disabledIconColor
- )
- }
- }
// endregion ContextMenuPopup Tests
}
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/HandwritingDelegatorTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/HandwritingDetectorTest.kt
similarity index 89%
rename from compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/HandwritingDelegatorTest.kt
rename to compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/HandwritingDetectorTest.kt
index b8a7814..056ac9c 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/HandwritingDelegatorTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/HandwritingDetectorTest.kt
@@ -18,7 +18,7 @@
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.text.handwriting.handwritingDelegator
+import androidx.compose.foundation.text.handwriting.handwritingDetector
import androidx.compose.foundation.text.handwriting.isStylusHandwritingSupported
import androidx.compose.foundation.text.performStylusClick
import androidx.compose.foundation.text.performStylusHandwriting
@@ -39,7 +39,7 @@
@MediumTest
@RunWith(AndroidJUnit4::class)
-internal class HandwritingDelegatorTest {
+internal class HandwritingDetectorTest {
@get:Rule
val rule = createComposeRule()
@@ -48,7 +48,7 @@
private val imm = FakeInputMethodManager()
- private val tag = "delegator"
+ private val tag = "detector"
private var callbackCount = 0;
@@ -65,35 +65,35 @@
Spacer(
modifier = Modifier
.fillMaxSize()
- .handwritingDelegator { callbackCount++ }
+ .handwritingDetector { callbackCount++ }
.testTag(tag)
)
}
}
@Test
- fun delegator_handwriting_preparesDelegation() {
+ fun detector_handwriting_preparesDelegation() {
rule.onNodeWithTag(tag).performStylusHandwriting()
assertHandwritingDelegationPrepared()
}
@Test
- fun delegator_click_notPreparesDelegation() {
+ fun detector_click_notPreparesDelegation() {
rule.onNodeWithTag(tag).performStylusClick()
assertHandwritingDelegationNotPrepared()
}
@Test
- fun delegator_longClick_notPreparesDelegation() {
+ fun detector_longClick_notPreparesDelegation() {
rule.onNodeWithTag(tag).performStylusLongClick()
assertHandwritingDelegationNotPrepared()
}
@Test
- fun delegator_longPressAndDrag_notPreparesDelegation() {
+ fun detector_longPressAndDrag_notPreparesDelegation() {
rule.onNodeWithTag(tag).performStylusLongPressAndDrag()
assertHandwritingDelegationNotPrepared()
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/HandwritingDelegateTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/HandwritingHandlerTest.kt
similarity index 88%
rename from compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/HandwritingDelegateTest.kt
rename to compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/HandwritingHandlerTest.kt
index a40a175..62e72ca 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/HandwritingDelegateTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/HandwritingHandlerTest.kt
@@ -18,7 +18,7 @@
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.text.BasicTextField
-import androidx.compose.foundation.text.handwriting.handwritingDelegate
+import androidx.compose.foundation.text.handwriting.handwritingHandler
import androidx.compose.foundation.text.handwriting.isStylusHandwritingSupported
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
@@ -37,7 +37,7 @@
@MediumTest
@RunWith(AndroidJUnit4::class)
-internal class HandwritingDelegateTest {
+internal class HandwritingHandlerTest {
@get:Rule
val rule = createComposeRule()
@@ -51,16 +51,16 @@
}
@Test
- fun delegate_gainFocus_acceptsDelegation() {
+ fun handler_gainFocus_acceptsDelegation() {
val imm = FakeInputMethodManager()
immRule.setFactory { imm }
- val tag = "delegate"
+ val tag = "handler"
InputMethodInterceptor(rule).setTextFieldTestContent {
val state = remember { TextFieldState() }
BasicTextField(
state = state,
- modifier = Modifier.fillMaxSize().handwritingDelegate().testTag(tag)
+ modifier = Modifier.fillMaxSize().handwritingHandler().testTag(tag)
)
}
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldLongPressTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldLongPressTest.kt
index 509edf6..9495032 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldLongPressTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldLongPressTest.kt
@@ -16,7 +16,6 @@
package androidx.compose.foundation.text.input.internal.selection
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
@@ -41,6 +40,8 @@
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsFocused
+import androidx.compose.ui.test.assertIsNotDisplayed
+import androidx.compose.ui.test.assertIsNotFocused
import androidx.compose.ui.test.click
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.longClick
@@ -63,7 +64,6 @@
/**
* Tests for long click interactions on BasicTextField.
*/
-@OptIn(ExperimentalFoundationApi::class)
@LargeTest
class TextFieldLongPressTest : FocusedWindowTest {
@@ -92,8 +92,8 @@
}
@Test
- fun longPress_requestsFocus_beforePointerIsReleased() {
- val state = TextFieldState("Hello, World!")
+ fun longPress_doesNotRequestsFocus_beforePointerIsReleased() {
+ val state = TextFieldState("abc def ghi")
rule.setTextFieldTestContent {
BasicTextField(
state = state,
@@ -106,9 +106,34 @@
longPress(center)
}
+ rule.onNodeWithTag(TAG).assertIsNotFocused()
+ rule.onNode(isSelectionHandle(Handle.SelectionStart)).assertIsNotDisplayed()
+ rule.onNode(isSelectionHandle(Handle.SelectionEnd)).assertIsNotDisplayed()
+ assertThat(state.selection).isEqualTo(TextRange(4, 7))
+ }
+
+ @Test
+ fun longPress_requestsFocus_afterPointerIsReleased() {
+ val state = TextFieldState("abc def ghi")
+ rule.setTextFieldTestContent {
+ BasicTextField(
+ state = state,
+ textStyle = defaultTextStyle,
+ modifier = Modifier.testTag(TAG)
+ )
+ }
+
+ rule.onNodeWithTag(TAG).performTouchInput {
+ longPress(center)
+ }
+
+ rule.onNodeWithTag(TAG).assertIsNotFocused()
+
+ rule.onNodeWithTag(TAG).performTouchInput {
+ up()
+ }
+
rule.onNodeWithTag(TAG).assertIsFocused()
- rule.onNode(isSelectionHandle(Handle.SelectionStart)).assertIsDisplayed()
- rule.onNode(isSelectionHandle(Handle.SelectionEnd)).assertIsDisplayed()
}
@Test
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldMagnifierTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldMagnifierTest.kt
index b04d52d..28d2264 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldMagnifierTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldMagnifierTest.kt
@@ -21,9 +21,11 @@
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.Handle
@@ -35,6 +37,8 @@
import androidx.compose.foundation.text.selection.AbstractSelectionMagnifierTests
import androidx.compose.foundation.text.selection.assertMagnifierExists
import androidx.compose.foundation.text.selection.assertNoMagnifierExists
+import androidx.compose.foundation.text.selection.assertThatOffset
+import androidx.compose.foundation.text.selection.gestures.util.longPress
import androidx.compose.foundation.text.selection.getMagnifierCenterOffset
import androidx.compose.foundation.text.selection.isSelectionHandle
import androidx.compose.runtime.Composable
@@ -44,6 +48,7 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.geometry.isUnspecified
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalLayoutDirection
@@ -65,6 +70,7 @@
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.unit.toSize
+import androidx.compose.ui.util.lerp
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import androidx.test.filters.SdkSuppress
@@ -74,7 +80,6 @@
import org.junit.Test
import org.junit.runner.RunWith
-@OptIn(ExperimentalFoundationApi::class)
@MediumTest
@SdkSuppress(minSdkVersion = 28)
@RunWith(AndroidJUnit4::class)
@@ -361,6 +366,129 @@
)
}
+ // regression - When dragging to the final empty line, the magnifier appeared on the second
+ // to last line instead of on the final line. It should appear on the final line.
+ // This test is moved up from abstract test class `AbstractSelectionMagnifierTests` because
+ // the long press behavior is different between the new BasicTextField and the legacy one.
+ @Test
+ fun textField_magnifier_centeredOnCorrectLine_whenLinesAreEmpty() {
+ lateinit var textLayout: TextLayoutResult
+ rule.setTextFieldTestContent {
+ Content(
+ text = "a\n\n",
+ modifier = Modifier
+ // Center the text to give the magnifier lots of room to move.
+ .fillMaxSize()
+ .wrapContentSize()
+ .testTag(tag),
+ onTextLayout = { textLayout = it }
+ )
+ }
+
+ rule.waitForIdle()
+
+ val firstPressOffset = textLayout.getBoundingBox(0).centerLeft + Offset(1f, 0f)
+
+ val placedOffset = rule.onNodeWithTag(tag).fetchSemanticsNode().boundsInRoot.topLeft
+
+ fun assertMagnifierAt(expected: Offset) {
+ rule.waitForIdle()
+ val actual = getMagnifierCenterOffset(rule, requireSpecified = true) - placedOffset
+ assertThatOffset(actual).equalsWithTolerance(expected)
+ }
+
+ // start selection at first character
+ rule.onNodeWithTag(tag).performTouchInput {
+ longPress(firstPressOffset)
+ }
+ assertMagnifierAt(firstPressOffset)
+
+ fun getOffsetAtLine(line: Int): Offset = Offset(
+ x = firstPressOffset.x,
+ y = lerp(
+ start = textLayout.getLineTop(lineIndex = line),
+ stop = textLayout.getLineBottom(lineIndex = line),
+ fraction = 0.5f
+ )
+ )
+
+ val secondOffset = getOffsetAtLine(1)
+ rule.onNodeWithTag(tag).performTouchInput {
+ moveTo(secondOffset)
+ }
+ assertMagnifierAt(Offset(0f, secondOffset.y))
+
+ val thirdOffset = getOffsetAtLine(2)
+ rule.onNodeWithTag(tag).performTouchInput {
+ moveTo(thirdOffset)
+ }
+ assertMagnifierAt(Offset(0f, thirdOffset.y))
+ }
+
+ // Regression - magnifier should be constrained to end of line in BiDi,
+ // not the last offset which could be in middle of the line.
+ // This test is moved up from abstract test class `AbstractSelectionMagnifierTests` because
+ // the long press behavior is different between the new BasicTextField and the legacy one.
+ @Test
+ fun textField_magnifier_centeredToEndOfLine_whenBidiEndOffsetInMiddleOfLine() {
+ val ltrWord = "hello"
+ val rtlWord = "בבבבב"
+
+ lateinit var textLayout: TextLayoutResult
+ rule.setTextFieldTestContent {
+ Content(
+ text = """
+ $rtlWord $ltrWord
+ $ltrWord $rtlWord
+ $rtlWord $ltrWord
+ """.trimIndent().trim(),
+ modifier = Modifier
+ // Center the text to give the magnifier lots of room to move.
+ .fillMaxSize()
+ .wrapContentHeight()
+ .testTag(tag),
+ onTextLayout = { textLayout = it }
+ )
+ }
+
+ val placedPosition = rule.onNodeWithTag(tag).fetchSemanticsNode().positionInRoot
+
+ fun getCenterForLine(line: Int): Float {
+ val top = textLayout.getLineTop(line)
+ val bottom = textLayout.getLineBottom(line)
+ return (bottom - top) / 2 + top
+ }
+
+ val farRightX = rule.onNodeWithTag(tag).fetchSemanticsNode().boundsInRoot.right - 1f
+
+ rule.onNodeWithTag(tag).performTouchInput {
+ longPress(Offset(farRightX, getCenterForLine(0)))
+ }
+ rule.waitForIdle()
+ Truth.assertWithMessage("Magnifier should not be shown")
+ .that(getMagnifierCenterOffset(rule).isUnspecified)
+ .isTrue()
+
+ val secondLineCenterY = getCenterForLine(1)
+ val secondOffset = Offset(farRightX, secondLineCenterY)
+ rule.onNodeWithTag(tag).performTouchInput {
+ moveTo(secondOffset)
+ }
+ rule.waitForIdle()
+ Truth.assertWithMessage("Magnifier should not be shown")
+ .that(getMagnifierCenterOffset(rule).isUnspecified)
+ .isTrue()
+
+ val lineRightX = textLayout.getLineRight(1)
+ val thirdOffset = Offset(lineRightX + 1f, secondLineCenterY)
+ rule.onNodeWithTag(tag).performTouchInput {
+ moveTo(thirdOffset)
+ }
+ rule.waitForIdle()
+ val actual = getMagnifierCenterOffset(rule, requireSpecified = true) - placedPosition
+ assertThatOffset(actual).equalsWithTolerance(Offset(lineRightX, secondLineCenterY))
+ }
+
@OptIn(ExperimentalTestApi::class, ExperimentalFoundationApi::class)
private fun checkMagnifierStayAtEndWhenDraggedBeyondScroll(
handle: Handle,
@@ -427,6 +555,56 @@
)
}
+ /**
+ * BasicTextField(state) has a different long press behavior compared to the legacy
+ * `BasicTextField`. We need to override this helper function to make sure that we are testing
+ * it correctly.
+ */
+ override fun checkMagnifierShowsDuringInitialLongPressDrag(
+ expandForwards: Boolean,
+ layoutDirection: LayoutDirection
+ ) {
+ val dragDistance = Offset(10f, 0f)
+ val dragDirection = if (expandForwards) 1f else -1f
+ rule.setTextFieldTestContent {
+ Content(
+ if (layoutDirection == LayoutDirection.Ltr) {
+ "aaaa aaaa aaaa"
+ } else {
+ "באמת באמת באמת"
+ },
+ Modifier
+ // Center the text to give the magnifier lots of room to move.
+ .fillMaxSize()
+ .wrapContentSize()
+ .testTag(tag)
+ )
+ }
+
+ // Initiate selection.
+ rule.onNodeWithTag(tag)
+ .performTouchInput {
+ down(center)
+ moveBy(Offset.Zero, delayMillis = viewConfiguration.longPressTimeoutMillis + 100)
+ }
+
+ // Magnifier should show after long-press starts.
+ val magnifierInitialPosition = getMagnifierCenterOffset(rule, requireSpecified = true)
+
+ // Drag horizontally - the magnifier should follow.
+ rule.onNodeWithTag(tag)
+ .performTouchInput {
+ // Don't need to worry about touch slop for this test since the drag starts as soon
+ // as the long click is detected.
+ moveBy(dragDistance * dragDirection)
+ }
+
+ // make the assertion without sending an `up` event which would cause an input session
+ // to start and keyboard to show up.
+ Truth.assertThat(getMagnifierCenterOffset(rule))
+ .isEqualTo(magnifierInitialPosition + (dragDistance * dragDirection))
+ }
+
private fun setupDragAndDropContent(): View {
val state = TextFieldState(
"aaaa",
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/selection/AbstractSelectionMagnifierTests.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/selection/AbstractSelectionMagnifierTests.kt
index 6e9b2df..bb1bbcd 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/selection/AbstractSelectionMagnifierTests.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/text/selection/AbstractSelectionMagnifierTests.kt
@@ -388,7 +388,7 @@
// Abstract composable functions can't have default parameters.
@Composable
- private fun Content(
+ protected fun Content(
text: String,
modifier: Modifier,
style: TextStyle = TextStyle.Default,
@@ -442,7 +442,7 @@
assertNoMagnifierExists(rule)
}
- protected fun checkMagnifierShowsDuringInitialLongPressDrag(
+ protected open fun checkMagnifierShowsDuringInitialLongPressDrag(
expandForwards: Boolean,
layoutDirection: LayoutDirection = LayoutDirection.Ltr
) {
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/contextmenu/ContextMenuArea.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/contextmenu/ContextMenuArea.android.kt
index d585346..bb0792d 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/contextmenu/ContextMenuArea.android.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/contextmenu/ContextMenuArea.android.kt
@@ -22,12 +22,7 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
-import androidx.compose.ui.unit.IntOffset
-import androidx.compose.ui.unit.IntRect
-import androidx.compose.ui.unit.IntSize
-import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.round
-import androidx.compose.ui.window.PopupPositionProvider
/**
* Wraps [content] with the necessary components to show a context menu in it.
@@ -67,7 +62,9 @@
val status = state.status
if (status !is Status.Open) return
- val popupPositionProvider = remember(status) { StaticPositionProvider(status.offset.round()) }
+ val popupPositionProvider = remember(status) {
+ ContextMenuPopupPositionProvider(status.offset.round())
+ }
ContextMenuPopup(
modifier = modifier,
@@ -76,18 +73,3 @@
contextMenuBuilderBlock = contextMenuBuilderBlock,
)
}
-
-// TODO(b/331958453) Create a smarter provider that will handle
-// moving the menu outside of the click position when
-// the menu is in the bottom right corner.
-private class StaticPositionProvider(
- // The position should be local to the layout the popup is attached to.
- private val localPosition: IntOffset,
-) : PopupPositionProvider {
- override fun calculatePosition(
- anchorBounds: IntRect,
- windowSize: IntSize,
- layoutDirection: LayoutDirection,
- popupContentSize: IntSize
- ): IntOffset = anchorBounds.topLeft + localPosition
-}
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/contextmenu/ContextMenuPopupPositionProvider.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/contextmenu/ContextMenuPopupPositionProvider.android.kt
new file mode 100644
index 0000000..82dbe14
--- /dev/null
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/contextmenu/ContextMenuPopupPositionProvider.android.kt
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.contextmenu
+
+import androidx.annotation.VisibleForTesting
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.IntRect
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.window.PopupPositionProvider
+
+/**
+ * A [PopupPositionProvider] which places the context menu fully within the window,
+ * aligning a corner of the popup menu with the cursor. For each x/y dimension it will:
+ *
+ * * In cases where the popup length is too small for the window length, it aligns the
+ * start edges of the popup and the window.
+ * * In cases where there is enough length between the click position and the end of
+ * the window for entire popup length, it will align the start edge of the popup with the
+ * [localPosition].
+ * * In cases where there is not enough length between the click position and the end of
+ * the window for entire popup length, but there is between the click position and the
+ * start of the window, it will align the end edge of the popup with the [localPosition].
+ * * In the final case: the window length is wide enough for the popup, but there isn't enough
+ * length on either side of click position to fit an edge of the popup with the click position.
+ * It will align the end edges of the popup and window.
+ *
+ * @param localPosition The [IntOffset] to align to. This should be in the same coordinates that
+ * the `Popup` is anchored to.
+ */
+internal class ContextMenuPopupPositionProvider(
+ private val localPosition: IntOffset,
+) : PopupPositionProvider {
+ // TODO(b/256233441) anchorBounds should be positioned within the window that
+ // windowSize is derived from. However, it seems that windowSize's
+ // bounds do not include the top decoration, while the window anchorBounds
+ // is derived from does include the top decoration. This causes the
+ // resulting calculation to be off when approaching the bottom of the screen.
+ override fun calculatePosition(
+ anchorBounds: IntRect,
+ windowSize: IntSize,
+ layoutDirection: LayoutDirection,
+ popupContentSize: IntSize
+ ): IntOffset = IntOffset(
+ x = alignPopupAxis(
+ position = anchorBounds.left + localPosition.x,
+ popupLength = popupContentSize.width,
+ windowLength = windowSize.width,
+ closeAffinity = layoutDirection == LayoutDirection.Ltr
+ ),
+ y = alignPopupAxis(
+ position = anchorBounds.top + localPosition.y,
+ popupLength = popupContentSize.height,
+ windowLength = windowSize.height,
+ )
+ )
+}
+
+/**
+ * Align the popup to the position along an axis.
+ *
+ * @param position The position to align to along the window's axis
+ * @param popupLength The length of the popup along this axis
+ * @param windowLength The length of the window along this axis
+ * @param closeAffinity Whether the start side is the close edge (`0`)
+ * or the far edge ([windowLength])
+ * @return the coordinate along this axis that best aligns the popup to the position
+ */
+@VisibleForTesting
+internal fun alignPopupAxis(
+ position: Int,
+ popupLength: Int,
+ windowLength: Int,
+ closeAffinity: Boolean = true,
+): Int = when {
+ popupLength >= windowLength -> alignStartEdges(popupLength, windowLength, closeAffinity)
+
+ popupFitsBetweenPositionAndEndEdge(position, popupLength, windowLength, closeAffinity) ->
+ alignPopupStartEdgeToPosition(position, popupLength, closeAffinity)
+
+ popupFitsBetweenPositionAndStartEdge(position, popupLength, windowLength, closeAffinity) ->
+ alignPopupEndEdgeToPosition(position, popupLength, closeAffinity)
+
+ else -> alignEndEdges(popupLength, windowLength, closeAffinity)
+}
+
+private fun popupFitsBetweenPositionAndStartEdge(
+ position: Int,
+ popupLength: Int,
+ windowLength: Int,
+ closeAffinity: Boolean,
+): Boolean = if (closeAffinity) {
+ popupLength <= position
+} else {
+ windowLength - popupLength > position
+}
+
+private fun popupFitsBetweenPositionAndEndEdge(
+ position: Int,
+ popupLength: Int,
+ windowLength: Int,
+ closeAffinity: Boolean,
+): Boolean =
+ popupFitsBetweenPositionAndStartEdge(position, popupLength, windowLength, !closeAffinity)
+
+private fun alignPopupStartEdgeToPosition(
+ position: Int,
+ popupLength: Int,
+ closeAffinity: Boolean,
+): Int = if (closeAffinity) position else position - popupLength
+
+private fun alignPopupEndEdgeToPosition(
+ position: Int,
+ popupLength: Int,
+ closeAffinity: Boolean,
+): Int = alignPopupStartEdgeToPosition(position, popupLength, !closeAffinity)
+
+private fun alignStartEdges(popupLength: Int, windowLength: Int, closeAffinity: Boolean): Int =
+ if (closeAffinity) 0 else windowLength - popupLength
+
+private fun alignEndEdges(popupLength: Int, windowLength: Int, closeAffinity: Boolean): Int =
+ alignStartEdges(popupLength, windowLength, !closeAffinity)
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/contextmenu/ContextMenuUi.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/contextmenu/ContextMenuUi.android.kt
index 1f20fdd..0056666 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/contextmenu/ContextMenuUi.android.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/contextmenu/ContextMenuUi.android.kt
@@ -36,28 +36,33 @@
import androidx.compose.foundation.text.BasicText
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.font.FontWeight.Companion.Medium
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.sp
import androidx.compose.ui.util.fastForEach
import androidx.compose.ui.window.Popup
import androidx.compose.ui.window.PopupPositionProvider
import androidx.compose.ui.window.PopupProperties
-/**
- * Layout constants from the [Material 3 Menu Spec](https://m3.material.io/components/menus/specs).
- */
+private const val DisabledAlpha = 0.38f
+private const val IconAlpha = 0.6f
+
@VisibleForTesting
internal object ContextMenuSpec {
- // dimensions
+ // TODO(b/331955999) Determine colors/theming
+ private val PrimaryColor = Color.Black
+ val BackgroundColor = Color.White
+
+ val TextColor = PrimaryColor
+ val IconColor = PrimaryColor.copy(alpha = IconAlpha)
+ val DisabledColor = PrimaryColor.copy(DisabledAlpha)
+
+ // Layout constants from https://m3.material.io/components/menus/specs
val ContainerWidthMin = 112.dp
val ContainerWidthMax = 280.dp
val ListItemHeight = 48.dp
@@ -68,20 +73,6 @@
val HorizontalPadding = 12.dp // left/right of column and between elements in rows
val VerticalPadding = 8.dp // top/bottom of column and around dividers
val IconSize = 24.dp
-
- // text
- val FontSize = 14.sp
- val FontWeight = Medium
- val LineHeight = 20.sp
- val LetterSpacing = 0.1f.sp
- fun textStyle(color: Color): TextStyle = TextStyle(
- color = color,
- textAlign = LabelHorizontalTextAlignment,
- fontSize = FontSize,
- fontWeight = FontWeight,
- lineHeight = LineHeight,
- letterSpacing = LetterSpacing,
- )
}
private val DefaultPopupProperties = PopupProperties(focusable = true)
@@ -98,13 +89,12 @@
onDismissRequest = onDismiss,
properties = DefaultPopupProperties,
) {
- val colors = LocalContextMenuColors.current ?: DefaultContextMenuColors
- ContextMenuColumn(colors, modifier) {
+ ContextMenuColumn(modifier) {
val scope = remember { ContextMenuScope() }
with(scope) {
clear()
contextMenuBuilderBlock()
- Content(colors)
+ Content()
}
}
}
@@ -113,7 +103,6 @@
@VisibleForTesting
@Composable
internal fun ContextMenuColumn(
- colors: ContextMenuColors,
modifier: Modifier = Modifier,
content: @Composable ColumnScope.() -> Unit,
) {
@@ -123,7 +112,7 @@
ContextMenuSpec.MenuContainerElevation,
RoundedCornerShape(ContextMenuSpec.CornerRadius)
)
- .background(colors.backgroundColor)
+ .background(ContextMenuSpec.BackgroundColor)
.width(IntrinsicSize.Max)
.padding(vertical = ContextMenuSpec.VerticalPadding)
.verticalScroll(rememberScrollState()),
@@ -138,7 +127,6 @@
internal fun ContextMenuItem(
label: String,
enabled: Boolean,
- colors: ContextMenuColors,
modifier: Modifier = Modifier,
/**
* Icon to place in front of the label. If null, the icon will not be rendered
@@ -182,12 +170,13 @@
maxWidth = ContextMenuSpec.IconSize,
maxHeight = ContextMenuSpec.IconSize,
)
- ) { icon(if (enabled) colors.iconColor else colors.disabledIconColor) }
+ ) { icon(if (enabled) ContextMenuSpec.IconColor else ContextMenuSpec.DisabledColor) }
}
BasicText(
text = label,
- style = ContextMenuSpec.textStyle(
- color = if (enabled) colors.textColor else colors.disabledTextColor,
+ style = TextStyle(
+ color = if (enabled) ContextMenuSpec.TextColor else ContextMenuSpec.DisabledColor,
+ textAlign = ContextMenuSpec.LabelHorizontalTextAlignment,
),
maxLines = 1,
modifier = Modifier.weight(1f, fill = true)
@@ -202,11 +191,11 @@
// arbitrary composables into a context menu. Instead, we expose this API which then maps to
// context menu composables.
internal class ContextMenuScope internal constructor() {
- private val composables = mutableStateListOf<@Composable (colors: ContextMenuColors) -> Unit>()
+ private val composables = mutableListOf<@Composable () -> Unit>()
@Composable
- internal fun Content(colors: ContextMenuColors) {
- composables.fastForEach { composable -> composable(colors) }
+ internal fun Content() {
+ composables.fastForEach { composable -> composable() }
}
internal fun clear() {
@@ -234,8 +223,7 @@
/**
* Icon to place in front of the label. If null, the icon will not be rendered
* and the text will instead be further towards the start. The `iconColor` will
- * change based on whether the item is disabled or not. The size of this composable
- * will be [ContextMenuSpec.IconSize].
+ * change based on whether the item is disabled or not.
*/
leadingIcon: @Composable ((iconColor: Color) -> Unit)? = null,
/**
@@ -247,26 +235,14 @@
onClick: () -> Unit,
) {
check(label.isNotBlank()) { "Label must not be blank" }
- composables += { colors ->
+ composables += {
ContextMenuItem(
modifier = modifier,
label = label,
enabled = enabled,
- colors = colors,
leadingIcon = leadingIcon,
onClick = onClick
)
}
}
}
-
-private const val DisabledAlpha = 0.38f
-private const val IconAlpha = 0.6f
-
-private val DefaultContextMenuColors = ContextMenuColors(
- backgroundColor = Color.White,
- textColor = Color.Black,
- iconColor = Color.Black.copy(alpha = IconAlpha),
- disabledTextColor = Color.Black.copy(alpha = DisabledAlpha),
- disabledIconColor = Color.Black.copy(alpha = DisabledAlpha),
-)
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/handwriting/HandwritingDelegator.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/handwriting/HandwritingDetector.android.kt
similarity index 74%
rename from compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/handwriting/HandwritingDelegator.android.kt
rename to compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/handwriting/HandwritingDetector.android.kt
index 4545e0a..6b69713 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/handwriting/HandwritingDelegator.android.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/handwriting/HandwritingDetector.android.kt
@@ -30,17 +30,17 @@
import androidx.compose.ui.unit.IntSize
/**
- * Configures an element to act as a handwriting delegator which detects stylus handwriting and
- * delegates handling of the recognised text to a handwriting delegate.
+ * Configures an element to act as a handwriting detector which detects stylus handwriting and
+ * delegates handling of the recognised text to another element.
*
* Stylus movement on the element will start a handwriting session, and trigger the [callback]. The
- * [callback] implementation is expected to show and focus a handwriting delegate element which can
- * handle the recognized text from the handwriting session.
+ * [callback] implementation is expected to show and focus a text input field with a
+ * [handwritingHandler] modifier which can handle the recognized text from the handwriting session.
*
* A common use case is a component which looks like a text input field but does not actually
* support text input itself, and clicking on this fake text input field causes a real text input
* field to be shown. To support handwriting initiation in this case, this modifier can be applied
- * to the fake text input field to configure it as a delegator, and a [handwritingDelegate] modifier
+ * to the fake text input field to configure it as a detector, and a [handwritingHandler] modifier
* can be applied to the real text input field. The [callback] implementation is typically the same
* as the `onClick` implementation for the fake text field's [clickable] modifier, which shows and
* focuses the real text input field.
@@ -49,31 +49,33 @@
* is not supported.
*
* @param callback a callback which will be triggered when stylus handwriting is detected
+ *
+ * @sample androidx.compose.foundation.samples.HandwritingDetectorSample
*/
-fun Modifier.handwritingDelegator(callback: () -> Unit) =
- if (isStylusHandwritingSupported) then(HandwritingDelegatorElement(callback)) else this
+fun Modifier.handwritingDetector(callback: () -> Unit) =
+ if (isStylusHandwritingSupported) then(HandwritingDetectorElement(callback)) else this
-private class HandwritingDelegatorElement(
+private class HandwritingDetectorElement(
private val callback: () -> Unit
-) : ModifierNodeElement<HandwritingDelegatorNode>() {
- override fun create() = HandwritingDelegatorNode(callback)
+) : ModifierNodeElement<HandwritingDetectorNode>() {
+ override fun create() = HandwritingDetectorNode(callback)
- override fun update(node: HandwritingDelegatorNode) {
+ override fun update(node: HandwritingDetectorNode) {
node.callback = callback
}
override fun hashCode() = 31 * callback.hashCode()
override fun equals(other: Any?) =
- (this === other) or ((other is HandwritingDelegatorElement) && callback === other.callback)
+ (this === other) or ((other is HandwritingDetectorElement) && callback === other.callback)
override fun InspectorInfo.inspectableProperties() {
- name = "handwritingDelegator"
+ name = "handwritingDetector"
properties["callback"] = callback
}
}
-private class HandwritingDelegatorNode(var callback: () -> Unit) : DelegatingNode(),
+private class HandwritingDetectorNode(var callback: () -> Unit) : DelegatingNode(),
PointerInputModifierNode {
private val composeImm by lazy(LazyThreadSafetyMode.NONE) {
ComposeInputMethodManager(requireView())
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/handwriting/HandwritingDelegate.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/handwriting/HandwritingHandler.android.kt
similarity index 71%
rename from compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/handwriting/HandwritingDelegate.android.kt
rename to compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/handwriting/HandwritingHandler.android.kt
index 7534743..912e9d5 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/handwriting/HandwritingDelegate.android.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/handwriting/HandwritingHandler.android.kt
@@ -26,42 +26,44 @@
import androidx.compose.ui.platform.InspectorInfo
/**
- * Configures an element to act as a stylus handwriting delegate which can handle text input from a
- * handwriting session which was triggered by stylus handwriting on a handwriting delegator.
+ * Configures an element to act as a stylus handwriting handler which can handle text input from a
+ * handwriting session which was triggered by stylus handwriting on a handwriting detector.
*
* When this element gains focus, if there is an ongoing stylus handwriting delegation which was
- * triggered by stylus handwriting on a handwriting delegator, this element will receive text input
+ * triggered by stylus handwriting on a handwriting detector, this element will receive text input
* from the handwriting session via its input connection.
*
* A common use case is a component which looks like a text input field but does not actually
* support text input itself, and clicking on this fake text input field causes a real text input
- * field to be shown. To support handwriting initiation in this case, a [handwritingDelegator]
- * modifier can be applied to the fake text input field to configure it as a delegator, and this
+ * field to be shown. To support handwriting initiation in this case, a [handwritingDetector]
+ * modifier can be applied to the fake text input field to configure it as a detector, and this
* modifier can be applied to the real text input field. The `callback` implementation for the fake
- * text field's [handwritingDelegator] modifier is typically the same as the `onClick`
+ * text field's [handwritingDetector] modifier is typically the same as the `onClick`
* implementation its [clickable] modifier, which shows and focuses the real text input field.
*
* This function returns a no-op modifier on API levels below Android U (34) as stylus handwriting
* is not supported.
+ *
+ * @sample androidx.compose.foundation.samples.HandwritingDetectorSample
*/
-fun Modifier.handwritingDelegate(): Modifier =
- if (isStylusHandwritingSupported) then(HandwritingDelegateElement()) else this
+fun Modifier.handwritingHandler(): Modifier =
+ if (isStylusHandwritingSupported) then(HandwritingHandlerElement()) else this
-private class HandwritingDelegateElement : ModifierNodeElement<HandwritingDelegateNode>() {
- override fun create() = HandwritingDelegateNode()
+private class HandwritingHandlerElement : ModifierNodeElement<HandwritingHandlerNode>() {
+ override fun create() = HandwritingHandlerNode()
- override fun update(node: HandwritingDelegateNode) {}
+ override fun update(node: HandwritingHandlerNode) {}
override fun hashCode() = 0
- override fun equals(other: Any?) = other is HandwritingDelegateElement
+ override fun equals(other: Any?) = other is HandwritingHandlerElement
override fun InspectorInfo.inspectableProperties() {
- name = "handwritingDelegate"
+ name = "handwritingHandler"
}
}
-private class HandwritingDelegateNode : FocusEventModifierNode, Modifier.Node() {
+private class HandwritingHandlerNode : FocusEventModifierNode, Modifier.Node() {
private var focusState: FocusState? = null
private val composeImm by lazy(LazyThreadSafetyMode.NONE) {
ComposeInputMethodManager(requireView())
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/selection/AndroidTextFieldMagnifier.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/selection/AndroidTextFieldMagnifier.android.kt
index 16bcb5e..75ed1d8 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/selection/AndroidTextFieldMagnifier.android.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/input/internal/selection/AndroidTextFieldMagnifier.android.kt
@@ -29,6 +29,7 @@
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
+import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.isSpecified
import androidx.compose.ui.graphics.drawscope.ContentDrawScope
import androidx.compose.ui.layout.LayoutCoordinates
@@ -108,12 +109,18 @@
private fun restartAnimationJob() {
animationJob?.cancel()
animationJob = null
- // never start an expensive animation job if magnifier is not explicitly set to be visible
- // or magnifier is not supported.
- if (!visible || !isPlatformMagnifierSupported()) return
+ // never start an expensive animation job if magnifier is not supported.
+ if (!isPlatformMagnifierSupported()) return
animationJob = coroutineScope.launch {
val animationScope = this
snapshotFlow {
+ // Although `visible` is not backed by snapshot state, TextFieldMagnifierNode is
+ // responsible for calling `restartAnimationJob` everytime the value of `visible`
+ // changes. So we don't have to worry about whether snapshotFlow invalidates for
+ // `visible`.
+ if (!visible && !textFieldSelectionState.isBeingLongPressed) {
+ return@snapshotFlow Offset.Unspecified
+ }
calculateSelectionMagnifierCenterAndroid(
textFieldState,
textFieldSelectionState,
diff --git a/compose/foundation/foundation/src/androidUnitTest/kotlin/androidx/compose/foundation/contextmenu/ContextMenuPopupPositionProviderTest.kt b/compose/foundation/foundation/src/androidUnitTest/kotlin/androidx/compose/foundation/contextmenu/ContextMenuPopupPositionProviderTest.kt
new file mode 100644
index 0000000..e762655
--- /dev/null
+++ b/compose/foundation/foundation/src/androidUnitTest/kotlin/androidx/compose/foundation/contextmenu/ContextMenuPopupPositionProviderTest.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.compose.foundation.contextmenu
+
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.IntRect
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.LayoutDirection
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+class ContextMenuPopupPositionProviderTest {
+ //region ContextMenuPopupPositionProvider Tests
+ @Test
+ fun calculatePosition_onlyFitsInXDirection() {
+ val layoutDirection = LayoutDirection.Ltr
+ val windowSize = IntSize(width = 100, height = 100)
+ val anchorBounds = IntRect(left = 10, top = 20, right = 90, bottom = 80)
+ val position = IntOffset(x = 40, y = 30) // 50, 50 when translated to window
+ val popupContentSize = IntSize(width = 10, height = 200)
+
+ val subject = ContextMenuPopupPositionProvider(position)
+ val actual =
+ subject.calculatePosition(anchorBounds, windowSize, layoutDirection, popupContentSize)
+
+ assertThat(actual).isEqualTo(IntOffset(50, 0))
+ }
+
+ @Test
+ fun calculatePosition_onlyFitsInYDirection() {
+ val layoutDirection = LayoutDirection.Ltr
+ val windowSize = IntSize(width = 100, height = 100)
+ val anchorBounds = IntRect(left = 10, top = 20, right = 90, bottom = 80)
+ val position = IntOffset(x = 40, y = 30) // 50, 50 when translated to window
+ val popupContentSize = IntSize(width = 200, height = 10)
+
+ val subject = ContextMenuPopupPositionProvider(position)
+ val actual =
+ subject.calculatePosition(anchorBounds, windowSize, layoutDirection, popupContentSize)
+
+ assertThat(actual).isEqualTo(IntOffset(0, 50))
+ }
+
+ @Test
+ fun calculatePosition_windowIsAccountedFor_ltr() {
+ val layoutDirection = LayoutDirection.Ltr
+ val windowSize = IntSize(width = 100, height = 100)
+ val anchorBounds = IntRect(left = 10, top = 20, right = 90, bottom = 80)
+ val position = IntOffset(x = 40, y = 30) // 50, 50 when translated to window
+ val popupContentSize = IntSize(width = 10, height = 10)
+
+ val subject = ContextMenuPopupPositionProvider(position)
+ val actual =
+ subject.calculatePosition(anchorBounds, windowSize, layoutDirection, popupContentSize)
+
+ assertThat(actual).isEqualTo(IntOffset(50, 50))
+ }
+
+ @Test
+ fun calculatePosition_windowIsAccountedFor_rtl() {
+ val layoutDirection = LayoutDirection.Rtl
+ val windowSize = IntSize(width = 100, height = 100)
+ val anchorBounds = IntRect(left = 10, top = 20, right = 90, bottom = 80)
+ val position = IntOffset(x = 40, y = 30) // 50, 50 when translated to window
+ val popupContentSize = IntSize(width = 10, height = 10)
+
+ val subject = ContextMenuPopupPositionProvider(position)
+ val actual =
+ subject.calculatePosition(anchorBounds, windowSize, layoutDirection, popupContentSize)
+
+ assertThat(actual).isEqualTo(IntOffset(40, 50))
+ }
+ //endregion ContextMenuPopupPositionProvider Tests
+
+ //region alignPopupAxis Tests
+ @Test
+ fun alignPopupAxis_closeAffinity_popupLargerThanWindow() = alignPopupAxisTest(
+ position = 50,
+ popupLength = 200,
+ closeAffinity = true,
+ expected = 0
+ )
+
+ @Test
+ fun alignPopupAxis_closeAffinity_popupFitsNeitherSide() = alignPopupAxisTest(
+ position = 50,
+ popupLength = 75,
+ closeAffinity = true,
+ expected = 25
+ )
+
+ @Test
+ fun alignPopupAxis_closeAffinity_popupFitsCloseSide() = alignPopupAxisTest(
+ position = 90,
+ popupLength = 30,
+ closeAffinity = true,
+ expected = 60
+ )
+
+ @Test
+ fun alignPopupAxis_closeAffinity_popupFitsFarSide() = alignPopupAxisTest(
+ position = 20,
+ popupLength = 30,
+ closeAffinity = true,
+ expected = 20
+ )
+
+ @Test
+ fun alignPopupAxis_closeAffinity_popupFitsEitherSide() = alignPopupAxisTest(
+ position = 50,
+ popupLength = 10,
+ closeAffinity = true,
+ expected = 50
+ )
+
+ @Test
+ fun alignPopupAxis_farAffinity_popupLargerThanWindow() = alignPopupAxisTest(
+ position = 50,
+ popupLength = 200,
+ closeAffinity = false,
+ expected = -100
+ )
+
+ @Test
+ fun alignPopupAxis_farAffinity_popupFitsNeitherSide() = alignPopupAxisTest(
+ position = 50,
+ popupLength = 75,
+ closeAffinity = false,
+ expected = 0
+ )
+
+ @Test
+ fun alignPopupAxis_farAffinity_popupFitsCloseSide() = alignPopupAxisTest(
+ position = 90,
+ popupLength = 30,
+ closeAffinity = false,
+ expected = 60
+ )
+
+ @Test
+ fun alignPopupAxis_farAffinity_popupFitsFarSide() = alignPopupAxisTest(
+ position = 20,
+ popupLength = 30,
+ closeAffinity = false,
+ expected = 20
+ )
+
+ @Test
+ fun alignPopupAxis_farAffinity_popupFitsEitherSide() = alignPopupAxisTest(
+ position = 50,
+ popupLength = 10,
+ closeAffinity = false,
+ expected = 40
+ )
+
+ private fun alignPopupAxisTest(
+ position: Int,
+ popupLength: Int,
+ closeAffinity: Boolean,
+ expected: Int
+ ) {
+ val actual = alignPopupAxis(
+ position = position,
+ popupLength = popupLength,
+ windowLength = 100,
+ closeAffinity = closeAffinity
+ )
+ assertThat(actual).isEqualTo(expected)
+ }
+
+ @Test
+ fun alignPopupAxis_popupBarelyFitsInAfterSpace() {
+ val actual = alignPopupAxis(
+ position = 74,
+ popupLength = 25,
+ windowLength = 100,
+ )
+ assertThat(actual).isEqualTo(74)
+ }
+
+ @Test
+ fun alignPopupAxis_popupBarelyDoesNotFitInAfterSpace() {
+ val actual = alignPopupAxis(
+ position = 75,
+ popupLength = 25,
+ windowLength = 100,
+ )
+ assertThat(actual).isEqualTo(50)
+ }
+
+ @Test
+ fun alignPopupAxis_popupBarelyFitsInBeforeSpace() {
+ val actual = alignPopupAxis(
+ position = 25,
+ popupLength = 25,
+ windowLength = 100,
+ closeAffinity = false
+ )
+ assertThat(actual).isEqualTo(0)
+ }
+
+ @Test
+ fun alignPopupAxis_popupBarelyDoesNotFitInBeforeSpace() {
+ val actual = alignPopupAxis(
+ position = 24,
+ popupLength = 25,
+ windowLength = 100,
+ closeAffinity = false
+ )
+ assertThat(actual).isEqualTo(24)
+ }
+ //endregion alignPopupAxis Tests
+}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/contextmenu/ContextMenuUi.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/contextmenu/ContextMenuUi.kt
deleted file mode 100644
index 1ff9031..0000000
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/contextmenu/ContextMenuUi.kt
+++ /dev/null
@@ -1,79 +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.compose.foundation.contextmenu
-
-import androidx.compose.runtime.CompositionLocalProvider
-import androidx.compose.runtime.ProvidableCompositionLocal
-import androidx.compose.runtime.Stable
-import androidx.compose.runtime.compositionLocalOf
-import androidx.compose.ui.graphics.Color
-
-/**
- * Provides the colors for the context menu.
- *
- * Use with [CompositionLocalProvider] to apply your custom colors to the context menu.
- */
-val LocalContextMenuColors: ProvidableCompositionLocal<ContextMenuColors?> =
- compositionLocalOf { null }
-
-/**
- * Colors to be provided to [LocalContextMenuColors] to apply the colors to the context menu.
- *
- * @param backgroundColor Color of the background in the context menu
- * @param textColor Color of the text in context menu items
- * @param iconColor Color of any icons in context menu items
- * @param disabledTextColor Color of disabled text in context menu items
- * @param disabledIconColor Color of any disabled icons in context menu items
- */
-@Stable
-class ContextMenuColors(
- val backgroundColor: Color,
- val textColor: Color,
- val iconColor: Color,
- val disabledTextColor: Color,
- val disabledIconColor: Color,
-) {
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (other == null || other !is ContextMenuColors) return false
-
- if (this.backgroundColor != other.backgroundColor) return false
- if (this.textColor != other.textColor) return false
- if (this.iconColor != other.iconColor) return false
- if (this.disabledTextColor != other.disabledTextColor) return false
- if (this.disabledIconColor != other.disabledIconColor) return false
-
- return true
- }
-
- override fun hashCode(): Int {
- var result = backgroundColor.hashCode()
- result = 31 * result + textColor.hashCode()
- result = 31 * result + iconColor.hashCode()
- result = 31 * result + disabledTextColor.hashCode()
- result = 31 * result + disabledIconColor.hashCode()
- return result
- }
-
- override fun toString(): String = "ContextMenuColors(" +
- "backgroundColor=$backgroundColor, " +
- "textColor=$textColor, " +
- "iconColor=$iconColor, " +
- "disabledTextColor=$disabledTextColor, " +
- "disabledIconColor=$disabledIconColor" +
- ")"
-}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridSpan.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridSpan.kt
index d42e065..a032669 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridSpan.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridSpan.kt
@@ -20,21 +20,21 @@
import androidx.compose.runtime.Immutable
/**
- * Represents the span of an item in a [LazyVerticalGrid].
+ * Represents the span of an item in a [LazyVerticalGrid] or a [LazyHorizontalGrid].
*/
@Immutable
@kotlin.jvm.JvmInline
value class GridItemSpan internal constructor(private val packedValue: Long) {
/**
* The span of the item on the current line. This will be the horizontal span for items of
- * [LazyVerticalGrid].
+ * [LazyVerticalGrid] and the vertical span for a [LazyHorizontalGrid].
*/
val currentLineSpan: Int get() = packedValue.toInt()
}
/**
* Creates a [GridItemSpan] with a specified [currentLineSpan]. This will be the horizontal span
- * for an item of a [LazyVerticalGrid].
+ * for an item of a [LazyVerticalGrid] and the vertical span for a [LazyHorizontalGrid].
*/
fun GridItemSpan(@IntRange(from = 1) currentLineSpan: Int): GridItemSpan {
require(currentLineSpan > 0) { "The span value should be higher than 0" }
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutSemantics.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutSemantics.kt
index 1920744..ba90ecd 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutSemantics.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutSemantics.kt
@@ -195,10 +195,7 @@
scrollByOffset(action = it)
}
- getScrollViewportLength {
- it.add((state.viewport - state.contentPadding).toFloat())
- true
- }
+ getScrollViewportLength { (state.viewport - state.contentPadding).toFloat() }
collectionInfo = [email protected]
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldSelectionState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldSelectionState.kt
index c22e1ad..379e9ad 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldSelectionState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/internal/selection/TextFieldSelectionState.kt
@@ -187,6 +187,13 @@
var draggingHandle by mutableStateOf<Handle?>(null)
/**
+ * Whether a long press and drag gesture is currently ongoing. This is used to show the
+ * magnifier during a long press even though the element is not focused until long press
+ * is finished.
+ */
+ var isBeingLongPressed by mutableStateOf(false)
+
+ /**
* Whether to show the cursor handle below cursor indicator when the TextField is focused.
*/
private var showCursorHandle by mutableStateOf(false)
@@ -642,6 +649,9 @@
dragBeginPosition = Offset.Unspecified
dragTotalDistance = Offset.Zero
previousRawDragOffset = -1
+
+ isBeingLongPressed = false
+ requestFocus()
}
}
@@ -649,8 +659,6 @@
detectDragGesturesAfterLongPress(
onDragStart = onDragStart@{ dragStartOffset ->
logDebug { "onDragStart after longPress $dragStartOffset" }
- requestFocus()
-
// this gesture detector is applied on the decoration box. We do not need to
// convert the gesture offset, that's going to be calculated by [handleDragPosition]
updateHandleDragging(
@@ -658,6 +666,8 @@
position = dragStartOffset
)
+ isBeingLongPressed = true
+
dragBeginPosition = dragStartOffset
dragTotalDistance = Offset.Zero
previousRawDragOffset = -1
diff --git a/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/MaterialThemeContextMenuColorsTest.kt b/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/MaterialThemeContextMenuColorsTest.kt
deleted file mode 100644
index 57ca1357..0000000
--- a/compose/material/material/src/androidInstrumentedTest/kotlin/androidx/compose/material/MaterialThemeContextMenuColorsTest.kt
+++ /dev/null
@@ -1,275 +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.compose.material
-
-import androidx.compose.foundation.contextmenu.ContextMenuColors
-import androidx.compose.foundation.contextmenu.LocalContextMenuColors
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.MediumTest
-import com.google.common.truth.Fact
-import com.google.common.truth.FailureMetadata
-import com.google.common.truth.Subject
-import com.google.common.truth.Subject.Factory
-import com.google.common.truth.Truth
-import com.google.common.truth.Truth.assertThat
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@MediumTest
-@RunWith(AndroidJUnit4::class)
-class MaterialThemeContextMenuColorsTest {
- @get:Rule
- val rule = createComposeRule()
-
- @Test
- fun theme_light_contextMenuColors_setCorrectly() {
- val expectedSurfaceColor = Color.Red
- val onSurfaceColor = Color.Blue
-
- var contextMenuColors: ContextMenuColors? = null
- rule.setContent {
- MaterialTheme(
- colors = MaterialTheme.colors.copy(
- surface = expectedSurfaceColor,
- onSurface = onSurfaceColor,
- isLight = true
- ),
- ) {
- contextMenuColors = LocalContextMenuColors.current
- }
- }
-
- val expectedEnabledContentColor =
- onSurfaceColor.copy(alpha = Alpha.Content.LowContrast.ENABLED)
- val expectedEnabledIconColor = onSurfaceColor.copy(alpha = Alpha.Icon.Light.ENABLED)
- val expectedDisabledContentColor =
- onSurfaceColor.copy(alpha = Alpha.Content.LowContrast.DISABLED)
- val expectedDisabledIconColor = onSurfaceColor.copy(alpha = Alpha.Icon.Light.DISABLED)
-
- contextMenuColors.assertContextMenuColors(
- expectedBackgroundColor = expectedSurfaceColor,
- expectedTextColor = expectedEnabledContentColor,
- expectedIconColor = expectedEnabledIconColor,
- expectedDisabledTextColor = expectedDisabledContentColor,
- expectedDisabledIconColor = expectedDisabledIconColor,
- )
- }
-
- @Test
- fun theme_dark_contextMenuColors_setCorrectly() {
- val expectedSurfaceColor = Color.Red
- val onSurfaceColor = Color.Blue
-
- var contextMenuColors: ContextMenuColors? = null
- rule.setContent {
- MaterialTheme(
- colors = MaterialTheme.colors.copy(
- surface = expectedSurfaceColor,
- onSurface = onSurfaceColor,
- isLight = false
- ),
- ) {
- contextMenuColors = LocalContextMenuColors.current
- }
- }
-
- val expectedEnabledContentColor =
- onSurfaceColor.copy(alpha = Alpha.Content.HighContrast.ENABLED)
- val expectedEnabledIconColor = onSurfaceColor.copy(alpha = Alpha.Icon.Dark.ENABLED)
- val expectedDisabledContentColor =
- onSurfaceColor.copy(alpha = Alpha.Content.HighContrast.DISABLED)
- val expectedDisabledIconColor = onSurfaceColor.copy(alpha = Alpha.Icon.Dark.DISABLED)
-
- contextMenuColors.assertContextMenuColors(
- expectedBackgroundColor = expectedSurfaceColor,
- expectedTextColor = expectedEnabledContentColor,
- expectedIconColor = expectedEnabledIconColor,
- expectedDisabledTextColor = expectedDisabledContentColor,
- expectedDisabledIconColor = expectedDisabledIconColor,
- )
- }
-
- @Test
- fun theme_contextMenuColors_updatesOnRelevantColorChanges() {
- val initialSurfaceColor = Color.Red
- val expectedSurfaceColor = Color.Green
- val onSurfaceColor = Color.Blue
-
- var testSurfaceColor by mutableStateOf(initialSurfaceColor)
- var contextMenuColors: ContextMenuColors? = null
- rule.setContent {
- MaterialTheme(
- colors = MaterialTheme.colors.copy(
- surface = testSurfaceColor,
- onSurface = onSurfaceColor,
- isLight = true,
- ),
- ) {
- contextMenuColors = LocalContextMenuColors.current
- }
- }
-
- val expectedEnabledContentColor =
- onSurfaceColor.copy(alpha = Alpha.Content.LowContrast.ENABLED)
- val expectedEnabledIconColor = onSurfaceColor.copy(alpha = Alpha.Icon.Light.ENABLED)
- val expectedDisabledContentColor =
- onSurfaceColor.copy(alpha = Alpha.Content.LowContrast.DISABLED)
- val expectedDisabledIconColor = onSurfaceColor.copy(alpha = Alpha.Icon.Light.DISABLED)
-
- contextMenuColors.assertContextMenuColors(
- expectedBackgroundColor = initialSurfaceColor,
- expectedTextColor = expectedEnabledContentColor,
- expectedIconColor = expectedEnabledIconColor,
- expectedDisabledTextColor = expectedDisabledContentColor,
- expectedDisabledIconColor = expectedDisabledIconColor,
- )
-
- val initialContextMenuColors = contextMenuColors
- testSurfaceColor = expectedSurfaceColor
- rule.waitForIdle()
-
- assertThat(contextMenuColors).isNotEqualTo(initialContextMenuColors)
-
- contextMenuColors.assertContextMenuColors(
- expectedBackgroundColor = expectedSurfaceColor,
- expectedTextColor = expectedEnabledContentColor,
- expectedIconColor = expectedEnabledIconColor,
- expectedDisabledTextColor = expectedDisabledContentColor,
- expectedDisabledIconColor = expectedDisabledIconColor,
- )
- }
-
- @Test
- fun theme_contextMenuColors_updatesOnIsLightChanges() {
- val expectedSurfaceColor = Color.Red
- val onSurfaceColor = Color.Blue
-
- var isLight by mutableStateOf(true)
- var contextMenuColors: ContextMenuColors? = null
- rule.setContent {
- MaterialTheme(
- colors = MaterialTheme.colors.copy(
- surface = expectedSurfaceColor,
- onSurface = onSurfaceColor,
- isLight = isLight,
- ),
- ) {
- contextMenuColors = LocalContextMenuColors.current
- }
- }
-
- val initialContextMenuColors = contextMenuColors
- isLight = false
- rule.waitForIdle()
-
- assertThat(contextMenuColors).isNotEqualTo(initialContextMenuColors)
-
- val expectedEnabledContentColor =
- onSurfaceColor.copy(alpha = Alpha.Content.HighContrast.ENABLED)
- val expectedEnabledIconColor = onSurfaceColor.copy(alpha = Alpha.Icon.Dark.ENABLED)
- val expectedDisabledContentColor =
- onSurfaceColor.copy(alpha = Alpha.Content.HighContrast.DISABLED)
- val expectedDisabledIconColor = onSurfaceColor.copy(alpha = Alpha.Icon.Dark.DISABLED)
-
- contextMenuColors.assertContextMenuColors(
- expectedBackgroundColor = expectedSurfaceColor,
- expectedTextColor = expectedEnabledContentColor,
- expectedIconColor = expectedEnabledIconColor,
- expectedDisabledTextColor = expectedDisabledContentColor,
- expectedDisabledIconColor = expectedDisabledIconColor,
- )
- }
-
- @Test
- fun theme_contextMenuColors_doesNotUpdateOnIrrelevantColorChanges() {
- val expectedSurfaceColor = Color.Red
- val testOnSurfaceColor = Color.Blue
-
- var primaryColor by mutableStateOf(Color.Green)
-
- var contextMenuColors: ContextMenuColors? = null
- rule.setContent {
- MaterialTheme(
- colors = MaterialTheme.colors.copy(
- primary = primaryColor,
- surface = expectedSurfaceColor,
- onSurface = testOnSurfaceColor,
- ),
- ) {
- contextMenuColors = LocalContextMenuColors.current
- }
- }
-
- val firstContextMenuColors = contextMenuColors
- primaryColor = Color.Cyan
- rule.waitForIdle()
-
- val secondContextMenuColors = contextMenuColors
- assertThat(secondContextMenuColors).isSameInstanceAs(firstContextMenuColors)
- }
-}
-
-private fun ContextMenuColors?.assertContextMenuColors(
- expectedBackgroundColor: Color,
- expectedTextColor: Color,
- expectedIconColor: Color,
- expectedDisabledTextColor: Color,
- expectedDisabledIconColor: Color,
-) {
- assertThat(this).isNotNull()
- this!!
- assertThatColor(backgroundColor).isFuzzyEqualTo(expectedBackgroundColor)
- assertThatColor(textColor).isFuzzyEqualTo(expectedTextColor)
- assertThatColor(iconColor).isFuzzyEqualTo(expectedIconColor)
- assertThatColor(disabledTextColor).isFuzzyEqualTo(expectedDisabledTextColor)
- assertThatColor(disabledIconColor).isFuzzyEqualTo(expectedDisabledIconColor)
-}
-
-private fun assertThatColor(actual: Color): ColorSubject =
- Truth.assertAbout(ColorSubject.INSTANCE).that(actual)
-
-private class ColorSubject(
- failureMetadata: FailureMetadata?,
- private val subject: Color,
-) : Subject(failureMetadata, subject) {
- companion object {
- val INSTANCE = Factory<ColorSubject, Color> { failureMetadata, subject ->
- ColorSubject(failureMetadata, subject)
- }
- }
-
- fun isFuzzyEqualTo(expected: Color, tolerance: Float = 0.001f) {
- try {
- assertThat(subject.red).isWithin(tolerance).of(expected.red)
- assertThat(subject.green).isWithin(tolerance).of(expected.green)
- assertThat(subject.blue).isWithin(tolerance).of(expected.blue)
- assertThat(subject.alpha).isWithin(tolerance).of(expected.alpha)
- } catch (e: AssertionError) {
- failWithActual(
- Fact.simpleFact("Colors are not equal."),
- Fact.fact("expected", expected.toString()),
- Fact.fact("with tolerance", tolerance),
- )
- }
- }
-}
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ContentAlpha.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ContentAlpha.kt
index 5610a39..a3091b1 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ContentAlpha.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ContentAlpha.kt
@@ -19,7 +19,6 @@
import androidx.annotation.FloatRange
import androidx.compose.runtime.Composable
import androidx.compose.runtime.compositionLocalOf
-import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.luminance
/**
@@ -78,20 +77,11 @@
): Float {
val contentColor = LocalContentColor.current
val lightTheme = MaterialTheme.colors.isLight
- return contentAlpha(contentColor, lightTheme, highContrastAlpha, lowContrastAlpha)
- }
-
- internal fun contentAlpha(
- contentColor: Color,
- lightTheme: Boolean,
- @FloatRange(from = 0.0, to = 1.0)
- highContrastAlpha: Float,
- @FloatRange(from = 0.0, to = 1.0)
- lowContrastAlpha: Float
- ): Float = if (lightTheme) {
- if (contentColor.luminance() > 0.5) highContrastAlpha else lowContrastAlpha
- } else {
- if (contentColor.luminance() < 0.5) highContrastAlpha else lowContrastAlpha
+ return if (lightTheme) {
+ if (contentColor.luminance() > 0.5) highContrastAlpha else lowContrastAlpha
+ } else {
+ if (contentColor.luminance() < 0.5) highContrastAlpha else lowContrastAlpha
+ }
}
}
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/MaterialContextMenuColors.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/MaterialContextMenuColors.kt
deleted file mode 100644
index 13b1350..0000000
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/MaterialContextMenuColors.kt
+++ /dev/null
@@ -1,93 +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.compose.material
-
-import androidx.annotation.VisibleForTesting
-import androidx.compose.foundation.contextmenu.ContextMenuColors
-import androidx.compose.material.ContentAlpha.contentAlpha
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
-import androidx.compose.ui.graphics.Color
-
-/**
- * Remembers a [ContextMenuColors] based on [colors].
- *
- * The background color will be [Colors.surface] and all the other colors will be
- * [Colors.onSurface] with alpha applied. This matches [DropdownMenuContent] and
- * [DropdownMenuItemContent]. The alpha values match What is seen in [ContentAlpha].
- */
-@Composable
-internal fun rememberContextMenuColors(colors: Colors): ContextMenuColors {
- val backgroundColor = colors.surface
- val contentColor = colors.onSurface
- val isLight = colors.isLight
- return remember(backgroundColor, contentColor, isLight) {
- val textAlpha = Alpha.Content.enabled(contentColor, isLight)
- val iconAlpha = Alpha.Icon.enabled(isLight)
- val disabledTextAlpha = Alpha.Content.disabled(contentColor, isLight)
- val disabledIconAlpha = Alpha.Icon.disabled(isLight)
- ContextMenuColors(
- backgroundColor = backgroundColor,
- textColor = contentColor.copy(alpha = textAlpha),
- iconColor = contentColor.copy(alpha = iconAlpha),
- disabledTextColor = contentColor.copy(alpha = disabledTextAlpha),
- disabledIconColor = contentColor.copy(alpha = disabledIconAlpha),
- )
- }
-}
-
-@VisibleForTesting
-internal object Alpha {
- object Content {
- fun enabled(contentColor: Color, isLight: Boolean): Float =
- contentAlpha(contentColor, isLight, HighContrast.ENABLED, LowContrast.ENABLED)
-
- fun disabled(contentColor: Color, isLight: Boolean): Float =
- contentAlpha(contentColor, isLight, HighContrast.DISABLED, LowContrast.DISABLED)
-
- /** Values taken from [HighContrastContentAlpha]. */
- object HighContrast {
- const val ENABLED: Float = 1f
- const val DISABLED: Float = 0.38f
- }
-
- /** Values taken from [LowContrastContentAlpha]. */
- object LowContrast {
- const val ENABLED: Float = 0.87f
- const val DISABLED: Float = 0.38f
- }
- }
-
- /**
- * Values taken from
- * [material icons](https://m2.material.io/design/iconography/system-icons.html#color).
- */
- object Icon {
- fun enabled(isLight: Boolean): Float = if (isLight) Light.ENABLED else Dark.ENABLED
- fun disabled(isLight: Boolean): Float = if (isLight) Light.DISABLED else Dark.DISABLED
-
- object Light {
- const val ENABLED: Float = 0.54f
- const val DISABLED: Float = 0.38f
- }
-
- object Dark {
- const val ENABLED: Float = 0.7f
- const val DISABLED: Float = 0.5f
- }
- }
-}
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/MaterialTheme.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/MaterialTheme.kt
index 069d2f2..b8fa051 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/MaterialTheme.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/MaterialTheme.kt
@@ -17,7 +17,6 @@
package androidx.compose.material
import androidx.compose.foundation.LocalIndication
-import androidx.compose.foundation.contextmenu.LocalContextMenuColors
import androidx.compose.foundation.text.selection.LocalTextSelectionColors
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
@@ -66,7 +65,6 @@
}.apply { updateColorsFrom(colors) }
val rippleIndication = rippleOrFallbackImplementation()
val selectionColors = rememberTextSelectionColors(rememberedColors)
- val contextMenuColors = rememberContextMenuColors(rememberedColors)
@Suppress("DEPRECATION_ERROR")
CompositionLocalProvider(
LocalColors provides rememberedColors,
@@ -76,8 +74,7 @@
androidx.compose.material.ripple.LocalRippleTheme provides CompatRippleTheme,
LocalShapes provides shapes,
LocalTextSelectionColors provides selectionColors,
- LocalTypography provides typography,
- LocalContextMenuColors provides contextMenuColors,
+ LocalTypography provides typography
) {
ProvideTextStyle(value = typography.body1) {
PlatformMaterialTheme(content)
diff --git a/compose/material3/material3-window-size-class/build.gradle b/compose/material3/material3-window-size-class/build.gradle
index f3d4c53..28239af 100644
--- a/compose/material3/material3-window-size-class/build.gradle
+++ b/compose/material3/material3-window-size-class/build.gradle
@@ -122,6 +122,7 @@
type = LibraryType.PUBLISHED_KOTLIN_ONLY_LIBRARY
inceptionYear = "2022"
description = "Provides window size classes for building responsive UIs"
+ samples(project(":compose:material3:material3-window-size-class:material3-window-size-class-samples"))
}
android {
diff --git a/compose/material3/material3/api/current.txt b/compose/material3/material3/api/current.txt
index 21ff711..6d5fb74 100644
--- a/compose/material3/material3/api/current.txt
+++ b/compose/material3/material3/api/current.txt
@@ -1151,7 +1151,7 @@
}
public final class ProgressIndicatorDefaults {
- method public void drawStopIndicator(androidx.compose.ui.graphics.drawscope.DrawScope, float stopSize, long color, int strokeCap);
+ method public void drawStopIndicator(androidx.compose.ui.graphics.drawscope.DrawScope drawScope, float stopSize, long color, int strokeCap);
method @androidx.compose.runtime.Composable public long getCircularColor();
method public int getCircularDeterminateStrokeCap();
method @androidx.compose.runtime.Composable public long getCircularDeterminateTrackColor();
@@ -1196,7 +1196,7 @@
method @Deprecated @androidx.compose.runtime.Composable public static void LinearProgressIndicator(float progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional long trackColor);
method @Deprecated @androidx.compose.runtime.Composable public static void LinearProgressIndicator(float progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional long trackColor, optional int strokeCap);
method @Deprecated @androidx.compose.runtime.Composable public static void LinearProgressIndicator(kotlin.jvm.functions.Function0<java.lang.Float> progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional long trackColor, optional int strokeCap);
- method @androidx.compose.runtime.Composable public static void LinearProgressIndicator(kotlin.jvm.functions.Function0<java.lang.Float> progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional long trackColor, optional int strokeCap, optional float gapSize, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.drawscope.DrawScope,kotlin.Unit>? drawStopIndicator);
+ method @androidx.compose.runtime.Composable public static void LinearProgressIndicator(kotlin.jvm.functions.Function0<java.lang.Float> progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional long trackColor, optional int strokeCap, optional float gapSize, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.drawscope.DrawScope,kotlin.Unit> drawStopIndicator);
}
@androidx.compose.runtime.Immutable public final class RadioButtonColors {
@@ -1498,10 +1498,10 @@
@androidx.compose.runtime.Stable public final class SliderDefaults {
method @androidx.compose.runtime.Composable public void Thumb(androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.SliderColors colors, optional boolean enabled, optional long thumbSize);
method @Deprecated @androidx.compose.runtime.Composable public void Track(androidx.compose.material3.RangeSliderState rangeSliderState, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.SliderColors colors, optional boolean enabled);
- method @androidx.compose.runtime.Composable public void Track(androidx.compose.material3.RangeSliderState rangeSliderState, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.SliderColors colors, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.graphics.drawscope.DrawScope,? super androidx.compose.ui.geometry.Offset,kotlin.Unit>? drawStopIndicator, optional kotlin.jvm.functions.Function3<? super androidx.compose.ui.graphics.drawscope.DrawScope,? super androidx.compose.ui.geometry.Offset,? super androidx.compose.ui.graphics.Color,kotlin.Unit>? drawTick, optional float thumbTrackGapSize, optional float trackInsideCornerSize);
+ method @androidx.compose.runtime.Composable public void Track(androidx.compose.material3.RangeSliderState rangeSliderState, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.SliderColors colors, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.graphics.drawscope.DrawScope,? super androidx.compose.ui.geometry.Offset,kotlin.Unit>? drawStopIndicator, optional kotlin.jvm.functions.Function3<? super androidx.compose.ui.graphics.drawscope.DrawScope,? super androidx.compose.ui.geometry.Offset,? super androidx.compose.ui.graphics.Color,kotlin.Unit> drawTick, optional float thumbTrackGapSize, optional float trackInsideCornerSize);
method @Deprecated @androidx.compose.runtime.Composable public void Track(androidx.compose.material3.SliderPositions sliderPositions, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.SliderColors colors, optional boolean enabled);
method @Deprecated @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public void Track(androidx.compose.material3.SliderState sliderState, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.SliderColors colors, optional boolean enabled);
- method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public void Track(androidx.compose.material3.SliderState sliderState, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.SliderColors colors, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.graphics.drawscope.DrawScope,? super androidx.compose.ui.geometry.Offset,kotlin.Unit>? drawStopIndicator, optional kotlin.jvm.functions.Function3<? super androidx.compose.ui.graphics.drawscope.DrawScope,? super androidx.compose.ui.geometry.Offset,? super androidx.compose.ui.graphics.Color,kotlin.Unit>? drawTick, optional float thumbTrackGapSize, optional float trackInsideCornerSize);
+ method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public void Track(androidx.compose.material3.SliderState sliderState, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.SliderColors colors, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.graphics.drawscope.DrawScope,? super androidx.compose.ui.geometry.Offset,kotlin.Unit>? drawStopIndicator, optional kotlin.jvm.functions.Function3<? super androidx.compose.ui.graphics.drawscope.DrawScope,? super androidx.compose.ui.geometry.Offset,? super androidx.compose.ui.graphics.Color,kotlin.Unit> drawTick, optional float thumbTrackGapSize, optional float trackInsideCornerSize);
method @androidx.compose.runtime.Composable public androidx.compose.material3.SliderColors colors();
method @androidx.compose.runtime.Composable public androidx.compose.material3.SliderColors colors(optional long thumbColor, optional long activeTrackColor, optional long activeTickColor, optional long inactiveTrackColor, optional long inactiveTickColor, optional long disabledThumbColor, optional long disabledActiveTrackColor, optional long disabledActiveTickColor, optional long disabledInactiveTrackColor, optional long disabledInactiveTickColor);
method public float getTickSize();
diff --git a/compose/material3/material3/api/restricted_current.txt b/compose/material3/material3/api/restricted_current.txt
index 21ff711..6d5fb74 100644
--- a/compose/material3/material3/api/restricted_current.txt
+++ b/compose/material3/material3/api/restricted_current.txt
@@ -1151,7 +1151,7 @@
}
public final class ProgressIndicatorDefaults {
- method public void drawStopIndicator(androidx.compose.ui.graphics.drawscope.DrawScope, float stopSize, long color, int strokeCap);
+ method public void drawStopIndicator(androidx.compose.ui.graphics.drawscope.DrawScope drawScope, float stopSize, long color, int strokeCap);
method @androidx.compose.runtime.Composable public long getCircularColor();
method public int getCircularDeterminateStrokeCap();
method @androidx.compose.runtime.Composable public long getCircularDeterminateTrackColor();
@@ -1196,7 +1196,7 @@
method @Deprecated @androidx.compose.runtime.Composable public static void LinearProgressIndicator(float progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional long trackColor);
method @Deprecated @androidx.compose.runtime.Composable public static void LinearProgressIndicator(float progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional long trackColor, optional int strokeCap);
method @Deprecated @androidx.compose.runtime.Composable public static void LinearProgressIndicator(kotlin.jvm.functions.Function0<java.lang.Float> progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional long trackColor, optional int strokeCap);
- method @androidx.compose.runtime.Composable public static void LinearProgressIndicator(kotlin.jvm.functions.Function0<java.lang.Float> progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional long trackColor, optional int strokeCap, optional float gapSize, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.drawscope.DrawScope,kotlin.Unit>? drawStopIndicator);
+ method @androidx.compose.runtime.Composable public static void LinearProgressIndicator(kotlin.jvm.functions.Function0<java.lang.Float> progress, optional androidx.compose.ui.Modifier modifier, optional long color, optional long trackColor, optional int strokeCap, optional float gapSize, optional kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.drawscope.DrawScope,kotlin.Unit> drawStopIndicator);
}
@androidx.compose.runtime.Immutable public final class RadioButtonColors {
@@ -1498,10 +1498,10 @@
@androidx.compose.runtime.Stable public final class SliderDefaults {
method @androidx.compose.runtime.Composable public void Thumb(androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.SliderColors colors, optional boolean enabled, optional long thumbSize);
method @Deprecated @androidx.compose.runtime.Composable public void Track(androidx.compose.material3.RangeSliderState rangeSliderState, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.SliderColors colors, optional boolean enabled);
- method @androidx.compose.runtime.Composable public void Track(androidx.compose.material3.RangeSliderState rangeSliderState, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.SliderColors colors, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.graphics.drawscope.DrawScope,? super androidx.compose.ui.geometry.Offset,kotlin.Unit>? drawStopIndicator, optional kotlin.jvm.functions.Function3<? super androidx.compose.ui.graphics.drawscope.DrawScope,? super androidx.compose.ui.geometry.Offset,? super androidx.compose.ui.graphics.Color,kotlin.Unit>? drawTick, optional float thumbTrackGapSize, optional float trackInsideCornerSize);
+ method @androidx.compose.runtime.Composable public void Track(androidx.compose.material3.RangeSliderState rangeSliderState, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.SliderColors colors, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.graphics.drawscope.DrawScope,? super androidx.compose.ui.geometry.Offset,kotlin.Unit>? drawStopIndicator, optional kotlin.jvm.functions.Function3<? super androidx.compose.ui.graphics.drawscope.DrawScope,? super androidx.compose.ui.geometry.Offset,? super androidx.compose.ui.graphics.Color,kotlin.Unit> drawTick, optional float thumbTrackGapSize, optional float trackInsideCornerSize);
method @Deprecated @androidx.compose.runtime.Composable public void Track(androidx.compose.material3.SliderPositions sliderPositions, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.SliderColors colors, optional boolean enabled);
method @Deprecated @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public void Track(androidx.compose.material3.SliderState sliderState, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.material3.SliderColors colors, optional boolean enabled);
- method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public void Track(androidx.compose.material3.SliderState sliderState, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.SliderColors colors, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.graphics.drawscope.DrawScope,? super androidx.compose.ui.geometry.Offset,kotlin.Unit>? drawStopIndicator, optional kotlin.jvm.functions.Function3<? super androidx.compose.ui.graphics.drawscope.DrawScope,? super androidx.compose.ui.geometry.Offset,? super androidx.compose.ui.graphics.Color,kotlin.Unit>? drawTick, optional float thumbTrackGapSize, optional float trackInsideCornerSize);
+ method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3Api @androidx.compose.runtime.Composable public void Track(androidx.compose.material3.SliderState sliderState, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.material3.SliderColors colors, optional kotlin.jvm.functions.Function2<? super androidx.compose.ui.graphics.drawscope.DrawScope,? super androidx.compose.ui.geometry.Offset,kotlin.Unit>? drawStopIndicator, optional kotlin.jvm.functions.Function3<? super androidx.compose.ui.graphics.drawscope.DrawScope,? super androidx.compose.ui.geometry.Offset,? super androidx.compose.ui.graphics.Color,kotlin.Unit> drawTick, optional float thumbTrackGapSize, optional float trackInsideCornerSize);
method @androidx.compose.runtime.Composable public androidx.compose.material3.SliderColors colors();
method @androidx.compose.runtime.Composable public androidx.compose.material3.SliderColors colors(optional long thumbColor, optional long activeTrackColor, optional long activeTickColor, optional long inactiveTrackColor, optional long inactiveTickColor, optional long disabledThumbColor, optional long disabledActiveTrackColor, optional long disabledActiveTickColor, optional long disabledInactiveTrackColor, optional long disabledInactiveTickColor);
method public float getTickSize();
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/ProgressIndicatorSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/ProgressIndicatorSamples.kt
index 792b4a6..8e2c00b 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/ProgressIndicatorSamples.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/ProgressIndicatorSamples.kt
@@ -80,7 +80,7 @@
trackColor = MaterialTheme.colorScheme.surfaceVariant,
strokeCap = StrokeCap.Butt,
gapSize = 0.dp,
- drawStopIndicator = null
+ drawStopIndicator = {}
)
Spacer(Modifier.requiredHeight(30.dp))
Text("Set progress:")
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/MaterialThemeContextMenuColorsTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/MaterialThemeContextMenuColorsTest.kt
deleted file mode 100644
index 26d9b38..0000000
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/MaterialThemeContextMenuColorsTest.kt
+++ /dev/null
@@ -1,189 +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.compose.material3
-
-import androidx.compose.foundation.contextmenu.ContextMenuColors
-import androidx.compose.foundation.contextmenu.LocalContextMenuColors
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.MediumTest
-import com.google.common.truth.Fact
-import com.google.common.truth.FailureMetadata
-import com.google.common.truth.Subject
-import com.google.common.truth.Subject.Factory
-import com.google.common.truth.Truth
-import com.google.common.truth.Truth.assertThat
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@MediumTest
-@RunWith(AndroidJUnit4::class)
-class MaterialThemeContextMenuColorsTest {
- @get:Rule
- val rule = createComposeRule()
-
- @Test
- fun theme_contextMenuColors_setCorrectly() {
- val expectedSurfaceContainerColor = Color.Red
- val expectedOnSurfaceColor = Color.Blue
- val expectedOnSurfaceVariantColor = Color.Green
- val expectedDisabledContentColor =
- expectedOnSurfaceColor.copy(alpha = DisabledContextMenuContentOpacity)
-
- var contextMenuColors: ContextMenuColors? = null
- rule.setContent {
- MaterialTheme(
- colorScheme = MaterialTheme.colorScheme.copy(
- surfaceContainer = expectedSurfaceContainerColor,
- onSurface = expectedOnSurfaceColor,
- onSurfaceVariant = expectedOnSurfaceVariantColor,
- ),
- ) {
- contextMenuColors = LocalContextMenuColors.current
- }
- }
-
- contextMenuColors.assertContextMenuColors(
- expectedBackgroundColor = expectedSurfaceContainerColor,
- expectedTextColor = expectedOnSurfaceColor,
- expectedIconColor = expectedOnSurfaceVariantColor,
- expectedDisabledTextColor = expectedDisabledContentColor,
- expectedDisabledIconColor = expectedDisabledContentColor,
- )
- }
-
- @Test
- fun theme_contextMenuColors_updatesOnRelevantColorChanges() {
- val initialSurfaceContainerColor = Color.Black
- val expectedSurfaceContainerColor = Color.Red
- val expectedOnSurfaceColor = Color.Blue
- val expectedOnSurfaceVariantColor = Color.Green
- val expectedDisabledContentColor =
- expectedOnSurfaceColor.copy(alpha = DisabledContextMenuContentOpacity)
-
- var testSurfaceContainerColor by mutableStateOf(initialSurfaceContainerColor)
- var contextMenuColors: ContextMenuColors? = null
- rule.setContent {
- MaterialTheme(
- colorScheme = MaterialTheme.colorScheme.copy(
- surfaceContainer = testSurfaceContainerColor,
- onSurface = expectedOnSurfaceColor,
- onSurfaceVariant = expectedOnSurfaceVariantColor,
- ),
- ) {
- contextMenuColors = LocalContextMenuColors.current
- }
- }
-
- contextMenuColors.assertContextMenuColors(
- expectedBackgroundColor = initialSurfaceContainerColor,
- expectedTextColor = expectedOnSurfaceColor,
- expectedIconColor = expectedOnSurfaceVariantColor,
- expectedDisabledTextColor = expectedDisabledContentColor,
- expectedDisabledIconColor = expectedDisabledContentColor,
- )
-
- val initialContextMenuColors = contextMenuColors
- testSurfaceContainerColor = expectedSurfaceContainerColor
- rule.waitForIdle()
-
- assertThat(contextMenuColors).isNotEqualTo(initialContextMenuColors)
-
- contextMenuColors.assertContextMenuColors(
- expectedBackgroundColor = expectedSurfaceContainerColor,
- expectedTextColor = expectedOnSurfaceColor,
- expectedIconColor = expectedOnSurfaceVariantColor,
- expectedDisabledTextColor = expectedDisabledContentColor,
- expectedDisabledIconColor = expectedDisabledContentColor,
- )
- }
-
- @Test
- fun theme_contextMenuColors_doesNotUpdateOnIrrelevantColorChanges() {
- var primaryColor by mutableStateOf(Color.Black)
- var maybeContextMenuColors: ContextMenuColors? = null
- rule.setContent {
- MaterialTheme(
- colorScheme = MaterialTheme.colorScheme.copy(
- surfaceContainer = Color.Red,
- onSurface = Color.Blue,
- onSurfaceVariant = Color.Green,
- ),
- ) {
- maybeContextMenuColors = LocalContextMenuColors.current
- }
- }
-
- assertThat(maybeContextMenuColors).isNotNull()
-
- val initialContextMenuColors = maybeContextMenuColors
- primaryColor = Color.White
- rule.waitForIdle()
-
- assertThat(maybeContextMenuColors).isSameInstanceAs(initialContextMenuColors)
- }
-}
-
-private fun ContextMenuColors?.assertContextMenuColors(
- expectedBackgroundColor: Color,
- expectedTextColor: Color,
- expectedIconColor: Color,
- expectedDisabledTextColor: Color,
- expectedDisabledIconColor: Color,
-) {
- assertThat(this).isNotNull()
- this!!
- assertThatColor(backgroundColor).isFuzzyEqualTo(expectedBackgroundColor)
- assertThatColor(textColor).isFuzzyEqualTo(expectedTextColor)
- assertThatColor(iconColor).isFuzzyEqualTo(expectedIconColor)
- assertThatColor(disabledTextColor).isFuzzyEqualTo(expectedDisabledTextColor)
- assertThatColor(disabledIconColor).isFuzzyEqualTo(expectedDisabledIconColor)
-}
-
-private fun assertThatColor(actual: Color): ColorSubject =
- Truth.assertAbout(ColorSubject.INSTANCE).that(actual)
-
-private class ColorSubject(
- failureMetadata: FailureMetadata?,
- private val subject: Color,
-) : Subject(failureMetadata, subject) {
- companion object {
- val INSTANCE = Factory<ColorSubject, Color> { failureMetadata, subject ->
- ColorSubject(failureMetadata, subject)
- }
- }
-
- fun isFuzzyEqualTo(expected: Color, tolerance: Float = 0.001f) {
- try {
- assertThat(subject.red).isWithin(tolerance).of(expected.red)
- assertThat(subject.green).isWithin(tolerance).of(expected.green)
- assertThat(subject.blue).isWithin(tolerance).of(expected.blue)
- assertThat(subject.alpha).isWithin(tolerance).of(expected.alpha)
- } catch (e: AssertionError) {
- failWithActual(
- Fact.simpleFact("Colors are not equal."),
- Fact.fact("expected", expected.toString()),
- Fact.fact("with tolerance", tolerance),
- )
- }
- }
-}
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ProgressIndicatorScreenshotTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ProgressIndicatorScreenshotTest.kt
index 84c6532..6a6af69 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ProgressIndicatorScreenshotTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/ProgressIndicatorScreenshotTest.kt
@@ -80,7 +80,7 @@
Box(wrap.testTag(wrapperTestTag)) {
LinearProgressIndicator(
progress = { 0.5f },
- drawStopIndicator = null
+ drawStopIndicator = {}
)
}
}
diff --git a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/internal/PredictiveBack.android.kt b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/internal/PredictiveBack.android.kt
index 93ebcc7..af8fa37 100644
--- a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/internal/PredictiveBack.android.kt
+++ b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/internal/PredictiveBack.android.kt
@@ -16,8 +16,11 @@
package androidx.compose.material3.internal
-import androidx.compose.animation.core.EaseOut
+import androidx.compose.animation.core.CubicBezierEasing
+import androidx.compose.animation.core.Easing
+
+private val PredictiveBackEasing: Easing = CubicBezierEasing(0.1f, 0.1f, 0f, 1f)
internal object PredictiveBack {
- internal fun transform(progress: Float) = EaseOut.transform(progress)
+ internal fun transform(progress: Float) = PredictiveBackEasing.transform(progress)
}
diff --git a/compose/material3/material3/src/androidMain/res/values-be/strings.xml b/compose/material3/material3/src/androidMain/res/values-be/strings.xml
index ae2754d..e434ece 100644
--- a/compose/material3/material3/src/androidMain/res/values-be/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-be/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"Дыялогавае акно"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"Разгорнута"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"Згорнута"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"Уключыць (выключыць) выпадное меню"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"Закрыць"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"Пошук"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"Прапановы ўнізе"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-bg/strings.xml b/compose/material3/material3/src/androidMain/res/values-bg/strings.xml
index 4558f92..124dad0 100644
--- a/compose/material3/material3/src/androidMain/res/values-bg/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-bg/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"Диалогов прозорец"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"Разгънато"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"Свито"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"Превключване на падащото меню"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"Отхвърляне"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"Лента за търсене"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"Предложенията са по-долу"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-bn/strings.xml b/compose/material3/material3/src/androidMain/res/values-bn/strings.xml
index 9b9a91a..b74dd78 100644
--- a/compose/material3/material3/src/androidMain/res/values-bn/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-bn/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"ডায়ালগ"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"বড় করা হয়েছে"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"আড়াল করা হয়েছে"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"ড্রপডাউন মেনু টগল করুন"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"বাতিল করুন"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"সার্চ করুন"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"নিচে দেওয়া সাজেশন"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-bs/strings.xml b/compose/material3/material3/src/androidMain/res/values-bs/strings.xml
index e6cc97c..5d5d633 100644
--- a/compose/material3/material3/src/androidMain/res/values-bs/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-bs/strings.xml
@@ -20,7 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"Dijaloški okvir"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"Prošireno"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"Suženo"</string>
- <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"Prekidač za padajući izbornik"</string>
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"Uključivanje/isključivanje padajućeg menija"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"Odbacivanje"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"Pretraživanje"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"Prijedlozi su u nastavku"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-ca/strings.xml b/compose/material3/material3/src/androidMain/res/values-ca/strings.xml
index 3c04f34..ef12ec8 100644
--- a/compose/material3/material3/src/androidMain/res/values-ca/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-ca/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"Quadre de diàleg"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"S\'ha desplegat"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"S\'ha replegat"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"Commuta el menú desplegable"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"Ignora"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"Cerca"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"Suggeriments a continuació"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-cs/strings.xml b/compose/material3/material3/src/androidMain/res/values-cs/strings.xml
index 5a82875..3e7d316 100644
--- a/compose/material3/material3/src/androidMain/res/values-cs/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-cs/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"Dialogové okno"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"Rozbaleno"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"Sbaleno"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"Přepnout rozbalovací nabídku"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"Zavřít"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"Vyhledávání"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"Níže jsou k dispozici návrhy"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-da/strings.xml b/compose/material3/material3/src/androidMain/res/values-da/strings.xml
index a45b72a..d4032f7 100644
--- a/compose/material3/material3/src/androidMain/res/values-da/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-da/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"Dialogboks"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"Udvidet"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"Skjult"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"Skift visningen af rullemenuen"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"Afvis"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"Søg"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"Forslag nedenfor"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-de/strings.xml b/compose/material3/material3/src/androidMain/res/values-de/strings.xml
index 57b1135..f5a97c9 100644
--- a/compose/material3/material3/src/androidMain/res/values-de/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-de/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"Dialogfeld"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"Maximiert"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"Minimiert"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"Drop-down-Menü maximieren/minimieren"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"Schließen"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"Suche"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"Vorschläge unten"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-el/strings.xml b/compose/material3/material3/src/androidMain/res/values-el/strings.xml
index be85d0b..aa67f3f 100644
--- a/compose/material3/material3/src/androidMain/res/values-el/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-el/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"Παράθυρο διαλόγου"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"Αναπτυγμένο"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"Συμπτυγμένο"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"Εναλλαγή αναπτυσσόμενου μενού"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"Παράβλεψη"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"Αναζήτηση"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"Προτάσεις παρακάτω"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-en-rAU/strings.xml b/compose/material3/material3/src/androidMain/res/values-en-rAU/strings.xml
index 2a911184..f95be07 100644
--- a/compose/material3/material3/src/androidMain/res/values-en-rAU/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-en-rAU/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"Dialogue"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"Expanded"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"Collapsed"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"Toggle drop-down menu"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"Dismiss"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"Search"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"Suggestions below"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-en-rGB/strings.xml b/compose/material3/material3/src/androidMain/res/values-en-rGB/strings.xml
index 2a911184..f95be07 100644
--- a/compose/material3/material3/src/androidMain/res/values-en-rGB/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-en-rGB/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"Dialogue"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"Expanded"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"Collapsed"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"Toggle drop-down menu"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"Dismiss"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"Search"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"Suggestions below"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-en-rIN/strings.xml b/compose/material3/material3/src/androidMain/res/values-en-rIN/strings.xml
index 2a911184..f95be07 100644
--- a/compose/material3/material3/src/androidMain/res/values-en-rIN/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-en-rIN/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"Dialogue"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"Expanded"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"Collapsed"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"Toggle drop-down menu"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"Dismiss"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"Search"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"Suggestions below"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-es-rUS/strings.xml b/compose/material3/material3/src/androidMain/res/values-es-rUS/strings.xml
index 64be6ea..5f613de 100644
--- a/compose/material3/material3/src/androidMain/res/values-es-rUS/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-es-rUS/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"Diálogo"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"Expandido"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"Contraído"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"Activar o desactivar menú desplegable"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"Descartar"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"Buscar"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"Sugerencias a continuación"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-es/strings.xml b/compose/material3/material3/src/androidMain/res/values-es/strings.xml
index a770326..063f711 100644
--- a/compose/material3/material3/src/androidMain/res/values-es/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-es/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"Cuadro de diálogo"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"Desplegado"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"Contraído"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"Mostrar/ocultar menú desplegable"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"Cerrar"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"Buscar"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"Sugerencias a continuación"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-et/strings.xml b/compose/material3/material3/src/androidMain/res/values-et/strings.xml
index dface1c..512cc41 100644
--- a/compose/material3/material3/src/androidMain/res/values-et/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-et/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"Dialoog"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"Laiendatud"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"Ahendatud"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"Rippmenüü lülitamine"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"Loobu"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"Otsing"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"Soovitused on allpool"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-eu/strings.xml b/compose/material3/material3/src/androidMain/res/values-eu/strings.xml
index 2487d99..5353c99 100644
--- a/compose/material3/material3/src/androidMain/res/values-eu/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-eu/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"Leihoa"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"Zabalduta"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"Tolestuta"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"Zabaldu/Tolestu goitibeherako menua"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"Baztertu"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"Bilatu"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"Iradokizunak daude behean"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-fi/strings.xml b/compose/material3/material3/src/androidMain/res/values-fi/strings.xml
index e3fb5e9..ca77732 100644
--- a/compose/material3/material3/src/androidMain/res/values-fi/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-fi/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"Valintaikkuna"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"Laajennettu"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"Tiivistetty"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"Laita avattava valikko päälle"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"Ohita"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"Hae"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"Ehdotuksia alla"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-fr-rCA/strings.xml b/compose/material3/material3/src/androidMain/res/values-fr-rCA/strings.xml
index 57254bf..5548120 100644
--- a/compose/material3/material3/src/androidMain/res/values-fr-rCA/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-fr-rCA/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"Dialogue"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"Développé"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"Réduit"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"Ouvrir ou fermer le menu déroulant"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"Fermer"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"Recherche"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"Suggestions ci-dessous"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-fr/strings.xml b/compose/material3/material3/src/androidMain/res/values-fr/strings.xml
index aeedebb..9c4fdfc 100644
--- a/compose/material3/material3/src/androidMain/res/values-fr/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-fr/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"Boîte de dialogue"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"Développé"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"Réduit"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"Activer/désactiver le menu déroulant"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"Fermer"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"Rechercher"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"Suggestions ci-dessous"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-gl/strings.xml b/compose/material3/material3/src/androidMain/res/values-gl/strings.xml
index bb49fe7..5e96091 100644
--- a/compose/material3/material3/src/androidMain/res/values-gl/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-gl/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"Cadro de diálogo"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"Despregado"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"Contraído"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"Activa ou desactiva o menú despregable"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"Pechar"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"Buscar"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"Hai suxestións abaixo"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-hu/strings.xml b/compose/material3/material3/src/androidMain/res/values-hu/strings.xml
index 558e144..28d4863 100644
--- a/compose/material3/material3/src/androidMain/res/values-hu/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-hu/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"Párbeszédpanel"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"Kibontva"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"Összecsukva"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"Legördülő menü átváltása"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"Elvetés"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"Keresés"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"Javaslatok alább"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-hy/strings.xml b/compose/material3/material3/src/androidMain/res/values-hy/strings.xml
index c7111b0..bb0ef84 100644
--- a/compose/material3/material3/src/androidMain/res/values-hy/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-hy/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"Երկխոսության պատուհան"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"Ծավալված է"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"Ծալված է"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"Բացել/փակել իջնող ցանկը"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"Փակել"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"Որոնում"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"Առաջարկները հասանելի են ստորև"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-it/strings.xml b/compose/material3/material3/src/androidMain/res/values-it/strings.xml
index dca7866..c3f0b06 100644
--- a/compose/material3/material3/src/androidMain/res/values-it/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-it/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"Finestra di dialogo"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"Espanso"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"Compresso"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"Attiva/disattiva menu a discesa"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"Chiudi"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"Cerca"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"Suggerimenti sotto"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-iw/strings.xml b/compose/material3/material3/src/androidMain/res/values-iw/strings.xml
index 0bf5c61..4ce0a71 100644
--- a/compose/material3/material3/src/androidMain/res/values-iw/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-iw/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"תיבת דו-שיח"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"התפריט הנפתח מורחב"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"התפריט הנפתח מכווץ"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"החלפת המצב של התפריט הנפתח"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"סגירה"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"חיפוש"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"הצעות מופיעות למטה"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-ja/strings.xml b/compose/material3/material3/src/androidMain/res/values-ja/strings.xml
index 0a3593c..15894b9 100644
--- a/compose/material3/material3/src/androidMain/res/values-ja/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-ja/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"ダイアログ"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"開いています"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"閉じています"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"プルダウン メニューを切り替えます"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"閉じる"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"検索"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"検索候補は次のとおりです"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-ka/strings.xml b/compose/material3/material3/src/androidMain/res/values-ka/strings.xml
index acc03b8..bf58b02 100644
--- a/compose/material3/material3/src/androidMain/res/values-ka/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-ka/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"დიალოგი"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"გაფართოებული"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"ჩაკეცილი"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"გადართეთ ჩამოსაშლელი მენიუ"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"დახურვა"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"ძიება"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"შემოთავაზებები იხილეთ ქვემოთ"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-kk/strings.xml b/compose/material3/material3/src/androidMain/res/values-kk/strings.xml
index 8187675..8a1b7861 100644
--- a/compose/material3/material3/src/androidMain/res/values-kk/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-kk/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"Диалогтік терезе"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"Жайылды"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"Жиылды"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"Ашылмалы мәзірді ауыстыру"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"Жабу"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"Іздеу"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"Төмендегі ұсыныстар"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-kn/strings.xml b/compose/material3/material3/src/androidMain/res/values-kn/strings.xml
index 85008b6..5962fab 100644
--- a/compose/material3/material3/src/androidMain/res/values-kn/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-kn/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"ಡೈಲಾಗ್"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"ವಿಸ್ತರಿಸಲಾಗಿದೆ"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"ಕುಗ್ಗಿಸಲಾಗಿದೆ"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"ಡ್ರಾಪ್ಡೌನ್ ಮೆನುವನ್ನು ಟಾಗಲ್ ಮಾಡಿ"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"ವಜಾಗೊಳಿಸಿ"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"ಹುಡುಕಿ"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"ಸಲಹೆಗಳನ್ನು ಕೆಳಗೆ ನೀಡಲಾಗಿದೆ"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-ko/strings.xml b/compose/material3/material3/src/androidMain/res/values-ko/strings.xml
index bfdfeb2..e39a2e2 100644
--- a/compose/material3/material3/src/androidMain/res/values-ko/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-ko/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"대화상자"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"펼침"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"접힘"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"전환 드롭다운 메뉴"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"닫기"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"검색"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"아래의 추천 검색어"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-lt/strings.xml b/compose/material3/material3/src/androidMain/res/values-lt/strings.xml
index 9a96cc5..4157437 100644
--- a/compose/material3/material3/src/androidMain/res/values-lt/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-lt/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"Dialogo langas"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"Išskleista"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"Sutraukta"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"Perjungti išskleidžiamąjį meniu"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"Atsisakyti"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"Paieška"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"Pasiūlymai pateikti toliau"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-lv/strings.xml b/compose/material3/material3/src/androidMain/res/values-lv/strings.xml
index 7aaf2d9..fec1a52 100644
--- a/compose/material3/material3/src/androidMain/res/values-lv/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-lv/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"Dialoglodziņš"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"Izvērsta"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"Sakļauta"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"Pārslēgt nolaižamo izvēlni"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"Nerādīt"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"Meklēšana"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"Tālāk ir sniegti ieteikumi"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-mk/strings.xml b/compose/material3/material3/src/androidMain/res/values-mk/strings.xml
index 6d5f51b4..87aeea6 100644
--- a/compose/material3/material3/src/androidMain/res/values-mk/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-mk/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"Дијалог"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"Проширено"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"Собрано"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"Вклучување/исклучување паѓачко мени"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"Отфрли"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"Пребарување"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"Предлозите се наведени подолу"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-mn/strings.xml b/compose/material3/material3/src/androidMain/res/values-mn/strings.xml
index 8df6c2d..419bc71 100644
--- a/compose/material3/material3/src/androidMain/res/values-mn/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-mn/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"Харилцах цонх"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"Дэлгэсэн"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"Хураасан"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"Унадаг цэсийг асаах/унтраах"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"Хаах"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"Хайх"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"Доорх зөвлөмжүүд"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-mr/strings.xml b/compose/material3/material3/src/androidMain/res/values-mr/strings.xml
index 141f0bb..70e68f9 100644
--- a/compose/material3/material3/src/androidMain/res/values-mr/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-mr/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"डायलॉग"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"विस्तारित केला आहे"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"कोलॅप्स केला आहे"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"ड्रॉपडाउन मेनू टॉगल करा"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"डिसमिस करा"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"शोधा"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"सूचना खाली आहेत"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-my/strings.xml b/compose/material3/material3/src/androidMain/res/values-my/strings.xml
index 9bca021..a94a4b3 100644
--- a/compose/material3/material3/src/androidMain/res/values-my/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-my/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"ဒိုင်ယာလော့"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"ချဲ့ထားသည်"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"ချုံ့ထားသည်"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"ဆွဲချမီနူးကို ပြောင်းရန်"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"ပယ်ရန်"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"ရှာရန်"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"အကြံပြုချက်များသည် အောက်တွင်ရှိသည်"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-nb/strings.xml b/compose/material3/material3/src/androidMain/res/values-nb/strings.xml
index f18afdb5..7b79658 100644
--- a/compose/material3/material3/src/androidMain/res/values-nb/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-nb/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"Dialogboks"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"Vises"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"Skjules"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"Slå rullegardinmeny av/på"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"Lukk"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"Søk"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"Du finner forslag nedenfor"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-nl/strings.xml b/compose/material3/material3/src/androidMain/res/values-nl/strings.xml
index 0dc1cb0..1070dab 100644
--- a/compose/material3/material3/src/androidMain/res/values-nl/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-nl/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"Dialoogvenster"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"Uitgevouwen"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"Samengevouwen"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"Dropdownmenu aan-/uitzetten"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"Sluiten"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"Zoeken"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"Suggesties hieronder"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-pa/strings.xml b/compose/material3/material3/src/androidMain/res/values-pa/strings.xml
index b70e23d..037b577 100644
--- a/compose/material3/material3/src/androidMain/res/values-pa/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-pa/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"ਡਾਇਲੌਗ"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"ਵਿਸਤਾਰ ਕੀਤਾ ਗਿਆ"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"ਸਮੇਟਿਆ ਗਿਆ"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"ਡ੍ਰੌਪ-ਡਾਊਨ ਮੀਨੂ ਨੂੰ ਟੌਗਲ ਕਰੋ"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"ਖਾਰਜ ਕਰੋ"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"ਖੋਜੋ"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"ਸੁਝਾਅ ਹੇਠਾਂ ਹਨ"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-pt-rBR/strings.xml b/compose/material3/material3/src/androidMain/res/values-pt-rBR/strings.xml
index bb753a8..828b251 100644
--- a/compose/material3/material3/src/androidMain/res/values-pt-rBR/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-pt-rBR/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"Caixa de diálogo"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"Aberto"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"Fechado"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"Ativar/desativar o menu suspenso"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"Dispensar"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"Pesquisar"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"Sugestões abaixo"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-pt/strings.xml b/compose/material3/material3/src/androidMain/res/values-pt/strings.xml
index bb753a8..828b251 100644
--- a/compose/material3/material3/src/androidMain/res/values-pt/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-pt/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"Caixa de diálogo"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"Aberto"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"Fechado"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"Ativar/desativar o menu suspenso"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"Dispensar"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"Pesquisar"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"Sugestões abaixo"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-ro/strings.xml b/compose/material3/material3/src/androidMain/res/values-ro/strings.xml
index 077bd43..2045997 100644
--- a/compose/material3/material3/src/androidMain/res/values-ro/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-ro/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"Dialog"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"Extins"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"Restrâns"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"Comută meniul drop-down"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"Închide"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"Caută"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"Sugestii mai jos"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-si/strings.xml b/compose/material3/material3/src/androidMain/res/values-si/strings.xml
index c638936..2dc21c0 100644
--- a/compose/material3/material3/src/androidMain/res/values-si/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-si/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"සංවාදය"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"දිග හරින ලදි"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"හකුළන ලදි"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"පතන මෙනුව ටොගල් කරන්න"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"අස් කරන්න"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"සෙවීම"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"පහත යෝජනා"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-sq/strings.xml b/compose/material3/material3/src/androidMain/res/values-sq/strings.xml
index 0465363..c76613d 100644
--- a/compose/material3/material3/src/androidMain/res/values-sq/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-sq/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"Dialogu"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"Zgjeruar"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"Palosur"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"Zgjero/palos menynë me lëshim poshtë"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"Hiq"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"Kërkimi"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"Sugjerimet më poshtë"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-sv/strings.xml b/compose/material3/material3/src/androidMain/res/values-sv/strings.xml
index 45a0038..59d9068 100644
--- a/compose/material3/material3/src/androidMain/res/values-sv/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-sv/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"Dialogruta"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"Utökad"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"Komprimerad"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"Utöka/komprimera rullgardinsmeny"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"Stäng"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"Sök"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"Se förslag nedan"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-ta/strings.xml b/compose/material3/material3/src/androidMain/res/values-ta/strings.xml
index cd65b2c..8ff4dbb 100644
--- a/compose/material3/material3/src/androidMain/res/values-ta/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-ta/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"உரையாடல்"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"விரிவாக்கப்பட்டது"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"சுருக்கப்பட்டது"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"கீழ்த்தோன்றல் மெனுவை நிலைமாற்றும்"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"நிராகரிக்கும்"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"தேடல்"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"பரிந்துரைகள் கீழே கிடைக்கும்"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-tr/strings.xml b/compose/material3/material3/src/androidMain/res/values-tr/strings.xml
index f5d84bf..110d0eb 100644
--- a/compose/material3/material3/src/androidMain/res/values-tr/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-tr/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"Dialog"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"Genişletildi"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"Daraltıldı"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"Açılır menüyü açın/kapatın"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"Kapat"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"Arama"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"Önerileri aşağıda bulabilirsiniz"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-uk/strings.xml b/compose/material3/material3/src/androidMain/res/values-uk/strings.xml
index 3f4fca4..3ce4e58 100644
--- a/compose/material3/material3/src/androidMain/res/values-uk/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-uk/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"Вікно"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"Розгорнуто"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"Згорнуто"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"Згорнути або розгорнути спадне меню"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"Закрити"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"Пошук"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"Підказки внизу"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-uz/strings.xml b/compose/material3/material3/src/androidMain/res/values-uz/strings.xml
index 36b567e..95be160 100644
--- a/compose/material3/material3/src/androidMain/res/values-uz/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-uz/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"Muloqot oynasi"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"Yoyilgan"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"Yigʻilgan"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"Pastga ochiluvchi menyuni koʻrsatish/yashirish"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"Yopish"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"Qidiruv"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"Takliflar quyida"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-vi/strings.xml b/compose/material3/material3/src/androidMain/res/values-vi/strings.xml
index cb322a4..33eb1a6 100644
--- a/compose/material3/material3/src/androidMain/res/values-vi/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-vi/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"Hộp thoại"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"Đã mở rộng"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"Đã thu gọn"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"Bật/tắt trình đơn thả xuống"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"Đóng"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"Tìm kiếm"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"Các đề xuất ở bên dưới"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-zh-rCN/strings.xml b/compose/material3/material3/src/androidMain/res/values-zh-rCN/strings.xml
index c2b04c2..839fc1b 100644
--- a/compose/material3/material3/src/androidMain/res/values-zh-rCN/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-zh-rCN/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"对话框"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"已展开"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"已收起"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"展开/收起下拉菜单"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"关闭"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"搜索"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"以下是搜索建议"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-zh-rHK/strings.xml b/compose/material3/material3/src/androidMain/res/values-zh-rHK/strings.xml
index 51b6d1c..d1e43fb 100644
--- a/compose/material3/material3/src/androidMain/res/values-zh-rHK/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-zh-rHK/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"對話框"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"已展開"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"已收合"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"切換下拉式選單"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"關閉"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"搜尋"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"建議如下"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-zh-rTW/strings.xml b/compose/material3/material3/src/androidMain/res/values-zh-rTW/strings.xml
index 39ce414..2c2dc9b 100644
--- a/compose/material3/material3/src/androidMain/res/values-zh-rTW/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-zh-rTW/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"對話方塊"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"已展開"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"已收合"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"切換鈕下拉式選單"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"關閉"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"搜尋"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"建議如下"</string>
diff --git a/compose/material3/material3/src/androidMain/res/values-zu/strings.xml b/compose/material3/material3/src/androidMain/res/values-zu/strings.xml
index ac7b6fa..cb3f162 100644
--- a/compose/material3/material3/src/androidMain/res/values-zu/strings.xml
+++ b/compose/material3/material3/src/androidMain/res/values-zu/strings.xml
@@ -20,8 +20,7 @@
<string name="m3c_dialog" msgid="7617233117134790350">"Ibhokisi"</string>
<string name="m3c_dropdown_menu_expanded" msgid="2360841780724299882">"Kunwetshiwe"</string>
<string name="m3c_dropdown_menu_collapsed" msgid="3177828188723359358">"Kugoqiwe"</string>
- <!-- no translation found for m3c_dropdown_menu_toggle (8687821690726149911) -->
- <skip />
+ <string name="m3c_dropdown_menu_toggle" msgid="8687821690726149911">"Guqula imenyu yokwehlayo"</string>
<string name="m3c_snackbar_dismiss" msgid="6152755701819882931">"Chitha"</string>
<string name="m3c_search_bar_search" msgid="6152806324422087846">"Sesha"</string>
<string name="m3c_suggestions_available" msgid="7655536806087401899">"Iziphakamiso ngezansi"</string>
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/MaterialTheme.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/MaterialTheme.kt
index c7a61a2..6f88a0a 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/MaterialTheme.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/MaterialTheme.kt
@@ -17,8 +17,6 @@
package androidx.compose.material3
import androidx.compose.foundation.LocalIndication
-import androidx.compose.foundation.contextmenu.ContextMenuColors
-import androidx.compose.foundation.contextmenu.LocalContextMenuColors
import androidx.compose.foundation.text.selection.LocalTextSelectionColors
import androidx.compose.foundation.text.selection.TextSelectionColors
import androidx.compose.runtime.Composable
@@ -59,7 +57,6 @@
) {
val rippleIndication = rippleOrFallbackImplementation()
val selectionColors = rememberTextSelectionColors(colorScheme)
- val contextMenuColors = rememberContextMenuColors(colorScheme)
@Suppress("DEPRECATION_ERROR")
CompositionLocalProvider(
LocalColorScheme provides colorScheme,
@@ -69,7 +66,6 @@
LocalShapes provides shapes,
LocalTextSelectionColors provides selectionColors,
LocalTypography provides typography,
- LocalContextMenuColors provides contextMenuColors,
) {
ProvideTextStyle(value = typography.bodyLarge, content = content)
}
@@ -119,33 +115,3 @@
/*@VisibleForTesting*/
internal const val TextSelectionBackgroundOpacity = 0.4f
-
-/** See [the spec](https://m3.material.io/components/menus/specs). */
-/*@VisibleForTesting*/
-internal const val DisabledContextMenuContentOpacity = 0.38f
-
-/**
- * Remembers a [ContextMenuColors] based on [colorScheme].
- *
- * The background color will be [ColorScheme.surfaceContainer], the text color will be
- * [ColorScheme.onSurface], the icon color will be [ColorScheme.onSurfaceVariant],
- * and the disabled text/icon colors will be the same as the text color with the
- * opacity of [DisabledContextMenuContentOpacity] applied.
- * This matches [DropdownMenuContent] and [DropdownMenuItemContent].
- */
-@Composable
-private fun rememberContextMenuColors(colorScheme: ColorScheme): ContextMenuColors {
- val backgroundColor = colorScheme.surfaceContainer
- val textColor = colorScheme.onSurface
- val iconColor = colorScheme.onSurfaceVariant
- return remember(backgroundColor, textColor, iconColor) {
- val disabledColor = textColor.copy(alpha = DisabledContextMenuContentOpacity)
- ContextMenuColors(
- backgroundColor = backgroundColor,
- textColor = textColor,
- iconColor = iconColor,
- disabledTextColor = disabledColor,
- disabledIconColor = disabledColor,
- )
- }
-}
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ProgressIndicator.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ProgressIndicator.kt
index d0bcba7..9381e45 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ProgressIndicator.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ProgressIndicator.kt
@@ -136,8 +136,9 @@
trackColor: Color = ProgressIndicatorDefaults.linearTrackColor,
strokeCap: StrokeCap = ProgressIndicatorDefaults.LinearStrokeCap,
gapSize: Dp = ProgressIndicatorDefaults.LinearIndicatorTrackGapSize,
- drawStopIndicator: (DrawScope.() -> Unit)? = {
+ drawStopIndicator: DrawScope.() -> Unit = {
drawStopIndicator(
+ drawScope = this,
stopSize = ProgressIndicatorDefaults.LinearTrackStopIndicatorSize,
color = color,
strokeCap = strokeCap
@@ -175,7 +176,7 @@
0f, currentCoercedProgress, color, strokeWidth, strokeCap
)
// stop
- drawStopIndicator?.invoke(this)
+ drawStopIndicator(this)
}
}
@@ -894,35 +895,40 @@
/**
* Draws the stop indicator at the end of the track.
*
+ * @param drawScope the [DrawScope]
* @param stopSize size of this stop indicator, it cannot be bigger than the track's height
* @param color color of this stop indicator
* @param strokeCap stroke cap to use for the ends of this stop indicator
*/
- fun DrawScope.drawStopIndicator(
+ fun drawStopIndicator(
+ drawScope: DrawScope,
stopSize: Dp,
color: Color,
strokeCap: StrokeCap,
) {
- val adjustedStopSize = min(stopSize.toPx(), size.height) // Stop can't be bigger than track
- val stopOffset = (size.height - adjustedStopSize) / 2 // Offset from end
- if (strokeCap == StrokeCap.Round) {
- drawCircle(
- color = color,
- radius = adjustedStopSize / 2f,
- center = Offset(
- x = size.width - (adjustedStopSize / 2f) - stopOffset,
- y = size.height / 2f
+ with(drawScope) {
+ val adjustedStopSize =
+ min(stopSize.toPx(), size.height) // Stop can't be bigger than track
+ val stopOffset = (size.height - adjustedStopSize) / 2 // Offset from end
+ if (strokeCap == StrokeCap.Round) {
+ drawCircle(
+ color = color,
+ radius = adjustedStopSize / 2f,
+ center = Offset(
+ x = size.width - (adjustedStopSize / 2f) - stopOffset,
+ y = size.height / 2f
+ )
)
- )
- } else {
- drawRect(
- color = color,
- topLeft = Offset(
- x = size.width - adjustedStopSize - stopOffset,
- y = (size.height - adjustedStopSize) / 2f
- ),
- size = Size(width = adjustedStopSize, height = adjustedStopSize)
- )
+ } else {
+ drawRect(
+ color = color,
+ topLeft = Offset(
+ x = size.width - adjustedStopSize - stopOffset,
+ y = (size.height - adjustedStopSize) / 2f
+ ),
+ size = Size(width = adjustedStopSize, height = adjustedStopSize)
+ )
+ }
}
}
}
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Slider.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Slider.kt
index 2bb7881..e69ce27 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Slider.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Slider.kt
@@ -1164,13 +1164,15 @@
colors: SliderColors = colors(),
drawStopIndicator: (DrawScope.(Offset) -> Unit)? = {
drawStopIndicator(
+ drawScope = this,
offset = it,
color = colors.activeTrackColor,
size = TrackStopIndicatorSize
)
},
- drawTick: (DrawScope.(Offset, Color) -> Unit)? = { offset, color ->
+ drawTick: DrawScope.(Offset, Color) -> Unit = { offset, color ->
drawStopIndicator(
+ drawScope = this,
offset = offset,
color = color,
size = TickSize
@@ -1272,13 +1274,15 @@
colors: SliderColors = colors(),
drawStopIndicator: (DrawScope.(Offset) -> Unit)? = {
drawStopIndicator(
+ drawScope = this,
offset = it,
color = colors.activeTrackColor,
size = TrackStopIndicatorSize
)
},
- drawTick: (DrawScope.(Offset, Color) -> Unit)? = { offset, color ->
+ drawTick: DrawScope.(Offset, Color) -> Unit = { offset, color ->
drawStopIndicator(
+ drawScope = this,
offset = offset,
color = color,
size = TickSize
@@ -1329,7 +1333,7 @@
thumbTrackGapSize: Dp,
trackInsideCornerSize: Dp,
drawStopIndicator: (DrawScope.(Offset) -> Unit)?,
- drawTick: (DrawScope.(Offset, Color) -> Unit)?,
+ drawTick: DrawScope.(Offset, Color) -> Unit,
isRangeSlider: Boolean
) {
val sliderStart = Offset(0f, center.y)
@@ -1412,7 +1416,7 @@
if ((isRangeSlider && center.x in startGap) || center.x in endGap) {
return@forEachIndexed
}
- drawTick?.invoke(
+ drawTick(
this,
center, // offset
if (outsideFraction) inactiveTickColor else activeTickColor // color
@@ -1474,16 +1478,19 @@
halfRectPath.rewind()
}
- private fun DrawScope.drawStopIndicator(
+ private fun drawStopIndicator(
+ drawScope: DrawScope,
offset: Offset,
size: Dp,
color: Color
) {
- drawCircle(
- color = color,
- center = offset,
- radius = size.toPx() / 2f
- )
+ with(drawScope) {
+ drawCircle(
+ color = color,
+ center = offset,
+ radius = size.toPx() / 2f
+ )
+ }
}
/**
diff --git a/compose/ui/ui-graphics/src/androidInstrumentedTest/kotlin/androidx/compose/ui/graphics/ColorTest.kt b/compose/ui/ui-graphics/src/androidInstrumentedTest/kotlin/androidx/compose/ui/graphics/ColorTest.kt
index cfe1192..9217737 100644
--- a/compose/ui/ui-graphics/src/androidInstrumentedTest/kotlin/androidx/compose/ui/graphics/ColorTest.kt
+++ b/compose/ui/ui-graphics/src/androidInstrumentedTest/kotlin/androidx/compose/ui/graphics/ColorTest.kt
@@ -24,7 +24,6 @@
import kotlin.math.abs
import kotlin.test.Test
import kotlin.test.assertEquals
-import kotlin.test.assertFailsWith
import kotlin.test.assertFalse
import kotlin.test.assertNotEquals
import kotlin.test.assertTrue
@@ -207,58 +206,42 @@
@Test
fun srgbOutOfBounds_highRed() {
- assertFailsWith<IllegalArgumentException> {
- Color(2f, 0f, 0f)
- }
+ assertEquals(1.0f, Color(2f, 0f, 0f).red)
}
@Test
fun srgbOutOfBounds_lowRed() {
- assertFailsWith<IllegalArgumentException> {
- Color(-1f, 0f, 0f)
- }
+ assertEquals(0.0f, Color(-1f, 0f, 0f).red)
}
@Test
fun srgbOutOfBounds_highGreen() {
- assertFailsWith<IllegalArgumentException> {
- Color(0f, 2f, 0f)
- }
+ assertEquals(1.0f, Color(0f, 2f, 0f).green)
}
@Test
fun srgbOutOfBounds_lowGreen() {
- assertFailsWith<IllegalArgumentException> {
- Color(0f, -1f, 0f)
- }
+ assertEquals(0.0f, Color(0f, -1f, 0f).green)
}
@Test
fun srgbOutOfBounds_highBlue() {
- assertFailsWith<IllegalArgumentException> {
- Color(0f, 0f, 2f)
- }
+ assertEquals(1.0f, Color(0f, 0f, 2f).blue)
}
@Test
fun srgbOutOfBounds_lowBlue() {
- assertFailsWith<IllegalArgumentException> {
- Color(0f, 0f, -1f)
- }
+ assertEquals(0.0f, Color(0f, 0f, -1f).blue)
}
@Test
fun srgbOutOfBounds_highAlpha() {
- assertFailsWith<IllegalArgumentException> {
- Color(0f, 0f, 0f, 2f)
- }
+ assertEquals(1.0f, Color(0f, 0f, 0f, 2f).alpha)
}
@Test
fun srgbOutOfBounds_lowAlpha() {
- assertFailsWith<IllegalArgumentException> {
- Color(0f, 0f, 0f, -1f)
- }
+ assertEquals(0.0f, Color(0f, 0f, 0f, -1f).alpha)
}
@Test
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Color.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Color.kt
index 873cedd..469f415 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Color.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Color.kt
@@ -403,10 +403,19 @@
internal const val UnspecifiedColor = 0x10UL
/**
- * Create a [Color] by passing individual [red], [green], [blue], [alpha], and [colorSpace]
- * components. The default [color space][ColorSpace] is [sRGB][ColorSpaces.Srgb] and
- * the default [alpha] is `1.0` (opaque). [colorSpace] must have a [ColorSpace.componentCount] of
- * 3.
+ * Create a [Color] by passing individual [red], [green], [blue], [alpha],
+ * and [colorSpace] components. The default [color space][ColorSpace]
+ * is [sRGB][ColorSpaces.Srgb] and the default [alpha] is `1.0`
+ * (opaque).
+ *
+ * If the [red], [green], or [blue] values are outside of the range defined
+ * by [colorSpace] (see [ColorSpace.getMinValue] and [ColorSpace.getMaxValue],
+ * these values get clamped appropriately to be within range.
+ *
+ * @throws IllegalArgumentException If [colorSpace] does not have
+ * [ColorSpace.componentCount] equal to 3.
+ * @throws IllegalArgumentException If [colorSpace] has an [ColorSpace.id]
+ * set to [ColorSpace.MinId].
*/
@Stable
fun Color(
@@ -416,21 +425,12 @@
alpha: Float = 1f,
colorSpace: ColorSpace = ColorSpaces.Srgb
): Color {
- requirePrecondition(
- red in colorSpace.getMinValue(0)..colorSpace.getMaxValue(0) &&
- green in colorSpace.getMinValue(1)..colorSpace.getMaxValue(1) &&
- blue in colorSpace.getMinValue(2)..colorSpace.getMaxValue(2) &&
- alpha in 0f..1f
- ) {
- "red = $red, green = $green, blue = $blue, alpha = $alpha outside the range for $colorSpace"
- }
-
if (colorSpace.isSrgb) {
val argb = (
- ((alpha * 255.0f + 0.5f).toInt() shl 24) or
- ((red * 255.0f + 0.5f).toInt() shl 16) or
- ((green * 255.0f + 0.5f).toInt() shl 8) or
- (blue * 255.0f + 0.5f).toInt()
+ ((alpha.fastCoerceIn(0.0f, 1.0f) * 255.0f + 0.5f).toInt() shl 24) or
+ ((red.fastCoerceIn(0.0f, 1.0f) * 255.0f + 0.5f).toInt() shl 16) or
+ ((green.fastCoerceIn(0.0f, 1.0f) * 255.0f + 0.5f).toInt() shl 8) or
+ (blue.fastCoerceIn(0.0f, 1.0f) * 255.0f + 0.5f).toInt()
)
return Color(argb.toULong() shl 32)
}
@@ -444,11 +444,10 @@
"Unknown color space, please use a color space in ColorSpaces"
}
- val r = floatToHalf(red)
- val g = floatToHalf(green)
- val b = floatToHalf(blue)
-
- val a = (max(0.0f, min(alpha, 1.0f)) * 1023.0f + 0.5f).toInt()
+ val r = floatToHalf(red.fastCoerceIn(colorSpace.getMinValue(0), colorSpace.getMaxValue(0)))
+ val g = floatToHalf(green.fastCoerceIn(colorSpace.getMinValue(1), colorSpace.getMaxValue(1)))
+ val b = floatToHalf(blue.fastCoerceIn(colorSpace.getMinValue(2), colorSpace.getMaxValue(2)))
+ val a = (alpha.fastCoerceIn(0.0f, 1.0f) * 1023.0f + 0.5f).toInt()
return Color(
(
@@ -583,11 +582,16 @@
val endA = endColor.green
val endB = endColor.blue
+ // We need to clamp the input fraction since over/undershoot easing curves
+ // can yield fractions outside of the 0..1 range, which would in turn cause
+ // Lab/alpha values to be outside of the valid color range.
+ // Clamping the fraction is cheaper than clamping all 4 components separately.
+ val t = fraction.fastCoerceIn(0.0f, 1.0f)
val interpolated = UncheckedColor(
- lerp(startL, endL, fraction),
- lerp(startA, endA, fraction),
- lerp(startB, endB, fraction),
- lerp(startAlpha, endAlpha, fraction),
+ lerp(startL, endL, t),
+ lerp(startA, endA, t),
+ lerp(startB, endB, t),
+ lerp(startAlpha, endAlpha, t),
colorSpace
)
return interpolated.convert(stop.colorSpace)
diff --git a/compose/ui/ui-tooling/build.gradle b/compose/ui/ui-tooling/build.gradle
index cb5c42f..98f9161 100644
--- a/compose/ui/ui-tooling/build.gradle
+++ b/compose/ui/ui-tooling/build.gradle
@@ -138,7 +138,7 @@
description = "Compose tooling library. This library exposes information to our tools for better IDE support."
legacyDisableKotlinStrictApiMode = true
samples(project(":compose:animation:animation:animation-samples"))
- // samples(project(":compose:animation:animation-core:animation-core-samples"))TODO(b/318840087)
+ // samples(project(":compose:animation:animation-core:animation-core-samples")) TODO(b/318840087)
}
android {
diff --git a/compose/ui/ui-tooling/src/androidInstrumentedTest/kotlin/androidx/compose/ui/tooling/animation/clock/AnimateXAsStateClockTest.kt b/compose/ui/ui-tooling/src/androidInstrumentedTest/kotlin/androidx/compose/ui/tooling/animation/clock/AnimateXAsStateClockTest.kt
index 80ee278..c649199 100644
--- a/compose/ui/ui-tooling/src/androidInstrumentedTest/kotlin/androidx/compose/ui/tooling/animation/clock/AnimateXAsStateClockTest.kt
+++ b/compose/ui/ui-tooling/src/androidInstrumentedTest/kotlin/androidx/compose/ui/tooling/animation/clock/AnimateXAsStateClockTest.kt
@@ -413,8 +413,8 @@
clock.setStateParameters(listOf(Color.Red), listOf(0.1f, 0.2f, 0.3f))
clock.setStateParameters(listOf(Color.Red), emptyList<Color>())
clock.setStateParameters(listOf(null), listOf(null))
- // Invalid arguments for color.
- clock.setStateParameters(listOf(10f, 10f, 10f, 10f), listOf(10f, 10f, 10f, 10f))
+ // Values outside of color range, should get clamped
+ clock.setStateParameters(listOf(0f, 0f, 10f, 10f), listOf(10f, 10f, 0f, 10f))
}
// State hasn't changed.
checkUpdatedState(clock, label = "ColorAnimation",
diff --git a/compose/ui/ui/api/current.txt b/compose/ui/ui/api/current.txt
index 3c8c3a5..d44f325 100644
--- a/compose/ui/ui/api/current.txt
+++ b/compose/ui/ui/api/current.txt
@@ -2702,7 +2702,7 @@
}
public interface RootForTest {
- method @SuppressCompatibility @androidx.compose.ui.ExperimentalComposeUiApi public void forceAccessibilityForTesting();
+ method @SuppressCompatibility @androidx.compose.ui.ExperimentalComposeUiApi public void forceAccessibilityForTesting(boolean enable);
method public androidx.compose.ui.unit.Density getDensity();
method public androidx.compose.ui.semantics.SemanticsOwner getSemanticsOwner();
method @Deprecated public androidx.compose.ui.text.input.TextInputService getTextInputService();
@@ -3545,7 +3545,7 @@
method public static String getPaneTitle(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
method public static androidx.compose.ui.semantics.ProgressBarRangeInfo getProgressBarRangeInfo(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
method public static int getRole(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
- method public static void getScrollViewportLength(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function1<? super java.util.List<java.lang.Float>,java.lang.Boolean> action);
+ method public static void getScrollViewportLength(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Float?> action);
method public static boolean getSelected(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
method public static String getStateDescription(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
method public static String getTestTag(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
diff --git a/compose/ui/ui/api/restricted_current.txt b/compose/ui/ui/api/restricted_current.txt
index 8805d2c..d152116 100644
--- a/compose/ui/ui/api/restricted_current.txt
+++ b/compose/ui/ui/api/restricted_current.txt
@@ -2755,7 +2755,7 @@
}
public interface RootForTest {
- method @SuppressCompatibility @androidx.compose.ui.ExperimentalComposeUiApi public void forceAccessibilityForTesting();
+ method @SuppressCompatibility @androidx.compose.ui.ExperimentalComposeUiApi public void forceAccessibilityForTesting(boolean enable);
method public androidx.compose.ui.unit.Density getDensity();
method public androidx.compose.ui.semantics.SemanticsOwner getSemanticsOwner();
method @Deprecated public androidx.compose.ui.text.input.TextInputService getTextInputService();
@@ -3605,7 +3605,7 @@
method public static String getPaneTitle(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
method public static androidx.compose.ui.semantics.ProgressBarRangeInfo getProgressBarRangeInfo(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
method public static int getRole(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
- method public static void getScrollViewportLength(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function1<? super java.util.List<java.lang.Float>,java.lang.Boolean> action);
+ method public static void getScrollViewportLength(androidx.compose.ui.semantics.SemanticsPropertyReceiver, optional String? label, kotlin.jvm.functions.Function0<java.lang.Float?> action);
method public static boolean getSelected(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
method public static String getStateDescription(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
method public static String getTestTag(androidx.compose.ui.semantics.SemanticsPropertyReceiver);
diff --git a/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/accessibility/AccessibilityBenchmark.kt b/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/accessibility/AccessibilityBenchmark.kt
index 726b920..a1b5633e 100644
--- a/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/accessibility/AccessibilityBenchmark.kt
+++ b/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/accessibility/AccessibilityBenchmark.kt
@@ -18,6 +18,8 @@
import android.view.View
import android.view.accessibility.AccessibilityNodeProvider
+import androidx.benchmark.ExperimentalBenchmarkConfigApi
+import androidx.benchmark.MicrobenchmarkConfig
import androidx.benchmark.junit4.BenchmarkRule
import androidx.benchmark.junit4.measureRepeated
import androidx.compose.foundation.layout.Column
@@ -53,8 +55,9 @@
@get:Rule
val composeTestRule = createComposeRule()
+ @OptIn(ExperimentalBenchmarkConfigApi::class)
@get:Rule
- val benchmarkRule = BenchmarkRule()
+ val benchmarkRule = BenchmarkRule(MicrobenchmarkConfig(traceAppTagEnabled = true))
private lateinit var composeView: View
private lateinit var provider: AccessibilityNodeProvider
@@ -143,7 +146,7 @@
// this is just a temporary workaround for now.
LaunchedEffect(Unit) {
// Ensure that accessibility is enabled for testing.
- (composeView as RootForTest).forceAccessibilityForTesting()
+ (composeView as RootForTest).forceAccessibilityForTesting(true)
}
}
}
diff --git a/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/accessibility/SemanticsEventsBenchmark.kt b/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/accessibility/SemanticsEventsBenchmark.kt
index 157105c..4611573 100644
--- a/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/accessibility/SemanticsEventsBenchmark.kt
+++ b/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/accessibility/SemanticsEventsBenchmark.kt
@@ -16,6 +16,8 @@
package androidx.compose.ui.benchmark.accessibility
+import androidx.benchmark.ExperimentalBenchmarkConfigApi
+import androidx.benchmark.MicrobenchmarkConfig
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.text.BasicText
@@ -27,7 +29,7 @@
import androidx.compose.testutils.ComposeTestCase
import androidx.compose.testutils.ToggleableTestCase
import androidx.compose.testutils.benchmark.ComposeBenchmarkRule
-import androidx.compose.testutils.benchmark.toggleStateBenchmarkMeasure
+import androidx.compose.testutils.benchmark.toggleStateBenchmarkComposeMeasureLayout
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.node.RootForTest
@@ -50,8 +52,9 @@
@RunWith(AndroidJUnit4::class)
class SemanticsEventsBenchmark {
+ @OptIn(ExperimentalBenchmarkConfigApi::class)
@get:Rule
- val benchmarkRule = ComposeBenchmarkRule()
+ val benchmarkRule = ComposeBenchmarkRule(MicrobenchmarkConfig(traceAppTagEnabled = true))
private val semanticsFactory = { SemanticsTestCase() }
@@ -60,7 +63,7 @@
*/
@Test
fun sendSemanticsEvents() {
- benchmarkRule.toggleStateBenchmarkMeasure(
+ benchmarkRule.toggleStateBenchmarkComposeMeasureLayout(
caseFactory = semanticsFactory
)
}
@@ -95,7 +98,7 @@
// Make sure the delay between batches of a11y events is set to zero.
(composeView as RootForTest).setAccessibilityEventBatchIntervalMillis(0L)
// Ensure that accessibility is enabled for testing.
- (composeView as RootForTest).forceAccessibilityForTesting()
+ (composeView as RootForTest).forceAccessibilityForTesting(true)
}
}
diff --git a/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/accessibility/TextAccessibilityBenchmark.kt b/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/accessibility/TextAccessibilityBenchmark.kt
new file mode 100644
index 0000000..0186a36
--- /dev/null
+++ b/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/accessibility/TextAccessibilityBenchmark.kt
@@ -0,0 +1,416 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.benchmark.accessibility
+
+import android.view.View
+import android.view.accessibility.AccessibilityNodeProvider
+import android.view.accessibility.AccessibilityNodeProvider.HOST_VIEW_ID
+import androidx.annotation.UiThread
+import androidx.benchmark.ExperimentalBenchmarkConfigApi
+import androidx.benchmark.MicrobenchmarkConfig
+import androidx.benchmark.junit4.BenchmarkRule
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.foundation.lazy.rememberLazyListState
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.setValue
+import androidx.compose.testutils.ComposeTestCase
+import androidx.compose.testutils.ToggleableTestCase
+import androidx.compose.testutils.assertNoPendingChanges
+import androidx.compose.testutils.benchmark.ComposeBenchmarkRule
+import androidx.compose.testutils.benchmark.toggleStateBenchmarkComposeMeasureLayout
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.node.RootForTest
+import androidx.compose.ui.platform.LocalView
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.SemanticsProperties
+import androidx.compose.ui.semantics.getAllSemanticsNodes
+import androidx.compose.ui.semantics.getOrNull
+import androidx.compose.ui.unit.dp
+import androidx.test.filters.LargeTest
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@LargeTest
+@RunWith(Parameterized::class)
+class TextAccessibilityBenchmark(
+ private val accessibilityEnabled: Boolean,
+ private val invalidateSemanticsOnEachRun: Boolean
+) {
+
+ @OptIn(ExperimentalBenchmarkConfigApi::class)
+ @get:Rule
+ val benchmarkRule = ComposeBenchmarkRule(MicrobenchmarkConfig(traceAppTagEnabled = true))
+
+ lateinit var view: View
+ lateinit var nodeProvider: AccessibilityNodeProvider
+
+ @Test
+ fun createAccessibilityNodeInfoFromId_singleTextComponent() {
+ if (!accessibilityEnabled) return
+
+ measureRepeatedOnUiThread(
+ content = {
+ Text("Text Composable", Modifier.testTag("tag"))
+ },
+ benchmark = {
+ val semanticsId = runWithTimingDisabled { findIdByTag("tag") }
+ nodeProvider.createAccessibilityNodeInfo(semanticsId)
+ }
+ )
+ }
+
+ @Test fun createAccessibilityNodeInfoFromId_singleOfMultipleTextComponents() {
+ if (!accessibilityEnabled) return
+
+ measureRepeatedOnUiThread(
+ content = {
+ Column {
+ Text(
+ modifier = Modifier.testTag("tag"),
+ text = "Text Composable"
+ )
+ repeat(9) {
+ Text("Text Composable")
+ }
+ }
+ },
+ benchmark = {
+ val semanticsId = runWithTimingDisabled { findIdByTag("tag") }
+ nodeProvider.createAccessibilityNodeInfo(semanticsId)
+ }
+ )
+ }
+
+ /**
+ * We don't use the testTag here, but retained it to make this test comparable with
+ * [createAccessibilityNodeInfoFromId_singleTextComponent].
+ */
+ @Test
+ fun createAccessibilityNodeInfoFromRoot_singleTextComponent() {
+ if (!accessibilityEnabled) return
+
+ measureRepeatedOnUiThread(
+ content = {
+ Text("Text Composable", Modifier.testTag("text"))
+ },
+ benchmark = {
+ nodeProvider.createAccessibilityNodeInfo(HOST_VIEW_ID)
+ }
+ )
+ }
+
+ /**
+ * We don't use the testTag here, but retained it to make this test comparable with
+ * [createAccessibilityNodeInfoFromId_singleOfMultipleTextComponents].
+ */
+ @Test
+ fun createAccessibilityNodeInfoFromRoot_multipleTextComponents() {
+ if (!accessibilityEnabled) return
+
+ measureRepeatedOnUiThread(
+ content = {
+ Column {
+ Text(
+ modifier = Modifier.testTag("text"),
+ text = "Text Composable"
+ )
+ repeat(9) {
+ Text("Text Composable")
+ }
+ }
+ },
+ benchmark = {
+ nodeProvider.createAccessibilityNodeInfo(HOST_VIEW_ID)
+ }
+ )
+ }
+
+ @Test
+ fun sendEvents_addRemoveSingleTextComponent() {
+ benchmarkRule.toggleStateBenchmarkComposeMeasureLayout(
+ caseFactory = {
+ object : ComposeTestCase, ToggleableTestCase {
+ var include by mutableStateOf(true)
+
+ @Composable
+ override fun Content() {
+ setupAccessibility()
+ if (include) Text("abc")
+ }
+
+ override fun toggleState() {
+ include = !include
+ if (invalidateSemanticsOnEachRun) invalidateSemantics()
+ }
+ }
+ }
+ )
+ }
+
+ @Test
+ fun sendEvents_addRemoveMultipleTextComponents() {
+ benchmarkRule.toggleStateBenchmarkComposeMeasureLayout(
+ caseFactory = {
+ object : ComposeTestCase, ToggleableTestCase {
+ var include by mutableStateOf(true)
+
+ @Composable
+ override fun Content() {
+ setupAccessibility()
+ Column {
+ if (include) {
+ repeat(10) {
+ Text("abc")
+ }
+ }
+ }
+ }
+
+ override fun toggleState() {
+ include = !include
+ if (invalidateSemanticsOnEachRun) invalidateSemantics()
+ }
+ }
+ }
+ )
+ }
+
+ @Test
+ fun sendEvents_changeNumberOfTextComponents() {
+ benchmarkRule.toggleStateBenchmarkComposeMeasureLayout(
+ caseFactory = {
+ object : ComposeTestCase, ToggleableTestCase {
+ var count by mutableStateOf(5)
+
+ @Composable
+ override fun Content() {
+ setupAccessibility()
+ Column {
+ repeat(count) {
+ Text("abc")
+ }
+ }
+ }
+
+ override fun toggleState() {
+ count = if (count == 5) 10 else 5
+ if (invalidateSemanticsOnEachRun) invalidateSemantics()
+ }
+ }
+ }
+ )
+ }
+
+ @Test
+ fun sendEvents_changeTextInSingleTextComponent() {
+ benchmarkRule.toggleStateBenchmarkComposeMeasureLayout(
+ caseFactory = {
+ object : ComposeTestCase, ToggleableTestCase {
+ var initialValue by mutableStateOf(true)
+
+ @Composable
+ override fun Content() {
+ setupAccessibility()
+ Text(if (initialValue) "abc" else "def")
+ }
+
+ override fun toggleState() {
+ initialValue = !initialValue
+ if (invalidateSemanticsOnEachRun) invalidateSemantics()
+ }
+ }
+ }
+ )
+ }
+
+ @Test
+ fun sendEvents_changeTextInMultipleTextComponents() {
+ benchmarkRule.toggleStateBenchmarkComposeMeasureLayout(
+ caseFactory = {
+ object : ComposeTestCase, ToggleableTestCase {
+ var initialValue by mutableStateOf(true)
+
+ @Composable
+ override fun Content() {
+ setupAccessibility()
+
+ Column {
+ Text(if (initialValue) "abc" else "ABC")
+ Text(if (initialValue) "def" else "DEF")
+ Text(if (initialValue) "ghi" else "GHI")
+ Text(if (initialValue) "jkl" else "JKL")
+ Text(if (initialValue) "lmn" else "MNO")
+ Text(if (initialValue) "opq" else "OPQ")
+ Text(if (initialValue) "rst" else "RST")
+ Text(if (initialValue) "uvw" else "UVW")
+ Text(if (initialValue) "xyz" else "XYZ")
+ }
+ }
+
+ override fun toggleState() {
+ initialValue = !initialValue
+ if (invalidateSemanticsOnEachRun) invalidateSemantics()
+ }
+ }
+ }
+ )
+ }
+
+ @Test
+ fun sendEvents_changeTextInOneOfMultipleText() {
+ benchmarkRule.toggleStateBenchmarkComposeMeasureLayout(
+ caseFactory = {
+ object : ComposeTestCase, ToggleableTestCase {
+ var initialValue by mutableStateOf(true)
+
+ @Composable
+ override fun Content() {
+ setupAccessibility()
+ Column {
+ Text("abc")
+ Text("def")
+ Text("ghi")
+ Text(if (initialValue) "jkl" else "JKL")
+ Text("opq")
+ Text("rst")
+ Text("uvw")
+ Text("xyz")
+ }
+ }
+
+ override fun toggleState() {
+ initialValue = !initialValue
+ if (invalidateSemanticsOnEachRun) invalidateSemantics()
+ }
+ }
+ }
+ )
+ }
+
+ @Test
+ fun sendEvents_scrollTest() {
+ benchmarkRule.toggleStateBenchmarkComposeMeasureLayout(
+ caseFactory = {
+ object : ComposeTestCase, ToggleableTestCase {
+ lateinit var state: LazyListState
+ lateinit var coroutineScope: CoroutineScope
+
+ @Composable
+ override fun Content() {
+ setupAccessibility()
+ state = rememberLazyListState()
+ coroutineScope = rememberCoroutineScope()
+
+ LazyColumn(Modifier.height(600.dp), state) {
+ items(300) {
+ Text("item $it", Modifier.height(60.dp))
+ }
+ }
+ }
+
+ override fun toggleState() {
+ coroutineScope.launch {
+ state.scrollToItem(if (state.firstVisibleItemIndex == 0) 200 else 0)
+ if (invalidateSemanticsOnEachRun) invalidateSemantics()
+ }
+ }
+ }
+ }
+ )
+ }
+
+ companion object {
+ @JvmStatic
+ @Parameterized.Parameters(
+ name = "accessibilityEnabled = {0}, invalidateSemanticsOnEachRun = {1}"
+ )
+ fun initParameters() = listOf(
+ arrayOf(false, false),
+ arrayOf(true, false),
+ arrayOf(true, true)
+ )
+ }
+
+ private fun findIdByTag(@Suppress("SameParameterValue") tag: String): Int {
+ return (view as RootForTest).semanticsOwner
+ .getAllSemanticsNodes(mergingEnabled = false)
+ .find { it.config.getOrNull(SemanticsProperties.TestTag) == tag }!!.id
+ }
+
+ private fun measureRepeatedOnUiThread(
+ content: @Composable () -> Unit,
+ @UiThread benchmark: BenchmarkRule.Scope.() -> Unit
+ ) {
+ benchmarkRule.runBenchmarkFor(
+ givenTestCase = {
+ object : ComposeTestCase {
+ @Composable
+ override fun Content() {
+ view = LocalView.current
+ nodeProvider = view.accessibilityNodeProvider
+ setupAccessibility()
+ content()
+ }
+ }
+ }
+ ) {
+
+ benchmarkRule.measureRepeatedOnUiThread {
+ runWithTimingDisabled {
+ doFrame()
+ assertNoPendingChanges()
+ if (invalidateSemanticsOnEachRun) invalidateSemantics()
+ }
+ benchmark()
+ runWithTimingDisabled { disposeContent() }
+ }
+ }
+ }
+
+ @Composable
+ @OptIn(ExperimentalComposeUiApi::class)
+ private fun setupAccessibility() {
+ view = LocalView.current
+ // TODO(b/308007375): Eventually we will be able to remove `accessibilityForTesting()`;
+ // this is just a temporary workaround for now.
+ LaunchedEffect(Unit) {
+ // Make sure the delay between batches of a11y events is set to zero.
+ (view as RootForTest).setAccessibilityEventBatchIntervalMillis(0L)
+ // Ensure that accessibility is enabled for testing.
+ (view as RootForTest).forceAccessibilityForTesting(accessibilityEnabled)
+ }
+ }
+
+ @OptIn(ExperimentalComposeUiApi::class)
+ private fun invalidateSemantics() {
+ // Setting forceAccessibilityForTesting invalidates semantics.
+ (view as RootForTest).forceAccessibilityForTesting(accessibilityEnabled)
+ }
+}
diff --git a/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/accessibility/TextFieldAccessibilityBenchmark.kt b/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/accessibility/TextFieldAccessibilityBenchmark.kt
new file mode 100644
index 0000000..0687bc1
--- /dev/null
+++ b/compose/ui/ui/benchmark/src/androidTest/java/androidx/compose/ui/benchmark/accessibility/TextFieldAccessibilityBenchmark.kt
@@ -0,0 +1,429 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.benchmark.accessibility
+
+import android.view.View
+import android.view.accessibility.AccessibilityNodeProvider
+import android.view.accessibility.AccessibilityNodeProvider.HOST_VIEW_ID
+import androidx.annotation.UiThread
+import androidx.benchmark.ExperimentalBenchmarkConfigApi
+import androidx.benchmark.MicrobenchmarkConfig
+import androidx.benchmark.junit4.BenchmarkRule
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.foundation.lazy.rememberLazyListState
+import androidx.compose.material.TextField
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.setValue
+import androidx.compose.testutils.ComposeTestCase
+import androidx.compose.testutils.ToggleableTestCase
+import androidx.compose.testutils.assertNoPendingChanges
+import androidx.compose.testutils.benchmark.ComposeBenchmarkRule
+import androidx.compose.testutils.benchmark.toggleStateBenchmarkComposeMeasureLayout
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.node.RootForTest
+import androidx.compose.ui.platform.LocalView
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.SemanticsProperties
+import androidx.compose.ui.semantics.getAllSemanticsNodes
+import androidx.compose.ui.semantics.getOrNull
+import androidx.compose.ui.unit.dp
+import androidx.test.filters.LargeTest
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@LargeTest
+@RunWith(Parameterized::class)
+class TextFieldAccessibilityBenchmark(
+ private val accessibilityEnabled: Boolean,
+ private val invalidateSemanticsOnEachRun: Boolean
+) {
+ @OptIn(ExperimentalBenchmarkConfigApi::class)
+ @get:Rule
+ val benchmarkRule = ComposeBenchmarkRule(MicrobenchmarkConfig(traceAppTagEnabled = true))
+
+ lateinit var view: View
+ lateinit var nodeProvider: AccessibilityNodeProvider
+
+ @Test
+ fun createAccessibilityNodeInfoFromId_singleTextField() {
+ if (!accessibilityEnabled) return
+
+ measureRepeatedOnUiThread(
+ content = {
+ TextField(
+ modifier = Modifier.testTag("tag"),
+ value = "abc",
+ onValueChange = {},
+ )
+ },
+ benchmark = {
+ val semanticsId = runWithTimingDisabled { findIdByTag("tag") }
+ nodeProvider.createAccessibilityNodeInfo(semanticsId)
+ }
+ )
+ }
+
+ @Test fun createAccessibilityNodeInfoFromId_singleOfMultipleTextField() {
+ if (!accessibilityEnabled) return
+
+ measureRepeatedOnUiThread(
+ content = {
+ Column {
+ TextField(
+ modifier = Modifier.testTag("tag"),
+ value = "abc",
+ onValueChange = {},
+ )
+ repeat(9) {
+ TextField(value = "abc", onValueChange = {})
+ }
+ }
+ },
+ benchmark = {
+ val semanticsId = runWithTimingDisabled { findIdByTag("tag") }
+ nodeProvider.createAccessibilityNodeInfo(semanticsId)
+ }
+ )
+ }
+
+ /**
+ * We don't use the testTag here, but retained it to make this test comparable with
+ * [createAccessibilityNodeInfoFromId_singleTextField].
+ */
+ @Test
+ fun createAccessibilityNodeInfoFromRoot_singleTextField() {
+ if (!accessibilityEnabled) return
+
+ measureRepeatedOnUiThread(
+ content = {
+ TextField(
+ modifier = Modifier.testTag("tag"),
+ value = "abc",
+ onValueChange = {}
+ )
+ },
+ benchmark = {
+ nodeProvider.createAccessibilityNodeInfo(HOST_VIEW_ID)
+ }
+ )
+ }
+
+ /**
+ * We don't use the testTag here, but retained it to make this test comparable with
+ * [createAccessibilityNodeInfoFromId_singleOfMultipleTextField].
+ */
+ @Test
+ fun createAccessibilityNodeInfoFromRoot_multipleTextField() {
+ if (!accessibilityEnabled) return
+
+ measureRepeatedOnUiThread(
+ content = {
+ Column {
+ TextField(
+ modifier = Modifier.testTag("tag"),
+ value = "abc",
+ onValueChange = {},
+ )
+ repeat(9) {
+ TextField(value = "abc", onValueChange = {})
+ }
+ }
+ },
+ benchmark = {
+ nodeProvider.createAccessibilityNodeInfo(HOST_VIEW_ID)
+ }
+ )
+ }
+
+ @Test
+ fun sendEvents_addRemoveSingleTextField() {
+ benchmarkRule.toggleStateBenchmarkComposeMeasureLayout(
+ caseFactory = {
+ object : ComposeTestCase, ToggleableTestCase {
+ var include by mutableStateOf(true)
+
+ @Composable
+ override fun Content() {
+ setupAccessibility()
+ Column {
+ if (include) TextField(value = "abc", onValueChange = {})
+ }
+ }
+
+ override fun toggleState() {
+ include = !include
+ if (invalidateSemanticsOnEachRun) invalidateSemantics()
+ }
+ }
+ }
+ )
+ }
+
+ @Test
+ fun sendEvents_addRemoveMultipleTextFields() {
+ benchmarkRule.toggleStateBenchmarkComposeMeasureLayout(
+ caseFactory = {
+ object : ComposeTestCase, ToggleableTestCase {
+ var include by mutableStateOf(true)
+
+ @Composable
+ override fun Content() {
+ setupAccessibility()
+ Column {
+ if (include) {
+ repeat(10) {
+ TextField(value = "abc", onValueChange = {})
+ }
+ }
+ }
+ }
+
+ override fun toggleState() {
+ include = !include
+ if (invalidateSemanticsOnEachRun) invalidateSemantics()
+ }
+ }
+ }
+ )
+ }
+
+ @Test
+ fun sendEvents_changeNumberOfTextFields() {
+ benchmarkRule.toggleStateBenchmarkComposeMeasureLayout(
+ caseFactory = {
+ object : ComposeTestCase, ToggleableTestCase {
+ var count by mutableStateOf(5)
+
+ @Composable
+ override fun Content() {
+ setupAccessibility()
+ Column {
+ repeat(count) {
+ TextField(value = "abc", onValueChange = {})
+ }
+ }
+ }
+
+ override fun toggleState() {
+ count = if (count == 5) 10 else 5
+ if (invalidateSemanticsOnEachRun) invalidateSemantics()
+ }
+ }
+ }
+ )
+ }
+
+ @Test
+ fun sendEvents_changeTextInSingleTextField() {
+ benchmarkRule.toggleStateBenchmarkComposeMeasureLayout(
+ caseFactory = {
+ object : ComposeTestCase, ToggleableTestCase {
+ var initialValue by mutableStateOf(true)
+
+ @Composable
+ override fun Content() {
+ setupAccessibility()
+ TextField(value = if (initialValue) "abc" else "def", onValueChange = {})
+ }
+
+ override fun toggleState() {
+ initialValue = !initialValue
+ if (invalidateSemanticsOnEachRun) invalidateSemantics()
+ }
+ }
+ }
+ )
+ }
+
+ @Test
+ fun sendEvents_changeTextInMultipleTextFields() {
+ benchmarkRule.toggleStateBenchmarkComposeMeasureLayout(
+ caseFactory = {
+ object : ComposeTestCase, ToggleableTestCase {
+ var initialValue by mutableStateOf(true)
+
+ @Composable
+ override fun Content() {
+ setupAccessibility()
+ Column {
+ TextField(if (initialValue) "abc" else "ABC", {})
+ TextField(if (initialValue) "def" else "DEF", {})
+ TextField(if (initialValue) "ghi" else "GHI", {})
+ TextField(if (initialValue) "jkl" else "JKL", {})
+ TextField(if (initialValue) "lmn" else "MNO", {})
+ TextField(if (initialValue) "opq" else "OPQ", {})
+ TextField(if (initialValue) "rst" else "RST", {})
+ TextField(if (initialValue) "uvw" else "UVW", {})
+ TextField(if (initialValue) "xyz" else "XYZ", {})
+ }
+ }
+
+ override fun toggleState() {
+ initialValue = !initialValue
+ if (invalidateSemanticsOnEachRun) invalidateSemantics()
+ }
+ }
+ }
+ )
+ }
+
+ @Test
+ fun sendEvents_changeTextInOneOfMultipleTextFields() {
+ benchmarkRule.toggleStateBenchmarkComposeMeasureLayout(
+ caseFactory = {
+ object : ComposeTestCase, ToggleableTestCase {
+ var initialValue by mutableStateOf(true)
+
+ @Composable
+ override fun Content() {
+ setupAccessibility()
+ Column {
+ TextField("abc", {})
+ TextField("def", {})
+ TextField("ghi", {})
+ TextField(if (initialValue) "jkl" else "JKL", {})
+ TextField("opq", {})
+ TextField("rst", {})
+ TextField("uvw", {})
+ TextField("xyz", {})
+ }
+ }
+
+ override fun toggleState() {
+ initialValue = !initialValue
+ if (invalidateSemanticsOnEachRun) invalidateSemantics()
+ }
+ }
+ }
+ )
+ }
+
+ @Test
+ fun sendEvents_scrollTest() {
+ benchmarkRule.toggleStateBenchmarkComposeMeasureLayout(
+ caseFactory = {
+ object : ComposeTestCase, ToggleableTestCase {
+ lateinit var state: LazyListState
+ lateinit var coroutineScope: CoroutineScope
+
+ @Composable
+ override fun Content() {
+ setupAccessibility()
+ state = rememberLazyListState()
+ coroutineScope = rememberCoroutineScope()
+
+ LazyColumn(Modifier.height(600.dp), state) {
+ items(300) {
+ TextField(
+ value = "item $it",
+ onValueChange = {},
+ modifier = Modifier.height(100.dp)
+ )
+ }
+ }
+ }
+
+ override fun toggleState() {
+ coroutineScope.launch {
+ state.scrollToItem(if (state.firstVisibleItemIndex == 0) 200 else 0)
+ if (invalidateSemanticsOnEachRun) invalidateSemantics()
+ }
+ }
+ }
+ }
+ )
+ }
+
+ companion object {
+ @JvmStatic
+ @Parameterized.Parameters(
+ name = "accessibilityEnabled = {0}, invalidateSemanticsOnEachRun = {1}"
+ )
+ fun initParameters() = listOf(
+ arrayOf(false, false),
+ arrayOf(true, false),
+ arrayOf(true, true)
+ )
+ }
+
+ private fun findIdByTag(@Suppress("SameParameterValue") tag: String): Int {
+ return (view as RootForTest).semanticsOwner
+ .getAllSemanticsNodes(mergingEnabled = false)
+ .find { it.config.getOrNull(SemanticsProperties.TestTag) == tag }!!.id
+ }
+
+ private fun measureRepeatedOnUiThread(
+ content: @Composable () -> Unit,
+ @UiThread benchmark: BenchmarkRule.Scope.() -> Unit
+ ) {
+ benchmarkRule.runBenchmarkFor(
+ givenTestCase = {
+ object : ComposeTestCase {
+ @Composable
+ override fun Content() {
+ view = LocalView.current
+ nodeProvider = view.accessibilityNodeProvider
+ setupAccessibility()
+ content()
+ }
+ }
+ }
+ ) {
+ benchmarkRule.measureRepeatedOnUiThread {
+ runWithTimingDisabled {
+ doFrame()
+ assertNoPendingChanges()
+ if (invalidateSemanticsOnEachRun) invalidateSemantics()
+ }
+ benchmark()
+ runWithTimingDisabled { disposeContent() }
+ }
+ }
+ }
+
+ @Composable
+ @OptIn(ExperimentalComposeUiApi::class)
+ private fun setupAccessibility() {
+ view = LocalView.current
+ // TODO(b/308007375): Eventually we will be able to remove `accessibilityForTesting()`;
+ // this is just a temporary workaround for now.
+ LaunchedEffect(Unit) {
+ // Make sure the delay between batches of a11y events is set to zero.
+ (view as RootForTest).setAccessibilityEventBatchIntervalMillis(0L)
+ // Ensure that accessibility is enabled for testing.
+ (view as RootForTest).forceAccessibilityForTesting(accessibilityEnabled)
+ }
+ }
+
+ @OptIn(ExperimentalComposeUiApi::class)
+ private fun invalidateSemantics() {
+ // Setting forceAccessibilityForTesting invalidates semantics.
+ (view as RootForTest).forceAccessibilityForTesting(accessibilityEnabled)
+ }
+}
diff --git a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/accessibility/ScrollingTest.kt b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/accessibility/ScrollingTest.kt
index 1bc82d6..89ddec7 100644
--- a/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/accessibility/ScrollingTest.kt
+++ b/compose/ui/ui/src/androidInstrumentedTest/kotlin/androidx/compose/ui/accessibility/ScrollingTest.kt
@@ -100,10 +100,12 @@
}
) {
Text("foo", Modifier.size(10.toDp()))
- Text("bar",
+ Text(
+ "bar",
Modifier
.size(10.toDp())
- .testTag(tag))
+ .testTag(tag)
+ )
}
}
rule.mainClock.advanceTimeBy(accessibilityEventLoopIntervalMs)
@@ -522,10 +524,7 @@
false
}
- getScrollViewportLength {
- it.add((viewPortSize - contentPadding))
- true
- }
+ getScrollViewportLength { viewPortSize - contentPadding }
}
)
}
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt
index 4013394..3631b76 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt
@@ -952,8 +952,8 @@
/**
* This function is used by the delegate file to enable accessibility frameworks for testing.
*/
- override fun forceAccessibilityForTesting() {
- composeAccessibilityDelegate.accessibilityForceEnabledForTesting = true
+ override fun forceAccessibilityForTesting(enable: Boolean) {
+ composeAccessibilityDelegate.accessibilityForceEnabledForTesting = enable
}
/**
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 6800046..5d8af41 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
@@ -97,6 +97,7 @@
import androidx.compose.ui.util.fastForEachIndexed
import androidx.compose.ui.util.fastJoinToString
import androidx.compose.ui.util.fastRoundToInt
+import androidx.compose.ui.util.trace
import androidx.core.view.AccessibilityDelegateCompat
import androidx.core.view.ViewCompat.ACCESSIBILITY_LIVE_REGION_ASSERTIVE
import androidx.core.view.ViewCompat.ACCESSIBILITY_LIVE_REGION_POLITE
@@ -305,9 +306,11 @@
get() {
if (currentSemanticsNodesInvalidated) { // first instance of retrieving all nodes
currentSemanticsNodesInvalidated = false
- field = view.semanticsOwner.getAllUncoveredSemanticsNodesToIntObjectMap()
+ field = trace("generateCurrentSemanticsNodes") {
+ view.semanticsOwner.getAllUncoveredSemanticsNodesToIntObjectMap()
+ }
if (isEnabled) {
- setTraversalValues()
+ trace("setTraversalValues") { setTraversalValues() }
}
}
return field
@@ -431,29 +434,42 @@
}
private fun createNodeInfo(virtualViewId: Int): AccessibilityNodeInfo? {
- if (view.viewTreeOwners?.lifecycleOwner?.lifecycle?.currentState ==
- Lifecycle.State.DESTROYED
- ) {
- return null
+ trace("checkIfDestroyed") {
+ if (view.viewTreeOwners?.lifecycleOwner?.lifecycle?.currentState ==
+ Lifecycle.State.DESTROYED
+ ) {
+ return null
+ }
}
- val info: AccessibilityNodeInfoCompat = AccessibilityNodeInfoCompat.obtain()
- val semanticsNodeWithAdjustedBounds = currentSemanticsNodes[virtualViewId] ?: return null
+ val info: AccessibilityNodeInfoCompat = trace("createAccessibilityNodeInfoObject") {
+ AccessibilityNodeInfoCompat.obtain()
+ }
+ val semanticsNodeWithAdjustedBounds = trace("calculateNodeWithAdjustedBounds") {
+ currentSemanticsNodes[virtualViewId]
+ } ?: return null
val semanticsNode: SemanticsNode = semanticsNodeWithAdjustedBounds.semanticsNode
- if (virtualViewId == AccessibilityNodeProviderCompat.HOST_VIEW_ID) {
- info.setParent(view.getParentForAccessibility() as? View)
- } else {
- var parentId = checkPreconditionNotNull(semanticsNode.parent?.id) {
- "semanticsNode $virtualViewId has null parent"
+ 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
+ }
+ info.setParent(view, parentId)
}
- if (parentId == view.semanticsOwner.unmergedRootSemanticsNode.id) {
- parentId = AccessibilityNodeProviderCompat.HOST_VIEW_ID
- }
- info.setParent(view, parentId)
}
info.setSource(view, virtualViewId)
- info.setBoundsInScreen(boundsInScreen(semanticsNodeWithAdjustedBounds))
- populateAccessibilityNodeInfoProperties(virtualViewId, info, semanticsNode)
+ trace("setBoundsInScreen") {
+ info.setBoundsInScreen(boundsInScreen(semanticsNodeWithAdjustedBounds))
+ }
+
+ trace("populateAccessibilityNodeInfoProperties") {
+ populateAccessibilityNodeInfoProperties(virtualViewId, info, semanticsNode)
+ }
return info.unwrap()
}
@@ -1492,7 +1508,7 @@
event.contentDescription = contentDescription.fastJoinToString(",")
}
- return sendEvent(event)
+ return trace("sendEvent") { sendEvent(event) }
}
/**
@@ -1532,13 +1548,15 @@
@Suppress("DEPRECATION")
@VisibleForTesting
private fun createEvent(virtualViewId: Int, eventType: Int): AccessibilityEvent {
- val event: AccessibilityEvent = AccessibilityEvent.obtain(eventType)
+ val event: AccessibilityEvent = trace("obtainAccessibilityEvent") {
+ AccessibilityEvent.obtain(eventType)
+ }
event.isEnabled = true
event.className = ClassName
// Don't allow the client to override these properties.
- event.packageName = view.context.packageName
- event.setSource(view, virtualViewId)
+ trace("event.packageName") { event.packageName = view.context.packageName }
+ trace("event.setSource") { event.setSource(view, virtualViewId) }
if (isEnabled) {
// populate additional information from the node
@@ -2152,8 +2170,8 @@
// fun clearNode(semanticsNodeId: Int) { // clear the actionIdToId and labelToActionId nodes }
private val semanticsChangeChecker = Runnable {
- view.measureAndLayout()
- checkForSemanticsChanges()
+ trace("measureAndLayout") { view.measureAndLayout() }
+ trace("checkForSemanticsChanges") { checkForSemanticsChanges() }
checkingForSemanticsChanges = false
}
@@ -2178,39 +2196,45 @@
try {
val subtreeChangedSemanticsNodesIds = MutableIntSet()
for (notification in boundsUpdateChannel) {
- if (isEnabled) {
- for (i in subtreeChangedLayoutNodes.indices) {
- val layoutNode = subtreeChangedLayoutNodes.valueAt(i)
- sendSubtreeChangeAccessibilityEvents(
- layoutNode,
- subtreeChangedSemanticsNodesIds
- )
- sendTypeViewScrolledAccessibilityEvent(layoutNode)
+ trace("AccessibilityLoopIteration") {
+ if (isEnabled) {
+ for (i in subtreeChangedLayoutNodes.indices) {
+ val layoutNode = subtreeChangedLayoutNodes.valueAt(i)
+ trace("sendSubtreeChangeAccessibilityEvents") {
+ sendSubtreeChangeAccessibilityEvents(
+ layoutNode,
+ subtreeChangedSemanticsNodesIds
+ )
+ }
+ trace("sendTypeViewScrolledAccessibilityEvent") {
+ sendTypeViewScrolledAccessibilityEvent(layoutNode)
+ }
+ }
+ subtreeChangedSemanticsNodesIds.clear()
+ // When the bounds of layout nodes change, we will not always get semantics
+ // change notifications because bounds is not part of semantics. And bounds
+ // change from a layout node without semantics will affect the global bounds
+ // of it children which has semantics. Bounds change will affect which nodes
+ // are covered and which nodes are not, so the currentSemanticsNodes is not
+ // up to date anymore.
+ // After the subtree events are sent, accessibility services will get the
+ // current visible/invisible state. We also try to do semantics tree diffing
+ // to send out the proper accessibility events and update our copy here so that
+ // our incremental changes (represented by accessibility events) are consistent
+ // with accessibility services. That is: change - notify - new change -
+ // notify, if we don't do the tree diffing and update our copy here, we will
+ // combine old change and new change, which is missing finer-grained
+ // notification.
+ if (!checkingForSemanticsChanges) {
+ checkingForSemanticsChanges = true
+ handler.post(semanticsChangeChecker)
+ }
}
- subtreeChangedSemanticsNodesIds.clear()
- // When the bounds of layout nodes change, we will not always get semantics
- // change notifications because bounds is not part of semantics. And bounds
- // change from a layout node without semantics will affect the global bounds
- // of it children which has semantics. Bounds change will affect which nodes
- // are covered and which nodes are not, so the currentSemanticsNodes is not
- // up to date anymore.
- // After the subtree events are sent, accessibility services will get the
- // current visible/invisible state. We also try to do semantics tree diffing
- // to send out the proper accessibility events and update our copy here so that
- // our incremental changes (represented by accessibility events) are consistent
- // with accessibility services. That is: change - notify - new change -
- // notify, if we don't do the tree diffing and update our copy here, we will
- // combine old change and new change, which is missing finer-grained
- // notification.
- if (!checkingForSemanticsChanges) {
- checkingForSemanticsChanges = true
- handler.post(semanticsChangeChecker)
- }
+ subtreeChangedLayoutNodes.clear()
+ pendingHorizontalScrollEvents.clear()
+ pendingVerticalScrollEvents.clear()
+ delay(SendRecurringAccessibilityEventsIntervalMillis)
}
- subtreeChangedLayoutNodes.clear()
- pendingHorizontalScrollEvents.clear()
- pendingVerticalScrollEvents.clear()
- delay(SendRecurringAccessibilityEventsIntervalMillis)
}
} finally {
subtreeChangedLayoutNodes.clear()
@@ -2295,18 +2319,21 @@
}
// When we finally send the event, make sure it is an accessibility-focusable node.
- var semanticsNode = if (layoutNode.nodes.has(Nodes.Semantics))
- layoutNode
- else
- layoutNode.findClosestParentNode { it.nodes.has(Nodes.Semantics) }
+ val id = trace("GetSemanticsNode") {
+ 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
+ }?.let { semanticsNode = it }
+ }
+ semanticsNode?.semanticsId ?: return
}
- val id = semanticsNode?.semanticsId ?: return
+
if (!subtreeChangedSemanticsNodesIds.add(id)) {
return
}
@@ -2320,16 +2347,21 @@
private fun checkForSemanticsChanges() {
// Accessibility structural change
- if (isEnabled) {
- sendAccessibilitySemanticsStructureChangeEvents(
- view.semanticsOwner.unmergedRootSemanticsNode,
- previousSemanticsRoot
- )
+ trace("sendAccessibilitySemanticsStructureChangeEvents") {
+ if (isEnabled) {
+ sendAccessibilitySemanticsStructureChangeEvents(
+ view.semanticsOwner.unmergedRootSemanticsNode,
+ previousSemanticsRoot
+ )
+ }
}
-
// Accessibility property change
- sendSemanticsPropertyChangeEvents(currentSemanticsNodes)
- updateSemanticsNodesCopyAndPanes()
+ trace("sendSemanticsPropertyChangeEvents") {
+ sendSemanticsPropertyChangeEvents(currentSemanticsNodes)
+ }
+ trace("updateSemanticsNodesCopyAndPanes") {
+ updateSemanticsNodesCopyAndPanes()
+ }
}
private fun updateSemanticsNodesCopyAndPanes() {
@@ -3080,14 +3112,15 @@
// TODO(b/160820721): use AccessibilityNodeProviderCompat instead of AccessibilityNodeProvider
private inner class ComposeAccessibilityNodeProvider : AccessibilityNodeProvider() {
- override fun createAccessibilityNodeInfo(virtualViewId: Int):
- AccessibilityNodeInfo? {
- val nodeInfo = createNodeInfo(virtualViewId)
- if (sendingFocusAffectingEvent && virtualViewId == focusedVirtualViewId) {
- currentlyFocusedANI = nodeInfo
+ override fun createAccessibilityNodeInfo(virtualViewId: Int): AccessibilityNodeInfo? {
+ return trace("createAccessibilityNodeInfo") {
+ createNodeInfo(virtualViewId).also {
+ if (sendingFocusAffectingEvent && virtualViewId == focusedVirtualViewId) {
+ currentlyFocusedANI = it
+ }
}
- return nodeInfo
}
+ }
override fun performAction(
virtualViewId: Int,
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 fc363d0..710bf6c 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
@@ -63,6 +63,7 @@
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
@@ -418,25 +419,27 @@
internal val collapsedSemantics: SemanticsConfiguration?
get() {
- 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() } }
+ 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
}
- _collapsedSemantics = config
- return config
}
/**
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/RootForTest.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/RootForTest.kt
index 79ad71e..c76fdbe 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/RootForTest.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/RootForTest.kt
@@ -54,9 +54,11 @@
/**
* Force accessibility to be enabled for testing.
+ *
+ * @param enable force enable accessibility if true.
*/
@ExperimentalComposeUiApi
- fun forceAccessibilityForTesting()
+ fun forceAccessibilityForTesting(enable: Boolean)
/**
* Set the time interval between sending accessibility events in milliseconds.
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsProperties.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsProperties.kt
index 0d60bb9..3283865 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsProperties.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/semantics/SemanticsProperties.kt
@@ -101,7 +101,8 @@
/**
* @see SemanticsPropertyReceiver.isContainer
*/
- @Deprecated("Use `isTraversalGroup` instead.",
+ @Deprecated(
+ "Use `isTraversalGroup` instead.",
replaceWith = ReplaceWith("IsTraversalGroup"),
)
val IsContainer: SemanticsPropertyKey<Boolean>
@@ -939,7 +940,8 @@
*
* @see SemanticsProperties.IsContainer
*/
-@Deprecated("Use `isTraversalGroup` instead.",
+@Deprecated(
+ "Use `isTraversalGroup` instead.",
replaceWith = ReplaceWith("isTraversalGroup"),
)
var SemanticsPropertyReceiver.isContainer by SemanticsProperties.IsTraversalGroup
@@ -1571,8 +1573,7 @@
}
/**
- * Action to get a scrollable's active view port amount for scrolling actions. The result is the
- * first element of the array in the argument of the AccessibilityAction.
+ * Action to get a scrollable's active view port amount for scrolling actions.
*
* @param label Optional label for this action.
* @param action Action to be performed when the [SemanticsActions.GetScrollViewportLength] is
@@ -1580,7 +1581,15 @@
*/
fun SemanticsPropertyReceiver.getScrollViewportLength(
label: String? = null,
- action: ((MutableList<Float>) -> Boolean)
+ action: (() -> Float?)
) {
- this[SemanticsActions.GetScrollViewportLength] = AccessibilityAction(label, action)
+ this[SemanticsActions.GetScrollViewportLength] = AccessibilityAction(label) {
+ val viewport = action.invoke()
+ if (viewport == null) {
+ false
+ } else {
+ it.add(viewport)
+ true
+ }
+ }
}
diff --git a/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/platform/SkiaBasedOwner.skiko.kt b/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/platform/SkiaBasedOwner.skiko.kt
index dda9f9f..00fda90 100644
--- a/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/platform/SkiaBasedOwner.skiko.kt
+++ b/compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/platform/SkiaBasedOwner.skiko.kt
@@ -252,7 +252,7 @@
override fun sendKeyEvent(keyEvent: KeyEvent): Boolean =
sendKeyEvent(platformInputService, focusOwner, keyEvent)
- override fun forceAccessibilityForTesting() = TODO("Not yet implemented")
+ override fun forceAccessibilityForTesting(enable: Boolean) = TODO("Not yet implemented")
override fun setAccessibilityEventBatchIntervalMillis(accessibilityInterval: Long) =
TODO("Not yet implemented")
diff --git a/development/build_log_simplifier/messages.ignore b/development/build_log_simplifier/messages.ignore
index 81df2d7..840b4f0 100644
--- a/development/build_log_simplifier/messages.ignore
+++ b/development/build_log_simplifier/messages.ignore
@@ -287,7 +287,6 @@
WARN: .*\/unzippedJvmSources\/androidx\/paging\/compose\/LazyPagingItems\.kt:[0-9]+ Failed to resolve See PagingSource\.invalidate in DFunction refresh\. Did you mean PagingSource#invalidate\?
WARN: .*\/unzippedJvmSources\/androidx\/paging\/LoadStateAdapter\.kt:[0-9]+ Missing @param tag for parameter `holder` in DFunction onBindViewHolder
WARN: .*\/unzippedJvmSources\/androidx\/palette\/graphics\/Palette\.java:[0-9]+ Missing @param tag for parameter `target` in DFunction getColorForTarget
-WARN\: \$OUT_DIR\/androidx\/docs\-tip\-of\-tree\/build\/unzippedMultiplatformSources\/nativeMain\/androidx\/room\/RoomDatabase\.native\.kt\:UnknownLine Link does not resolve for \@throws kotlin\.IllegalArgumentException in DFunction valueOf\. Is it from a package that the containing file does not import\? Are docs inherited by an un\-documented override function\, but the exception class is not in scope in the inheriting class\? The general fix for these is to fully qualify the exception name\, e\.g\. \`\@throws java\.io\.IOException under some conditions\`\.
WARN: .*\/unzippedJvmSources\/androidx\/recyclerview\/widget\/BatchingListUpdateCallback\.java:[0-9]+ Missing @param tag for parameter `payload` in DFunction onChanged
WARN: .*\/unzippedJvmSources\/androidx\/recyclerview\/widget\/DefaultItemAnimator\.java:[0-9]+ Missing @param tag for parameter `fromX` in DFunction animateMove
WARN: .*\/unzippedJvmSources\/androidx\/recyclerview\/widget\/DefaultItemAnimator\.java:[0-9]+ Missing @param tag for parameter `fromY` in DFunction animateMove
@@ -505,12 +504,10 @@
WARN: .*\/unzippedMultiplatformSources\/jvmMain\/androidx\/datastore\/core\/Serializer\.kt:[0-9]+ Missing @param tag for parameter `output` in DFunction writeTo
WARN: .*\/unzippedMultiplatformSources\/commonJvmMain\/androidx\/datastore\/core\/Serializer\.kt:[0-9]+ Missing @param tag for parameter `output` in DFunction writeTo
WARN: .*\/unzippedMultiplatformSources\/nonJvmMain\/androidx\/annotation\/RestrictTo\.nonJvm\.kt:UnknownLine Link does not resolve for @throws kotlin\.IllegalArgumentException in DFunction valueOf\. Is it from a package that the containing file does not import\? Are docs inherited by an un-documented override function, but the exception class is not in scope in the inheriting class\? The general fix for these is to fully qualify the exception name, e\.g\. `@throws java\.io\.IOException under some conditions`\.
+WARN: .*\/unzippedMultiplatformSources\/.*Main\/androidx\/room\/RoomDatabase.*\.kt:UnknownLine Link does not resolve for @throws kotlin\.IllegalArgumentException in DFunction valueOf\. Is it from a package that the containing file does not import\? Are docs inherited by an un-documented override function, but the exception class is not in scope in the inheriting class\? The general fix for these is to fully qualify the exception name, e\.g\. `@throws java\.io\.IOException under some conditions`\.
WARN: File location could not be determined\. Failed to resolve See <a href="https:\/\/developer\.android\.com\/training\/transitions">Transition API Guide<\/a> in DPackage androidx\.transition
-# Task docs-*:unzipJvmSources
-# paging and wear have multiple libraries depending on the same samples library, which can cause the samples library to be unzipped multiple times
-Encountered duplicate path "androidx/paging/samples/.*
-Encountered duplicate path "androidx/wear/compose/material/samples/.*
-Encountered duplicate path "androidx/wear/watchface/samples/.*
+[0-9]+ cpus, so not storing build metrics.
+64 cpus, so storing build metrics.
# > Task :compose:ui:ui-tooling:processDebugAndroidTestManifest
\$SUPPORT/compose/ui/ui\-tooling/src/androidInstrumentedTest/AndroidManifest\.xml:[0-9]+:[0-9]+\-[0-9]+:[0-9]+ Warning:
# ./gradlew tasks warns as we have warnings present
diff --git a/docs-public/build.gradle b/docs-public/build.gradle
index 0e96b53..8ef736c 100644
--- a/docs-public/build.gradle
+++ b/docs-public/build.gradle
@@ -17,7 +17,6 @@
dependencies {
docs("androidx.activity:activity:1.9.0")
docs("androidx.activity:activity-compose:1.9.0")
- samples("androidx.activity:activity-compose-samples:1.9.0")
docs("androidx.activity:activity-ktx:1.9.0")
// ads-identifier is deprecated
docsWithoutApiSince("androidx.ads:ads-identifier:1.0.0-alpha05")
@@ -61,7 +60,6 @@
docs("androidx.camera:camera-viewfinder:1.4.0-alpha05")
docs("androidx.camera:camera-viewfinder-compose:1.4.0-alpha03")
docs("androidx.camera:camera-viewfinder-core:1.4.0-alpha05")
- samples("androidx.camera:camera-viewfinder-core-samples:1.4.0-alpha05")
docs("androidx.car.app:app:1.7.0-alpha02")
docs("androidx.car.app:app-automotive:1.7.0-alpha02")
docs("androidx.car.app:app-projected:1.7.0-alpha02")
@@ -97,17 +95,11 @@
docs("androidx.compose.material:material-navigation:1.7.0-alpha07")
samples("androidx.compose.material:material-navigation-samples:1.7.0-alpha07")
kmpDocs("androidx.compose.material:material-ripple:1.7.0-alpha07")
- samples("androidx.compose.material:material-samples:1.7.0-alpha07")
kmpDocs("androidx.compose.runtime:runtime:1.7.0-alpha07")
docs("androidx.compose.runtime:runtime-livedata:1.7.0-alpha07")
- samples("androidx.compose.runtime:runtime-livedata-samples:1.7.0-alpha07")
docs("androidx.compose.runtime:runtime-rxjava2:1.7.0-alpha07")
- samples("androidx.compose.runtime:runtime-rxjava2-samples:1.7.0-alpha07")
docs("androidx.compose.runtime:runtime-rxjava3:1.7.0-alpha07")
- samples("androidx.compose.runtime:runtime-rxjava3-samples:1.7.0-alpha07")
kmpDocs("androidx.compose.runtime:runtime-saveable:1.7.0-alpha07")
- samples("androidx.compose.runtime:runtime-saveable-samples:1.7.0-alpha07")
- samples("androidx.compose.runtime:runtime-samples:1.7.0-alpha07")
docs("androidx.compose.runtime:runtime-tracing:1.0.0-beta01")
kmpDocs("androidx.compose.ui:ui:1.7.0-alpha07")
docs("androidx.compose.ui:ui-android-stubs:1.7.0-alpha07")
@@ -124,10 +116,8 @@
kmpDocs("androidx.compose.ui:ui-tooling-data:1.7.0-alpha07")
kmpDocs("androidx.compose.ui:ui-tooling-preview:1.7.0-alpha07")
kmpDocs("androidx.compose.ui:ui-unit:1.7.0-alpha07")
- samples("androidx.compose.ui:ui-unit-samples:1.7.0-alpha07")
kmpDocs("androidx.compose.ui:ui-util:1.7.0-alpha07")
docs("androidx.compose.ui:ui-viewbinding:1.7.0-alpha07")
- samples("androidx.compose.ui:ui-viewbinding-samples:1.7.0-alpha07")
samples("androidx.compose.ui:ui-samples:1.7.0-alpha07")
docs("androidx.concurrent:concurrent-futures:1.2.0-alpha03")
docs("androidx.concurrent:concurrent-futures-ktx:1.2.0-alpha03")
@@ -191,15 +181,12 @@
docs("androidx.exifinterface:exifinterface:1.3.6")
docs("androidx.fragment:fragment:1.8.0-alpha02")
docs("androidx.fragment:fragment-compose:1.8.0-alpha02")
- samples("androidx.fragment:fragment-compose-samples:1.8.0-alpha02")
docs("androidx.fragment:fragment-ktx:1.8.0-alpha02")
docs("androidx.fragment:fragment-testing:1.8.0-alpha02")
docs("androidx.glance:glance:1.1.0-beta02")
docs("androidx.glance:glance-appwidget:1.1.0-beta02")
- samples("androidx.glance:glance-appwidget-samples:1.1.0-beta02")
docs("androidx.glance:glance-appwidget-preview:1.0.0-alpha06")
docs("androidx.glance:glance-appwidget-testing:1.1.0-beta02")
- samples("androidx.glance:glance-appwidget-testing-samples:1.1.0-beta02")
docs("androidx.glance:glance-material:1.1.0-beta02")
docs("androidx.glance:glance-material3:1.1.0-beta02")
docs("androidx.glance:glance-preview:1.0.0-alpha06")
@@ -218,7 +205,6 @@
docs("androidx.hilt:hilt-common:1.2.0")
docs("androidx.hilt:hilt-navigation:1.2.0")
docs("androidx.hilt:hilt-navigation-compose:1.2.0")
- samples("androidx.hilt:hilt-navigation-compose-samples:1.2.0")
docs("androidx.hilt:hilt-navigation-fragment:1.2.0")
docs("androidx.hilt:hilt-work:1.2.0")
docs("androidx.input:input-motionprediction:1.0.0-beta03")
@@ -241,7 +227,6 @@
docs("androidx.lifecycle:lifecycle-reactivestreams-ktx:2.8.0-beta01")
kmpDocs("androidx.lifecycle:lifecycle-runtime:2.8.0-beta01")
kmpDocs("androidx.lifecycle:lifecycle-runtime-compose:2.8.0-beta01")
- samples("androidx.lifecycle:lifecycle-runtime-compose-samples:2.8.0-beta01")
kmpDocs("androidx.lifecycle:lifecycle-runtime-ktx:2.8.0-beta01")
docs("androidx.lifecycle:lifecycle-runtime-testing:2.8.0-beta01")
docs("androidx.lifecycle:lifecycle-service:2.8.0-beta01")
@@ -259,38 +244,37 @@
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.3.1")
- docsWithoutApiSince("androidx.media3:media3-common:1.3.1")
- docsWithoutApiSince("androidx.media3:media3-container:1.3.1")
- docsWithoutApiSince("androidx.media3:media3-database:1.3.1")
- docsWithoutApiSince("androidx.media3:media3-datasource:1.3.1")
- docsWithoutApiSince("androidx.media3:media3-datasource-cronet:1.3.1")
- docsWithoutApiSince("androidx.media3:media3-datasource-okhttp:1.3.1")
- docsWithoutApiSince("androidx.media3:media3-datasource-rtmp:1.3.1")
- docsWithoutApiSince("androidx.media3:media3-decoder:1.3.1")
- docsWithoutApiSince("androidx.media3:media3-effect:1.3.1")
- docsWithoutApiSince("androidx.media3:media3-exoplayer:1.3.1")
- docsWithoutApiSince("androidx.media3:media3-exoplayer-dash:1.3.1")
- docsWithoutApiSince("androidx.media3:media3-exoplayer-hls:1.3.1")
- docsWithoutApiSince("androidx.media3:media3-exoplayer-ima:1.3.1")
- docsWithoutApiSince("androidx.media3:media3-exoplayer-rtsp:1.3.1")
- docsWithoutApiSince("androidx.media3:media3-exoplayer-smoothstreaming:1.3.1")
- docsWithoutApiSince("androidx.media3:media3-exoplayer-workmanager:1.3.1")
- docsWithoutApiSince("androidx.media3:media3-extractor:1.3.1")
- docsWithoutApiSince("androidx.media3:media3-muxer:1.3.1")
- docsWithoutApiSince("androidx.media3:media3-session:1.3.1")
- docsWithoutApiSince("androidx.media3:media3-test-utils:1.3.1")
- docsWithoutApiSince("androidx.media3:media3-test-utils-robolectric:1.3.1")
- docsWithoutApiSince("androidx.media3:media3-transformer:1.3.1")
- docsWithoutApiSince("androidx.media3:media3-ui:1.3.1")
- docsWithoutApiSince("androidx.media3:media3-ui-leanback:1.3.1")
+ docsWithoutApiSince("androidx.media3:media3-cast:1.4.0-alpha01")
+ docsWithoutApiSince("androidx.media3:media3-common:1.4.0-alpha01")
+ docsWithoutApiSince("androidx.media3:media3-container:1.4.0-alpha01")
+ docsWithoutApiSince("androidx.media3:media3-database:1.4.0-alpha01")
+ docsWithoutApiSince("androidx.media3:media3-datasource:1.4.0-alpha01")
+ docsWithoutApiSince("androidx.media3:media3-datasource-cronet:1.4.0-alpha01")
+ docsWithoutApiSince("androidx.media3:media3-datasource-okhttp:1.4.0-alpha01")
+ docsWithoutApiSince("androidx.media3:media3-datasource-rtmp:1.4.0-alpha01")
+ docsWithoutApiSince("androidx.media3:media3-decoder:1.4.0-alpha01")
+ docsWithoutApiSince("androidx.media3:media3-effect:1.4.0-alpha01")
+ docsWithoutApiSince("androidx.media3:media3-exoplayer:1.4.0-alpha01")
+ docsWithoutApiSince("androidx.media3:media3-exoplayer-dash:1.4.0-alpha01")
+ docsWithoutApiSince("androidx.media3:media3-exoplayer-hls:1.4.0-alpha01")
+ docsWithoutApiSince("androidx.media3:media3-exoplayer-ima:1.4.0-alpha01")
+ docsWithoutApiSince("androidx.media3:media3-exoplayer-rtsp:1.4.0-alpha01")
+ docsWithoutApiSince("androidx.media3:media3-exoplayer-smoothstreaming:1.4.0-alpha01")
+ docsWithoutApiSince("androidx.media3:media3-exoplayer-workmanager:1.4.0-alpha01")
+ docsWithoutApiSince("androidx.media3:media3-extractor:1.4.0-alpha01")
+ docsWithoutApiSince("androidx.media3:media3-muxer:1.4.0-alpha01")
+ docsWithoutApiSince("androidx.media3:media3-session:1.4.0-alpha01")
+ docsWithoutApiSince("androidx.media3:media3-test-utils:1.4.0-alpha01")
+ docsWithoutApiSince("androidx.media3:media3-test-utils-robolectric:1.4.0-alpha01")
+ docsWithoutApiSince("androidx.media3:media3-transformer:1.4.0-alpha01")
+ docsWithoutApiSince("androidx.media3:media3-ui:1.4.0-alpha01")
+ docsWithoutApiSince("androidx.media3:media3-ui-leanback:1.4.0-alpha01")
docs("androidx.mediarouter:mediarouter:1.7.0")
docs("androidx.mediarouter:mediarouter-testing:1.7.0")
docs("androidx.metrics:metrics-performance:1.0.0-beta01")
docs("androidx.navigation:navigation-common:2.8.0-alpha07")
docs("androidx.navigation:navigation-common-ktx:2.8.0-alpha07")
docs("androidx.navigation:navigation-compose:2.8.0-alpha07")
- samples("androidx.navigation:navigation-compose-samples:2.8.0-alpha07")
docs("androidx.navigation:navigation-dynamic-features-fragment:2.8.0-alpha07")
docs("androidx.navigation:navigation-dynamic-features-runtime:2.8.0-alpha07")
docs("androidx.navigation:navigation-fragment:2.8.0-alpha07")
@@ -304,14 +288,12 @@
kmpDocs("androidx.paging:paging-common:3.3.0-rc01")
docs("androidx.paging:paging-common-ktx:3.3.0-rc01")
kmpDocs("androidx.paging:paging-compose:3.3.0-rc01")
- samples("androidx.paging:paging-compose-samples:3.3.0-rc01")
docs("androidx.paging:paging-guava:3.3.0-rc01")
docs("androidx.paging:paging-runtime:3.3.0-rc01")
docs("androidx.paging:paging-runtime-ktx:3.3.0-rc01")
docs("androidx.paging:paging-rxjava2:3.3.0-rc01")
docs("androidx.paging:paging-rxjava2-ktx:3.3.0-rc01")
docs("androidx.paging:paging-rxjava3:3.3.0-rc01")
- samples("androidx.paging:paging-samples:3.3.0-rc01")
kmpDocs("androidx.paging:paging-testing:3.3.0-rc01")
docs("androidx.palette:palette:1.0.0")
docs("androidx.palette:palette-ktx:1.0.0")
@@ -409,14 +391,11 @@
docs("androidx.viewpager2:viewpager2:1.1.0-beta02")
docs("androidx.viewpager:viewpager:1.1.0-alpha01")
docs("androidx.wear.compose:compose-foundation:1.4.0-alpha07")
- samples("androidx.wear.compose:compose-foundation-samples:1.4.0-alpha07")
docs("androidx.wear.compose:compose-material:1.4.0-alpha07")
docs("androidx.wear.compose:compose-material-core:1.4.0-alpha07")
- samples("androidx.wear.compose:compose-material-samples:1.4.0-alpha07")
docs("androidx.wear.compose:compose-material3:1.0.0-alpha21")
samples("androidx.wear.compose:compose-material3-samples:1.4.0-alpha07")
docs("androidx.wear.compose:compose-navigation:1.4.0-alpha07")
- samples("androidx.wear.compose:compose-navigation-samples:1.4.0-alpha07")
docs("androidx.wear.compose:compose-ui-tooling:1.4.0-alpha07")
docs("androidx.wear.protolayout:protolayout:1.2.0-alpha01")
docs("androidx.wear.protolayout:protolayout-expression:1.2.0-alpha01")
@@ -437,14 +416,11 @@
docs("androidx.wear.watchface:watchface-complications-data:1.3.0-alpha03")
docs("androidx.wear.watchface:watchface-complications-data-source:1.3.0-alpha03")
docs("androidx.wear.watchface:watchface-complications-data-source-ktx:1.3.0-alpha03")
- samples("androidx.wear.watchface:watchface-complications-permission-dialogs-sample:1.3.0-alpha03")
docs("androidx.wear.watchface:watchface-complications-rendering:1.3.0-alpha03")
docs("androidx.wear.watchface:watchface-data:1.3.0-alpha03")
docs("androidx.wear.watchface:watchface-editor:1.3.0-alpha03")
docs("androidx.wear.watchface:watchface-editor-guava:1.3.0-alpha03")
- samples("androidx.wear.watchface:watchface-editor-samples:1.3.0-alpha03")
docs("androidx.wear.watchface:watchface-guava:1.3.0-alpha03")
- samples("androidx.wear.watchface:watchface-samples:1.3.0-alpha03")
docs("androidx.wear.watchface:watchface-style:1.3.0-alpha03")
docs("androidx.wear:wear:1.4.0-alpha01")
stubs(fileTree(dir: "../wear/wear_stubs/", include: ["com.google.android.wearable-stubs.jar"]))
@@ -475,9 +451,6 @@
docs("androidx.work:work-rxjava2:2.10.0-alpha02")
docs("androidx.work:work-rxjava3:2.10.0-alpha02")
docs("androidx.work:work-testing:2.10.0-alpha02")
-
- // Force upgrade to jsoup 1.16.2 (b/309773103)
- stubs("org.jsoup:jsoup:1.16.2")
}
afterEvaluate {
diff --git a/docs-tip-of-tree/build.gradle b/docs-tip-of-tree/build.gradle
index fd5648d..96f3018 100644
--- a/docs-tip-of-tree/build.gradle
+++ b/docs-tip-of-tree/build.gradle
@@ -17,16 +17,17 @@
}
dependencies {
+ // If there is not at least one samples dependency, DocsImplPlugin breaks. b/332262321
+ samples("androidx.window:window-samples:1.3.0-alpha03")
+
docsForOptionalProject(":xr:xr")
docs(project(":activity:activity"))
docs(project(":activity:activity-compose"))
- samples(project(":activity:activity-compose:activity-compose-samples"))
docs(project(":activity:activity-ktx"))
// ads-identifier is deprecated
kmpDocs(project(":annotation:annotation"))
docs(project(":annotation:annotation-experimental"))
docs(project(":appactions:builtintypes:builtintypes"))
- samples(project(":appactions:builtintypes:builtintypes:builtintypes-samples"))
docs(project(":appactions:interaction:interaction-capabilities-communication"))
docs(project(":appactions:interaction:interaction-capabilities-core"))
docs(project(":appactions:interaction:interaction-capabilities-productivity"))
@@ -56,7 +57,6 @@
docs(project(":benchmark:benchmark-macro-junit4"))
docs(project(":biometric:biometric"))
docs(project(":biometric:biometric-ktx"))
- samples(project(":biometric:biometric-ktx-samples"))
docs(project(":bluetooth:bluetooth"))
docs(project(":bluetooth:bluetooth-testing"))
docs(project(":browser:browser"))
@@ -75,75 +75,49 @@
docs(project(":camera:camera-viewfinder"))
docs(project(":camera:camera-viewfinder-compose"))
docs(project(":camera:camera-viewfinder-core"))
- samples(project(":camera:camera-viewfinder-core:camera-viewfinder-core-samples"))
docs(project(":car:app:app"))
docs(project(":car:app:app-automotive"))
docs(project(":car:app:app-projected"))
docs(project(":car:app:app-testing"))
- samples(project(":car:app:app-samples:navigation-common"))
docs(project(":cardview:cardview"))
kmpDocs(project(":collection:collection"))
docs(project(":collection:collection-ktx"))
kmpDocs(project(":compose:animation:animation"))
kmpDocs(project(":compose:animation:animation-core"))
kmpDocs(project(":compose:animation:animation-graphics"))
- samples(project(":compose:animation:animation-core:animation-core-samples"))
- samples(project(":compose:animation:animation:animation-samples"))
- samples(project(":compose:animation:animation-graphics:animation-graphics-samples"))
kmpDocs(project(":compose:foundation:foundation"))
kmpDocs(project(":compose:foundation:foundation-layout"))
- samples(project(":compose:foundation:foundation-layout:foundation-layout-samples"))
- samples(project(":compose:foundation:foundation:foundation-samples"))
kmpDocs(project(":compose:material3:adaptive:adaptive"))
kmpDocs(project(":compose:material3:adaptive:adaptive-layout"))
kmpDocs(project(":compose:material3:adaptive:adaptive-navigation"))
- samples(project(":compose:material3:adaptive:adaptive-samples"))
kmpDocs(project(":compose:material3:material3"))
- samples(project(":compose:material3:material3:material3-samples"))
kmpDocs(project(":compose:material3:material3-adaptive-navigation-suite"))
- samples(project(":compose:material3:material3-adaptive-navigation-suite:material3-adaptive-navigation-suite-samples"))
kmpDocs(project(":compose:material3:material3-common"))
- samples(project(":compose:material3:material3-common:material3-common-samples"))
kmpDocs(project(":compose:material3:material3-window-size-class"))
- samples(project(":compose:material3:material3-window-size-class:material3-window-size-class-samples"))
kmpDocs(project(":compose:material:material"))
kmpDocs(project(":compose:material:material-icons-core"))
- samples(project(":compose:material:material-icons-core:material-icons-core-samples"))
kmpDocs(project(":compose:material:material-ripple"))
docs(project(":compose:material:material-navigation"))
- samples(project(":compose:material:material-navigation-samples"))
- samples(project(":compose:material:material:material-samples"))
kmpDocs(project(":compose:runtime:runtime"))
- samples(project(":compose:runtime:runtime:runtime-samples"))
docs(project(":compose:runtime:runtime-livedata"))
- samples(project(":compose:runtime:runtime-livedata:runtime-livedata-samples"))
docs(project(":compose:runtime:runtime-rxjava2"))
- samples(project(":compose:runtime:runtime-rxjava2:runtime-rxjava2-samples"))
docs(project(":compose:runtime:runtime-rxjava3"))
- samples(project(":compose:runtime:runtime-rxjava3:runtime-rxjava3-samples"))
kmpDocs(project(":compose:runtime:runtime-saveable"))
- samples(project(":compose:runtime:runtime-saveable:runtime-saveable-samples"))
docs(project(":compose:runtime:runtime-tracing"))
kmpDocs(project(":compose:ui:ui"))
docs(project(":compose:ui:ui-android-stubs"))
kmpDocs(project(":compose:ui:ui-geometry"))
kmpDocs(project(":compose:ui:ui-graphics"))
- samples(project(":compose:ui:ui-graphics:ui-graphics-samples"))
kmpDocs(project(":compose:ui:ui-test"))
- samples(project(":compose:ui:ui-test:ui-test-samples"))
kmpDocs(project(":compose:ui:ui-test-junit4"))
kmpDocs(project(":compose:ui:ui-text"))
- samples(project(":compose:ui:ui-text:ui-text-samples"))
docs(project(":compose:ui:ui-text-google-fonts"))
kmpDocs(project(":compose:ui:ui-tooling"))
kmpDocs(project(":compose:ui:ui-tooling-data"))
kmpDocs(project(":compose:ui:ui-tooling-preview"))
kmpDocs(project(":compose:ui:ui-unit"))
- samples(project(":compose:ui:ui-unit:ui-unit-samples"))
kmpDocs(project(":compose:ui:ui-util"))
docs(project(":compose:ui:ui-viewbinding"))
- samples(project(":compose:ui:ui-viewbinding:ui-viewbinding-samples"))
- samples(project(":compose:ui:ui:ui-samples"))
docs(project(":concurrent:concurrent-futures"))
docs(project(":concurrent:concurrent-futures-ktx"))
docs(project(":constraintlayout:constraintlayout"))
@@ -157,14 +131,12 @@
docs(project(":core:core-appdigest"))
docs(project(":core:core-google-shortcuts"))
docs(project(":core:haptics:haptics"))
- samples(project(":core:haptics:haptics-samples"))
docs(project(":core:core-i18n"))
docs(project(":core:core-ktx"))
docs(project(":core:core-location-altitude"))
docs(project(":core:core-performance"))
docs(project(":core:core-performance-play-services"))
docs(project(":core:core-performance-testing"))
- samples(project(":core:core-performance:core-performance-samples"))
docs(project(":core:core-remoteviews"))
docs(project(":core:core-role"))
docs(project(":core:core-splashscreen"))
@@ -174,7 +146,6 @@
docs(project(":core:uwb:uwb-rxjava3"))
docs(project(":credentials:credentials"))
docs(project(":credentials:credentials-fido"))
- samples(project(":credentials:credentials-samples"))
docs(project(":credentials:credentials-play-services-auth"))
docs(project(":credentials:credentials-e2ee"))
docs(project(":cursoradapter:cursoradapter"))
@@ -207,14 +178,11 @@
docs(project(":exifinterface:exifinterface"))
docs(project(":fragment:fragment"))
docs(project(":fragment:fragment-compose"))
- samples(project(":fragment:fragment-compose:fragment-compose-samples"))
docs(project(":fragment:fragment-ktx"))
docs(project(":fragment:fragment-testing"))
docs(project(":glance:glance"))
docs(project(":glance:glance-appwidget"))
docs(project(":glance:glance-appwidget-testing"))
- samples(project(":glance:glance-appwidget:glance-appwidget-samples"))
- samples(project(":glance:glance-appwidget-testing:glance-appwidget-testing-samples"))
docs(project(":glance:glance-appwidget-preview"))
docs(project(":glance:glance-material"))
docs(project(":glance:glance-material3"))
@@ -224,18 +192,15 @@
docs(project(":glance:glance-wear-tiles"))
docs(project(":graphics:filters:filters"))
docs(project(":graphics:graphics-core"))
- samples(project(":graphics:graphics-core:graphics-core-samples"))
docs(project(":graphics:graphics-path"))
kmpDocs(project(":graphics:graphics-shapes"))
docs(project(":gridlayout:gridlayout"))
docs(project(":health:connect:connect-client"))
- samples(project(":health:connect:connect-client-samples"))
docs(project(":health:health-services-client"))
docs(project(":heifwriter:heifwriter"))
docs(project(":hilt:hilt-common"))
docs(project(":hilt:hilt-navigation"))
docs(project(":hilt:hilt-navigation-compose"))
- samples(project(":hilt:hilt-navigation-compose-samples"))
docs(project(":hilt:hilt-navigation-fragment"))
docs(project(":hilt:hilt-work"))
docs(project(":input:input-motionprediction"))
@@ -258,13 +223,11 @@
docs(project(":lifecycle:lifecycle-reactivestreams-ktx"))
kmpDocs(project(":lifecycle:lifecycle-runtime"))
kmpDocs(project(":lifecycle:lifecycle-runtime-compose"))
- samples(project(":lifecycle:lifecycle-runtime-compose:lifecycle-runtime-compose-samples"))
kmpDocs(project(":lifecycle:lifecycle-runtime-ktx"))
docs(project(":lifecycle:lifecycle-runtime-testing"))
docs(project(":lifecycle:lifecycle-service"))
kmpDocs(project(":lifecycle:lifecycle-viewmodel"))
kmpDocs(project(":lifecycle:lifecycle-viewmodel-compose"))
- samples(project(":lifecycle:lifecycle-viewmodel-compose:lifecycle-viewmodel-compose-samples"))
docs(project(":lifecycle:lifecycle-viewmodel-ktx"))
docs(project(":lifecycle:lifecycle-viewmodel-savedstate"))
docs(project(":loader:loader"))
@@ -278,7 +241,6 @@
docs(project(":navigation:navigation-common"))
docs(project(":navigation:navigation-common-ktx"))
docs(project(":navigation:navigation-compose"))
- samples(project(":navigation:navigation-compose:navigation-compose-samples"))
docs(project(":navigation:navigation-dynamic-features-fragment"))
docs(project(":navigation:navigation-dynamic-features-runtime"))
docs(project(":navigation:navigation-fragment"))
@@ -292,14 +254,12 @@
kmpDocs(project(":paging:paging-common"))
docs(project(":paging:paging-common-ktx"))
kmpDocs(project(":paging:paging-compose"))
- samples(project(":paging:paging-compose:paging-compose-samples"))
docs(project(":paging:paging-guava"))
docs(project(":paging:paging-runtime"))
docs(project(":paging:paging-runtime-ktx"))
docs(project(":paging:paging-rxjava2"))
docs(project(":paging:paging-rxjava2-ktx"))
docs(project(":paging:paging-rxjava3"))
- samples(project(":paging:paging-samples"))
kmpDocs(project(":paging:paging-testing"))
docs(project(":palette:palette"))
docs(project(":palette:palette-ktx"))
@@ -374,7 +334,6 @@
docs(project(":transition:transition-ktx"))
docs(project(":tv:tv-foundation"))
docs(project(":tv:tv-material"))
- samples(project(":tv:tv-samples"))
docs(project(":tvprovider:tvprovider"))
docs(project(":vectordrawable:vectordrawable"))
docs(project(":vectordrawable:vectordrawable-animated"))
@@ -383,14 +342,10 @@
docs(project(":viewpager2:viewpager2"))
docs(project(":viewpager:viewpager"))
docs(project(":wear:compose:compose-foundation"))
- samples(project(":wear:compose:compose-foundation-samples"))
docs(project(":wear:compose:compose-material"))
docs(project(":wear:compose:compose-material-core"))
- samples(project(":wear:compose:compose-material-samples"))
docs(project(":wear:compose:compose-material3"))
- samples(project(":wear:compose:compose-material3-samples"))
docs(project(":wear:compose:compose-navigation"))
- samples(project(":wear:compose:compose-navigation-samples"))
docs(project(":wear:compose:compose-ui-tooling"))
docs(project(":wear:protolayout:protolayout"))
docs(project(":wear:protolayout:protolayout-expression"))
@@ -411,26 +366,20 @@
docs(project(":wear:watchface:watchface-complications-data"))
docs(project(":wear:watchface:watchface-complications-data-source"))
docs(project(":wear:watchface:watchface-complications-data-source-ktx"))
- samples(project(":wear:watchface:watchface-complications-permission-dialogs-sample"))
docs(project(":wear:watchface:watchface-complications-rendering"))
docs(project(":wear:watchface:watchface-data"))
docs(project(":wear:watchface:watchface-editor"))
docs(project(":wear:watchface:watchface-editor-guava"))
- samples(project(":wear:watchface:watchface-editor-samples"))
docs(project(":wear:watchface:watchface-guava"))
- samples(project(":wear:watchface:watchface-samples"))
docs(project(":wear:watchface:watchface-style"))
docs(project(":wear:wear"))
docs(project(":wear:wear-core"))
stubs(fileTree(dir: "../wear/wear_stubs/", include: ["com.google.android.wearable-stubs.jar"]))
docs(project(":wear:wear-input"))
- samples(project(":wear:wear-input-samples"))
docs(project(":wear:wear-input-testing"))
docs(project(":wear:wear-ongoing"))
docs(project(":wear:wear-phone-interactions"))
- samples(project(":wear:wear-phone-interactions-samples"))
docs(project(":wear:wear-remote-interactions"))
- samples(project(":wear:wear-remote-interactions-samples"))
docs(project(":wear:wear-tooling-preview"))
docs(project(":webkit:webkit"))
docs(project(":window:window"))
@@ -441,7 +390,6 @@
docs(project(":window:window-java"))
docs(project(":window:window-rxjava2"))
docs(project(":window:window-rxjava3"))
- samples(project(":window:window-samples"))
docs(project(":window:window-testing"))
docs(project(":work:work-gcm"))
docs(project(":work:work-multiprocess"))
diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml
index 6601baa..ccbb1dd 100644
--- a/gradle/verification-metadata.xml
+++ b/gradle/verification-metadata.xml
@@ -644,6 +644,11 @@
<sha256 value="6d4e2b5a118aab62e6e5e29d185a0224eed82c85c40ac3d33cf04a270c3b3744" reason="https://github.com/google/guava/issues/7154"/>
</artifact>
</component>
+ <component group="com.google.guava" name="guava" version="33.0.0-android">
+ <artifact name="guava-33.0.0-jre.jar">
+ <sha256 value="f4d85c3e4d411694337cb873abea09b242b664bb013320be6105327c45991537" reason="https://github.com/google/guava/issues/7154"/>
+ </artifact>
+ </component>
<component group="com.google.prefab" name="cli" version="2.1.0">
<artifact name="cli-2.1.0-all.jar">
<sha256 value="e219c8cd6bfd9ff71503a57c6af34b9ba060f03525cc3e58330dee53245a5ed6" origin="Generated by Gradle" reason="https://github.com/google/prefab/issues/157"/>
diff --git a/graphics/graphics-shapes/api/1.0.0-beta02.txt b/graphics/graphics-shapes/api/1.0.0-beta02.txt
new file mode 100644
index 0000000..f9d62d6f
--- /dev/null
+++ b/graphics/graphics-shapes/api/1.0.0-beta02.txt
@@ -0,0 +1,163 @@
+// Signature format: 4.0
+package androidx.graphics.shapes {
+
+ public final class CornerRounding {
+ ctor public CornerRounding(optional @FloatRange(from=0.0) float radius, optional @FloatRange(from=0.0, to=1.0) float smoothing);
+ method public float getRadius();
+ method public float getSmoothing();
+ property public final float radius;
+ property public final float smoothing;
+ field public static final androidx.graphics.shapes.CornerRounding.Companion Companion;
+ field public static final androidx.graphics.shapes.CornerRounding Unrounded;
+ }
+
+ public static final class CornerRounding.Companion {
+ }
+
+ public class Cubic {
+ method public static final androidx.graphics.shapes.Cubic circularArc(float centerX, float centerY, float x0, float y0, float x1, float y1);
+ method public final operator androidx.graphics.shapes.Cubic div(float x);
+ method public final operator androidx.graphics.shapes.Cubic div(int x);
+ method public final float getAnchor0X();
+ method public final float getAnchor0Y();
+ method public final float getAnchor1X();
+ method public final float getAnchor1Y();
+ method public final float getControl0X();
+ method public final float getControl0Y();
+ method public final float getControl1X();
+ method public final float getControl1Y();
+ method public final operator androidx.graphics.shapes.Cubic plus(androidx.graphics.shapes.Cubic o);
+ method public final androidx.graphics.shapes.Cubic reverse();
+ method public final kotlin.Pair<androidx.graphics.shapes.Cubic,androidx.graphics.shapes.Cubic> split(float t);
+ method public static final androidx.graphics.shapes.Cubic straightLine(float x0, float y0, float x1, float y1);
+ method public final operator androidx.graphics.shapes.Cubic times(float x);
+ method public final operator androidx.graphics.shapes.Cubic times(int x);
+ method public final androidx.graphics.shapes.Cubic transformed(androidx.graphics.shapes.PointTransformer f);
+ property public final float anchor0X;
+ property public final float anchor0Y;
+ property public final float anchor1X;
+ property public final float anchor1Y;
+ property public final float control0X;
+ property public final float control0Y;
+ property public final float control1X;
+ property public final float control1Y;
+ field public static final androidx.graphics.shapes.Cubic.Companion Companion;
+ }
+
+ public static final class Cubic.Companion {
+ method public androidx.graphics.shapes.Cubic circularArc(float centerX, float centerY, float x0, float y0, float x1, float y1);
+ method public androidx.graphics.shapes.Cubic straightLine(float x0, float y0, float x1, float y1);
+ }
+
+ public final class CubicKt {
+ method public static androidx.graphics.shapes.Cubic Cubic(float anchor0X, float anchor0Y, float control0X, float control0Y, float control1X, float control1Y, float anchor1X, float anchor1Y);
+ }
+
+ public final class Morph {
+ ctor public Morph(androidx.graphics.shapes.RoundedPolygon start, androidx.graphics.shapes.RoundedPolygon end);
+ method public java.util.List<androidx.graphics.shapes.Cubic> asCubics(float progress);
+ method public float[] calculateBounds();
+ method public float[] calculateBounds(optional float[] bounds);
+ method public float[] calculateBounds(optional float[] bounds, optional boolean approximate);
+ method public float[] calculateMaxBounds(optional float[] bounds);
+ method public inline void forEachCubic(float progress, optional androidx.graphics.shapes.MutableCubic mutableCubic, kotlin.jvm.functions.Function1<? super androidx.graphics.shapes.MutableCubic,kotlin.Unit> callback);
+ method public inline void forEachCubic(float progress, kotlin.jvm.functions.Function1<? super androidx.graphics.shapes.MutableCubic,kotlin.Unit> callback);
+ }
+
+ public final class MutableCubic extends androidx.graphics.shapes.Cubic {
+ ctor public MutableCubic();
+ method public void interpolate(androidx.graphics.shapes.Cubic c1, androidx.graphics.shapes.Cubic c2, float progress);
+ method public void transform(androidx.graphics.shapes.PointTransformer f);
+ }
+
+ public interface MutablePoint {
+ method public float getX();
+ method public float getY();
+ method public void setX(float);
+ method public void setY(float);
+ property public abstract float x;
+ property public abstract float y;
+ }
+
+ public fun interface PointTransformer {
+ method public long transform(float x, float y);
+ }
+
+ public final class RoundedPolygon {
+ method public float[] calculateBounds();
+ method public float[] calculateBounds(optional float[] bounds);
+ method public float[] calculateBounds(optional float[] bounds, optional boolean approximate);
+ method public float[] calculateMaxBounds(optional float[] bounds);
+ method public float getCenterX();
+ method public float getCenterY();
+ method public java.util.List<androidx.graphics.shapes.Cubic> getCubics();
+ method public androidx.graphics.shapes.RoundedPolygon normalized();
+ method public androidx.graphics.shapes.RoundedPolygon transformed(androidx.graphics.shapes.PointTransformer f);
+ property public final float centerX;
+ property public final float centerY;
+ property public final java.util.List<androidx.graphics.shapes.Cubic> cubics;
+ field public static final androidx.graphics.shapes.RoundedPolygon.Companion Companion;
+ }
+
+ public static final class RoundedPolygon.Companion {
+ }
+
+ public final class RoundedPolygonKt {
+ method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(androidx.graphics.shapes.RoundedPolygon source);
+ method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(float[] vertices);
+ method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(float[] vertices, optional androidx.graphics.shapes.CornerRounding rounding);
+ method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(float[] vertices, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding);
+ method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(float[] vertices, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional float centerX);
+ method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(float[] vertices, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional float centerX, optional float centerY);
+ method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(@IntRange(from=3L) int numVertices);
+ method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(@IntRange(from=3L) int numVertices, optional float radius);
+ method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(@IntRange(from=3L) int numVertices, optional float radius, optional float centerX);
+ method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(@IntRange(from=3L) int numVertices, optional float radius, optional float centerX, optional float centerY);
+ method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(@IntRange(from=3L) int numVertices, optional float radius, optional float centerX, optional float centerY, optional androidx.graphics.shapes.CornerRounding rounding);
+ method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(@IntRange(from=3L) int numVertices, optional float radius, optional float centerX, optional float centerY, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding);
+ }
+
+ public final class ShapesKt {
+ method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion);
+ method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional @IntRange(from=3L) int numVertices);
+ method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional @IntRange(from=3L) int numVertices, optional float radius);
+ method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional @IntRange(from=3L) int numVertices, optional float radius, optional float centerX);
+ method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional @IntRange(from=3L) int numVertices, optional float radius, optional float centerX, optional float centerY);
+ method public static androidx.graphics.shapes.RoundedPolygon pill(androidx.graphics.shapes.RoundedPolygon.Companion);
+ method public static androidx.graphics.shapes.RoundedPolygon pill(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width);
+ method public static androidx.graphics.shapes.RoundedPolygon pill(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height);
+ method public static androidx.graphics.shapes.RoundedPolygon pill(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional float smoothing);
+ method public static androidx.graphics.shapes.RoundedPolygon pill(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional float smoothing, optional float centerX);
+ method public static androidx.graphics.shapes.RoundedPolygon pill(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional float smoothing, optional float centerX, optional float centerY);
+ method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion);
+ method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width);
+ method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height);
+ method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional int numVerticesPerRadius);
+ method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional int numVerticesPerRadius, optional @FloatRange(from=0.0, fromInclusive=false, to=1.0, toInclusive=false) float innerRadiusRatio);
+ method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional int numVerticesPerRadius, optional @FloatRange(from=0.0, fromInclusive=false, to=1.0, toInclusive=false) float innerRadiusRatio, optional androidx.graphics.shapes.CornerRounding rounding);
+ method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional int numVerticesPerRadius, optional @FloatRange(from=0.0, fromInclusive=false, to=1.0, toInclusive=false) float innerRadiusRatio, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding);
+ method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional int numVerticesPerRadius, optional @FloatRange(from=0.0, fromInclusive=false, to=1.0, toInclusive=false) float innerRadiusRatio, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding);
+ method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional int numVerticesPerRadius, optional @FloatRange(from=0.0, fromInclusive=false, to=1.0, toInclusive=false) float innerRadiusRatio, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional @FloatRange(from=0.0, to=1.0) float vertexSpacing);
+ method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional int numVerticesPerRadius, optional @FloatRange(from=0.0, fromInclusive=false, to=1.0, toInclusive=false) float innerRadiusRatio, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional @FloatRange(from=0.0, to=1.0) float vertexSpacing, optional @FloatRange(from=0.0, to=1.0) float startLocation);
+ method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional int numVerticesPerRadius, optional @FloatRange(from=0.0, fromInclusive=false, to=1.0, toInclusive=false) float innerRadiusRatio, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional @FloatRange(from=0.0, to=1.0) float vertexSpacing, optional @FloatRange(from=0.0, to=1.0) float startLocation, optional float centerX);
+ method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional int numVerticesPerRadius, optional @FloatRange(from=0.0, fromInclusive=false, to=1.0, toInclusive=false) float innerRadiusRatio, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional @FloatRange(from=0.0, to=1.0) float vertexSpacing, optional @FloatRange(from=0.0, to=1.0) float startLocation, optional float centerX, optional float centerY);
+ method public static androidx.graphics.shapes.RoundedPolygon rectangle(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional float centerX, optional float centerY);
+ method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius);
+ method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius);
+ method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius, optional float innerRadius);
+ method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius, optional float innerRadius, optional androidx.graphics.shapes.CornerRounding rounding);
+ method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius, optional float innerRadius, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding);
+ method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius, optional float innerRadius, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding);
+ method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius, optional float innerRadius, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional float centerX);
+ method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius, optional float innerRadius, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional float centerX, optional float centerY);
+ }
+
+ public final class Shapes_androidKt {
+ method public static android.graphics.Path toPath(androidx.graphics.shapes.Morph, float progress, optional android.graphics.Path path);
+ method public static android.graphics.Path toPath(androidx.graphics.shapes.RoundedPolygon);
+ method public static android.graphics.Path toPath(androidx.graphics.shapes.RoundedPolygon, optional android.graphics.Path path);
+ method public static androidx.graphics.shapes.RoundedPolygon transformed(androidx.graphics.shapes.RoundedPolygon, android.graphics.Matrix matrix);
+ }
+
+}
+
diff --git a/graphics/graphics-shapes/api/res-1.0.0-beta02.txt b/graphics/graphics-shapes/api/res-1.0.0-beta02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/graphics/graphics-shapes/api/res-1.0.0-beta02.txt
diff --git a/graphics/graphics-shapes/api/restricted_1.0.0-beta02.txt b/graphics/graphics-shapes/api/restricted_1.0.0-beta02.txt
new file mode 100644
index 0000000..d691f29
--- /dev/null
+++ b/graphics/graphics-shapes/api/restricted_1.0.0-beta02.txt
@@ -0,0 +1,164 @@
+// Signature format: 4.0
+package androidx.graphics.shapes {
+
+ public final class CornerRounding {
+ ctor public CornerRounding(optional @FloatRange(from=0.0) float radius, optional @FloatRange(from=0.0, to=1.0) float smoothing);
+ method public float getRadius();
+ method public float getSmoothing();
+ property public final float radius;
+ property public final float smoothing;
+ field public static final androidx.graphics.shapes.CornerRounding.Companion Companion;
+ field public static final androidx.graphics.shapes.CornerRounding Unrounded;
+ }
+
+ public static final class CornerRounding.Companion {
+ }
+
+ public class Cubic {
+ method public static final androidx.graphics.shapes.Cubic circularArc(float centerX, float centerY, float x0, float y0, float x1, float y1);
+ method public final operator androidx.graphics.shapes.Cubic div(float x);
+ method public final operator androidx.graphics.shapes.Cubic div(int x);
+ method public final float getAnchor0X();
+ method public final float getAnchor0Y();
+ method public final float getAnchor1X();
+ method public final float getAnchor1Y();
+ method public final float getControl0X();
+ method public final float getControl0Y();
+ method public final float getControl1X();
+ method public final float getControl1Y();
+ method public final operator androidx.graphics.shapes.Cubic plus(androidx.graphics.shapes.Cubic o);
+ method public final androidx.graphics.shapes.Cubic reverse();
+ method public final kotlin.Pair<androidx.graphics.shapes.Cubic,androidx.graphics.shapes.Cubic> split(float t);
+ method public static final androidx.graphics.shapes.Cubic straightLine(float x0, float y0, float x1, float y1);
+ method public final operator androidx.graphics.shapes.Cubic times(float x);
+ method public final operator androidx.graphics.shapes.Cubic times(int x);
+ method public final androidx.graphics.shapes.Cubic transformed(androidx.graphics.shapes.PointTransformer f);
+ property public final float anchor0X;
+ property public final float anchor0Y;
+ property public final float anchor1X;
+ property public final float anchor1Y;
+ property public final float control0X;
+ property public final float control0Y;
+ property public final float control1X;
+ property public final float control1Y;
+ field public static final androidx.graphics.shapes.Cubic.Companion Companion;
+ }
+
+ public static final class Cubic.Companion {
+ method public androidx.graphics.shapes.Cubic circularArc(float centerX, float centerY, float x0, float y0, float x1, float y1);
+ method public androidx.graphics.shapes.Cubic straightLine(float x0, float y0, float x1, float y1);
+ }
+
+ public final class CubicKt {
+ method public static androidx.graphics.shapes.Cubic Cubic(float anchor0X, float anchor0Y, float control0X, float control0Y, float control1X, float control1Y, float anchor1X, float anchor1Y);
+ }
+
+ public final class Morph {
+ ctor public Morph(androidx.graphics.shapes.RoundedPolygon start, androidx.graphics.shapes.RoundedPolygon end);
+ method public java.util.List<androidx.graphics.shapes.Cubic> asCubics(float progress);
+ method public float[] calculateBounds();
+ method public float[] calculateBounds(optional float[] bounds);
+ method public float[] calculateBounds(optional float[] bounds, optional boolean approximate);
+ method public float[] calculateMaxBounds(optional float[] bounds);
+ method public inline void forEachCubic(float progress, optional androidx.graphics.shapes.MutableCubic mutableCubic, kotlin.jvm.functions.Function1<? super androidx.graphics.shapes.MutableCubic,kotlin.Unit> callback);
+ method public inline void forEachCubic(float progress, kotlin.jvm.functions.Function1<? super androidx.graphics.shapes.MutableCubic,kotlin.Unit> callback);
+ property @kotlin.PublishedApi internal final java.util.List<kotlin.Pair<androidx.graphics.shapes.Cubic,androidx.graphics.shapes.Cubic>> morphMatch;
+ }
+
+ public final class MutableCubic extends androidx.graphics.shapes.Cubic {
+ ctor public MutableCubic();
+ method public void interpolate(androidx.graphics.shapes.Cubic c1, androidx.graphics.shapes.Cubic c2, float progress);
+ method public void transform(androidx.graphics.shapes.PointTransformer f);
+ }
+
+ public interface MutablePoint {
+ method public float getX();
+ method public float getY();
+ method public void setX(float);
+ method public void setY(float);
+ property public abstract float x;
+ property public abstract float y;
+ }
+
+ public fun interface PointTransformer {
+ method public long transform(float x, float y);
+ }
+
+ public final class RoundedPolygon {
+ method public float[] calculateBounds();
+ method public float[] calculateBounds(optional float[] bounds);
+ method public float[] calculateBounds(optional float[] bounds, optional boolean approximate);
+ method public float[] calculateMaxBounds(optional float[] bounds);
+ method public float getCenterX();
+ method public float getCenterY();
+ method public java.util.List<androidx.graphics.shapes.Cubic> getCubics();
+ method public androidx.graphics.shapes.RoundedPolygon normalized();
+ method public androidx.graphics.shapes.RoundedPolygon transformed(androidx.graphics.shapes.PointTransformer f);
+ property public final float centerX;
+ property public final float centerY;
+ property public final java.util.List<androidx.graphics.shapes.Cubic> cubics;
+ field public static final androidx.graphics.shapes.RoundedPolygon.Companion Companion;
+ }
+
+ public static final class RoundedPolygon.Companion {
+ }
+
+ public final class RoundedPolygonKt {
+ method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(androidx.graphics.shapes.RoundedPolygon source);
+ method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(float[] vertices);
+ method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(float[] vertices, optional androidx.graphics.shapes.CornerRounding rounding);
+ method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(float[] vertices, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding);
+ method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(float[] vertices, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional float centerX);
+ method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(float[] vertices, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional float centerX, optional float centerY);
+ method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(@IntRange(from=3L) int numVertices);
+ method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(@IntRange(from=3L) int numVertices, optional float radius);
+ method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(@IntRange(from=3L) int numVertices, optional float radius, optional float centerX);
+ method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(@IntRange(from=3L) int numVertices, optional float radius, optional float centerX, optional float centerY);
+ method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(@IntRange(from=3L) int numVertices, optional float radius, optional float centerX, optional float centerY, optional androidx.graphics.shapes.CornerRounding rounding);
+ method public static androidx.graphics.shapes.RoundedPolygon RoundedPolygon(@IntRange(from=3L) int numVertices, optional float radius, optional float centerX, optional float centerY, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding);
+ }
+
+ public final class ShapesKt {
+ method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion);
+ method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional @IntRange(from=3L) int numVertices);
+ method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional @IntRange(from=3L) int numVertices, optional float radius);
+ method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional @IntRange(from=3L) int numVertices, optional float radius, optional float centerX);
+ method public static androidx.graphics.shapes.RoundedPolygon circle(androidx.graphics.shapes.RoundedPolygon.Companion, optional @IntRange(from=3L) int numVertices, optional float radius, optional float centerX, optional float centerY);
+ method public static androidx.graphics.shapes.RoundedPolygon pill(androidx.graphics.shapes.RoundedPolygon.Companion);
+ method public static androidx.graphics.shapes.RoundedPolygon pill(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width);
+ method public static androidx.graphics.shapes.RoundedPolygon pill(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height);
+ method public static androidx.graphics.shapes.RoundedPolygon pill(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional float smoothing);
+ method public static androidx.graphics.shapes.RoundedPolygon pill(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional float smoothing, optional float centerX);
+ method public static androidx.graphics.shapes.RoundedPolygon pill(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional float smoothing, optional float centerX, optional float centerY);
+ method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion);
+ method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width);
+ method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height);
+ method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional int numVerticesPerRadius);
+ method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional int numVerticesPerRadius, optional @FloatRange(from=0.0, fromInclusive=false, to=1.0, toInclusive=false) float innerRadiusRatio);
+ method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional int numVerticesPerRadius, optional @FloatRange(from=0.0, fromInclusive=false, to=1.0, toInclusive=false) float innerRadiusRatio, optional androidx.graphics.shapes.CornerRounding rounding);
+ method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional int numVerticesPerRadius, optional @FloatRange(from=0.0, fromInclusive=false, to=1.0, toInclusive=false) float innerRadiusRatio, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding);
+ method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional int numVerticesPerRadius, optional @FloatRange(from=0.0, fromInclusive=false, to=1.0, toInclusive=false) float innerRadiusRatio, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding);
+ method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional int numVerticesPerRadius, optional @FloatRange(from=0.0, fromInclusive=false, to=1.0, toInclusive=false) float innerRadiusRatio, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional @FloatRange(from=0.0, to=1.0) float vertexSpacing);
+ method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional int numVerticesPerRadius, optional @FloatRange(from=0.0, fromInclusive=false, to=1.0, toInclusive=false) float innerRadiusRatio, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional @FloatRange(from=0.0, to=1.0) float vertexSpacing, optional @FloatRange(from=0.0, to=1.0) float startLocation);
+ method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional int numVerticesPerRadius, optional @FloatRange(from=0.0, fromInclusive=false, to=1.0, toInclusive=false) float innerRadiusRatio, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional @FloatRange(from=0.0, to=1.0) float vertexSpacing, optional @FloatRange(from=0.0, to=1.0) float startLocation, optional float centerX);
+ method public static androidx.graphics.shapes.RoundedPolygon pillStar(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional int numVerticesPerRadius, optional @FloatRange(from=0.0, fromInclusive=false, to=1.0, toInclusive=false) float innerRadiusRatio, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional @FloatRange(from=0.0, to=1.0) float vertexSpacing, optional @FloatRange(from=0.0, to=1.0) float startLocation, optional float centerX, optional float centerY);
+ method public static androidx.graphics.shapes.RoundedPolygon rectangle(androidx.graphics.shapes.RoundedPolygon.Companion, optional float width, optional float height, optional androidx.graphics.shapes.CornerRounding rounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional float centerX, optional float centerY);
+ method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius);
+ method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius);
+ method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius, optional float innerRadius);
+ method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius, optional float innerRadius, optional androidx.graphics.shapes.CornerRounding rounding);
+ method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius, optional float innerRadius, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding);
+ method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius, optional float innerRadius, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding);
+ method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius, optional float innerRadius, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional float centerX);
+ method public static androidx.graphics.shapes.RoundedPolygon star(androidx.graphics.shapes.RoundedPolygon.Companion, int numVerticesPerRadius, optional float radius, optional float innerRadius, optional androidx.graphics.shapes.CornerRounding rounding, optional androidx.graphics.shapes.CornerRounding? innerRounding, optional java.util.List<androidx.graphics.shapes.CornerRounding>? perVertexRounding, optional float centerX, optional float centerY);
+ }
+
+ public final class Shapes_androidKt {
+ method public static android.graphics.Path toPath(androidx.graphics.shapes.Morph, float progress, optional android.graphics.Path path);
+ method public static android.graphics.Path toPath(androidx.graphics.shapes.RoundedPolygon);
+ method public static android.graphics.Path toPath(androidx.graphics.shapes.RoundedPolygon, optional android.graphics.Path path);
+ method public static androidx.graphics.shapes.RoundedPolygon transformed(androidx.graphics.shapes.RoundedPolygon, android.graphics.Matrix matrix);
+ }
+
+}
+
diff --git a/graphics/graphics-shapes/build.gradle b/graphics/graphics-shapes/build.gradle
index 3fcc603..c75fad11 100644
--- a/graphics/graphics-shapes/build.gradle
+++ b/graphics/graphics-shapes/build.gradle
@@ -71,7 +71,7 @@
dependsOn(jvmMain)
dependencies {
implementation("androidx.core:core-ktx:1.10.0")
- implementation(project(":annotation:annotation-experimental"))
+ implementation("androidx.annotation:annotation-experimental:1.4.0-rc01")
}
}
diff --git a/libraryversions.toml b/libraryversions.toml
index 91ec465..93c28a5 100644
--- a/libraryversions.toml
+++ b/libraryversions.toml
@@ -73,7 +73,7 @@
GRAPHICS_CORE = "1.0.0-rc01"
GRAPHICS_FILTERS = "1.0.0-alpha01"
GRAPHICS_PATH = "1.0.0-rc01"
-GRAPHICS_SHAPES = "1.0.0-beta01"
+GRAPHICS_SHAPES = "1.0.0-beta02"
GRIDLAYOUT = "1.1.0-beta02"
HEALTH_CONNECT = "1.1.0-alpha08"
HEALTH_SERVICES_CLIENT = "1.1.0-alpha03"
diff --git a/navigation/navigation-common/api/current.txt b/navigation/navigation-common/api/current.txt
index 9c913ab..a18b9c5 100644
--- a/navigation/navigation-common/api/current.txt
+++ b/navigation/navigation-common/api/current.txt
@@ -163,7 +163,7 @@
method public androidx.navigation.NavDeepLink.Builder setAction(String action);
method public androidx.navigation.NavDeepLink.Builder setMimeType(String mimeType);
method public androidx.navigation.NavDeepLink.Builder setUriPattern(String uriPattern);
- method @SuppressCompatibility @androidx.navigation.ExperimentalSafeArgsApi public inline <reified T> androidx.navigation.NavDeepLink.Builder setUriPattern(String uriPattern, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<?>> typeMap);
+ method @SuppressCompatibility @androidx.navigation.ExperimentalSafeArgsApi public inline <reified T> androidx.navigation.NavDeepLink.Builder setUriPattern(String basePath, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<?>> typeMap);
}
@kotlin.DslMarker public @interface NavDeepLinkDsl {
@@ -455,16 +455,21 @@
property public boolean isNullableAllowed;
property public String name;
field public static final androidx.navigation.NavType<boolean[]?> BoolArrayType;
+ field public static final androidx.navigation.NavType<java.util.List<java.lang.Boolean>?> BoolListType;
field public static final androidx.navigation.NavType<java.lang.Boolean> BoolType;
field public static final androidx.navigation.NavType.Companion Companion;
field public static final androidx.navigation.NavType<float[]?> FloatArrayType;
+ field public static final androidx.navigation.NavType<java.util.List<java.lang.Float>?> FloatListType;
field public static final androidx.navigation.NavType<java.lang.Float> FloatType;
field public static final androidx.navigation.NavType<int[]?> IntArrayType;
+ field public static final androidx.navigation.NavType<java.util.List<java.lang.Integer>?> IntListType;
field public static final androidx.navigation.NavType<java.lang.Integer> IntType;
field public static final androidx.navigation.NavType<long[]?> LongArrayType;
+ field public static final androidx.navigation.NavType<java.util.List<java.lang.Long>?> LongListType;
field public static final androidx.navigation.NavType<java.lang.Long> LongType;
field public static final androidx.navigation.NavType<java.lang.Integer> ReferenceType;
field public static final androidx.navigation.NavType<java.lang.String[]?> StringArrayType;
+ field public static final androidx.navigation.NavType<java.util.List<java.lang.String>?> StringListType;
field public static final androidx.navigation.NavType<java.lang.String?> StringType;
}
diff --git a/navigation/navigation-common/api/restricted_current.txt b/navigation/navigation-common/api/restricted_current.txt
index 9c913ab..a18b9c5 100644
--- a/navigation/navigation-common/api/restricted_current.txt
+++ b/navigation/navigation-common/api/restricted_current.txt
@@ -163,7 +163,7 @@
method public androidx.navigation.NavDeepLink.Builder setAction(String action);
method public androidx.navigation.NavDeepLink.Builder setMimeType(String mimeType);
method public androidx.navigation.NavDeepLink.Builder setUriPattern(String uriPattern);
- method @SuppressCompatibility @androidx.navigation.ExperimentalSafeArgsApi public inline <reified T> androidx.navigation.NavDeepLink.Builder setUriPattern(String uriPattern, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<?>> typeMap);
+ method @SuppressCompatibility @androidx.navigation.ExperimentalSafeArgsApi public inline <reified T> androidx.navigation.NavDeepLink.Builder setUriPattern(String basePath, optional java.util.Map<kotlin.reflect.KType,androidx.navigation.NavType<?>> typeMap);
}
@kotlin.DslMarker public @interface NavDeepLinkDsl {
@@ -455,16 +455,21 @@
property public boolean isNullableAllowed;
property public String name;
field public static final androidx.navigation.NavType<boolean[]?> BoolArrayType;
+ field public static final androidx.navigation.NavType<java.util.List<java.lang.Boolean>?> BoolListType;
field public static final androidx.navigation.NavType<java.lang.Boolean> BoolType;
field public static final androidx.navigation.NavType.Companion Companion;
field public static final androidx.navigation.NavType<float[]?> FloatArrayType;
+ field public static final androidx.navigation.NavType<java.util.List<java.lang.Float>?> FloatListType;
field public static final androidx.navigation.NavType<java.lang.Float> FloatType;
field public static final androidx.navigation.NavType<int[]?> IntArrayType;
+ field public static final androidx.navigation.NavType<java.util.List<java.lang.Integer>?> IntListType;
field public static final androidx.navigation.NavType<java.lang.Integer> IntType;
field public static final androidx.navigation.NavType<long[]?> LongArrayType;
+ field public static final androidx.navigation.NavType<java.util.List<java.lang.Long>?> LongListType;
field public static final androidx.navigation.NavType<java.lang.Long> LongType;
field public static final androidx.navigation.NavType<java.lang.Integer> ReferenceType;
field public static final androidx.navigation.NavType<java.lang.String[]?> StringArrayType;
+ field public static final androidx.navigation.NavType<java.util.List<java.lang.String>?> StringListType;
field public static final androidx.navigation.NavType<java.lang.String?> StringType;
}
diff --git a/navigation/navigation-common/src/androidTest/java/androidx/navigation/NavTypeTest.kt b/navigation/navigation-common/src/androidTest/java/androidx/navigation/NavTypeTest.kt
index c92982f..82dd11c 100644
--- a/navigation/navigation-common/src/androidTest/java/androidx/navigation/NavTypeTest.kt
+++ b/navigation/navigation-common/src/androidTest/java/androidx/navigation/NavTypeTest.kt
@@ -38,14 +38,24 @@
companion object {
private const val i = 1
private val ints = intArrayOf(0, 1)
+ private val intList = listOf(0, 1)
+ private val intArrayList = arrayListOf(0, 1)
private const val l = 1L
private val longs = longArrayOf(0L, 1L)
+ private val longList = listOf(0L, 1L)
+ private val longArrayList = arrayListOf(0L, 1L)
private const val fl = 1.5f
private val floats = floatArrayOf(1f, 2.5f)
+ private val floatList = listOf(1f, 2.5f)
+ private val floatArrayList = arrayListOf(1f, 2.5f)
private const val b = true
private val booleans = booleanArrayOf(b, false)
+ private val boolList = listOf(b, false)
+ private val booArrayList = arrayListOf(b, false)
private const val s = "a_string"
private val strings = arrayOf("aa", "bb")
+ private val stringList = listOf("aa", "bb")
+ private val stringArrayList = arrayListOf("aa", "bb")
private val reference = R.id.nav_id_reference
private val referenceHex = "0x" + R.id.nav_id_reference.toString(16)
private val parcelable = ActivityInfo()
@@ -71,22 +81,32 @@
.isEqualTo(NavType.IntType)
assertThat(NavType.fromArgType("integer[]", null))
.isEqualTo(NavType.IntArrayType)
+ assertThat(NavType.fromArgType("List<Int>", null))
+ .isEqualTo(NavType.IntListType)
assertThat(NavType.fromArgType("long", null))
.isEqualTo(NavType.LongType)
assertThat(NavType.fromArgType("long[]", null))
.isEqualTo(NavType.LongArrayType)
+ assertThat(NavType.fromArgType("List<Long>", null))
+ .isEqualTo(NavType.LongListType)
assertThat(NavType.fromArgType("float", null))
.isEqualTo(NavType.FloatType)
assertThat(NavType.fromArgType("float[]", null))
.isEqualTo(NavType.FloatArrayType)
+ assertThat(NavType.fromArgType("List<Float>", null))
+ .isEqualTo(NavType.FloatListType)
assertThat(NavType.fromArgType("boolean", null))
.isEqualTo(NavType.BoolType)
assertThat(NavType.fromArgType("boolean[]", null))
.isEqualTo(NavType.BoolArrayType)
+ assertThat(NavType.fromArgType("List<Boolean>", null))
+ .isEqualTo(NavType.BoolListType)
assertThat(NavType.fromArgType("string", null))
.isEqualTo(NavType.StringType)
assertThat(NavType.fromArgType("string[]", null))
.isEqualTo(NavType.StringArrayType)
+ assertThat(NavType.fromArgType("List<String>", null))
+ .isEqualTo(NavType.StringListType)
assertThat(NavType.fromArgType("reference", null))
.isEqualTo(NavType.ReferenceType)
assertThat(NavType.fromArgType("android.content.pm.ActivityInfo", null))
@@ -169,6 +189,16 @@
.isEqualTo(ints)
bundle.clear()
+ NavType.IntListType.put(bundle, key, intList)
+ assertThat(NavType.IntListType[bundle, key])
+ .isEqualTo(intList)
+ bundle.clear()
+
+ NavType.IntListType.put(bundle, key, intArrayList)
+ assertThat(NavType.IntListType[bundle, key])
+ .isEqualTo(intArrayList)
+ bundle.clear()
+
NavType.LongType.put(bundle, key, l)
assertThat(NavType.LongType[bundle, key])
.isEqualTo(l)
@@ -179,6 +209,16 @@
.isEqualTo(longs)
bundle.clear()
+ NavType.LongListType.put(bundle, key, longList)
+ assertThat(NavType.LongListType[bundle, key])
+ .isEqualTo(longList)
+ bundle.clear()
+
+ NavType.LongListType.put(bundle, key, longArrayList)
+ assertThat(NavType.LongListType[bundle, key])
+ .isEqualTo(longArrayList)
+ bundle.clear()
+
NavType.FloatType.put(bundle, key, fl)
assertThat(NavType.FloatType[bundle, key])
.isEqualTo(fl)
@@ -189,6 +229,16 @@
.isEqualTo(floats)
bundle.clear()
+ NavType.FloatListType.put(bundle, key, floatList)
+ assertThat(NavType.FloatListType[bundle, key])
+ .isEqualTo(floatList)
+ bundle.clear()
+
+ NavType.FloatListType.put(bundle, key, floatArrayList)
+ assertThat(NavType.FloatListType[bundle, key])
+ .isEqualTo(floatArrayList)
+ bundle.clear()
+
NavType.BoolType.put(bundle, key, b)
assertThat(NavType.BoolType[bundle, key])
.isEqualTo(b)
@@ -199,6 +249,16 @@
.isEqualTo(booleans)
bundle.clear()
+ NavType.BoolListType.put(bundle, key, boolList)
+ assertThat(NavType.BoolListType[bundle, key])
+ .isEqualTo(boolList)
+ bundle.clear()
+
+ NavType.BoolListType.put(bundle, key, booArrayList)
+ assertThat(NavType.BoolListType[bundle, key])
+ .isEqualTo(booArrayList)
+ bundle.clear()
+
NavType.StringType.put(bundle, key, s)
assertThat(NavType.StringType[bundle, key])
.isEqualTo(s)
@@ -209,6 +269,16 @@
.isEqualTo(strings)
bundle.clear()
+ NavType.StringListType.put(bundle, key, stringList)
+ assertThat(NavType.StringListType[bundle, key])
+ .isEqualTo(stringList)
+ bundle.clear()
+
+ NavType.StringListType.put(bundle, key, stringArrayList)
+ assertThat(NavType.StringListType[bundle, key])
+ .isEqualTo(stringArrayList)
+ bundle.clear()
+
NavType.ReferenceType.put(bundle, key, reference)
assertThat(NavType.ReferenceType[bundle, key])
.isEqualTo(reference)
@@ -302,18 +372,48 @@
assertThat((NavType.IntArrayType as CollectionNavType).serializeAsValues(
intArrayOf(0, 1))
).containsExactly("0", "1").inOrder()
+ assertThat((NavType.IntListType as CollectionNavType).serializeAsValues(
+ listOf(0, 1))
+ ).containsExactly("0", "1").inOrder()
+ assertThat((NavType.IntListType as CollectionNavType).serializeAsValues(
+ arrayListOf(0, 1))
+ ).containsExactly("0", "1").inOrder()
assertThat((NavType.BoolArrayType as CollectionNavType).serializeAsValues(
booleanArrayOf(true, false))
).containsExactly("true", "false").inOrder()
+ assertThat((NavType.BoolListType as CollectionNavType).serializeAsValues(
+ listOf(true, false))
+ ).containsExactly("true", "false").inOrder()
+ assertThat((NavType.BoolListType as CollectionNavType).serializeAsValues(
+ arrayListOf(true, false))
+ ).containsExactly("true", "false").inOrder()
assertThat((NavType.StringArrayType as CollectionNavType).serializeAsValues(
arrayOf("test", "test2"))
).containsExactly("test", "test2").inOrder()
+ assertThat((NavType.StringListType as CollectionNavType).serializeAsValues(
+ listOf("test", "test2"))
+ ).containsExactly("test", "test2").inOrder()
+ assertThat((NavType.StringListType as CollectionNavType).serializeAsValues(
+ arrayListOf("test", "test2"))
+ ).containsExactly("test", "test2").inOrder()
assertThat((NavType.FloatArrayType as CollectionNavType).serializeAsValues(
floatArrayOf(1F, 2F))
).containsExactly("1.0", "2.0").inOrder()
+ assertThat((NavType.FloatListType as CollectionNavType).serializeAsValues(
+ listOf(1F, 2F))
+ ).containsExactly("1.0", "2.0").inOrder()
+ assertThat((NavType.FloatListType as CollectionNavType).serializeAsValues(
+ arrayListOf(1F, 2F))
+ ).containsExactly("1.0", "2.0").inOrder()
assertThat((NavType.LongArrayType as CollectionNavType).serializeAsValues(
longArrayOf(1L, 2L))
).containsExactly("1", "2").inOrder()
+ assertThat((NavType.LongListType as CollectionNavType).serializeAsValues(
+ listOf(1L, 2L))
+ ).containsExactly("1", "2").inOrder()
+ assertThat((NavType.LongListType as CollectionNavType).serializeAsValues(
+ arrayListOf(1L, 2L))
+ ).containsExactly("1", "2").inOrder()
}
@Test
diff --git a/navigation/navigation-common/src/androidTest/java/androidx/navigation/serialization/RouteFilledTest.kt b/navigation/navigation-common/src/androidTest/java/androidx/navigation/serialization/RouteFilledTest.kt
index 21f2e2e..9bb4d25 100644
--- a/navigation/navigation-common/src/androidTest/java/androidx/navigation/serialization/RouteFilledTest.kt
+++ b/navigation/navigation-common/src/androidTest/java/androidx/navigation/serialization/RouteFilledTest.kt
@@ -402,6 +402,23 @@
}
@Test
+ fun routeListArgs() {
+ @Serializable
+ @SerialName(PATH_SERIAL_NAME)
+ class IntList(val list: List<Int>)
+ assertThatRouteFilledFrom(
+ IntList(listOf(1, 2)),
+ listOf(
+ navArgument("list") {
+ type = NavType.IntListType
+ nullable = false
+ unknownDefaultValuePresent = false
+ }
+ )
+ ).isEqualTo("$PATH_SERIAL_NAME?list=1&list=2")
+ }
+
+ @Test
fun withSecondaryConstructor() {
@Serializable
@SerialName(PATH_SERIAL_NAME)
diff --git a/navigation/navigation-common/src/main/java/androidx/navigation/NavDeepLink.kt b/navigation/navigation-common/src/main/java/androidx/navigation/NavDeepLink.kt
index 66e79f8..bdc5f3d 100644
--- a/navigation/navigation-common/src/main/java/androidx/navigation/NavDeepLink.kt
+++ b/navigation/navigation-common/src/main/java/androidx/navigation/NavDeepLink.kt
@@ -469,7 +469,7 @@
* Set the uri pattern for the [NavDeepLink].
*
* Arguments extracted from destination [T] will be automatically appended to the base path
- * provided in [uriPattern].
+ * provided in [basePath].
*
* Arguments are appended based on property name and in the same order as their declaration
* order in [T]. They are appended as query parameters if the argument has either:
@@ -512,7 +512,7 @@
*
*
* @param T The destination's route from KClass
- * @param uriPattern The base path to append arguments onto
+ * @param basePath The base uri path to append arguments onto
* @param typeMap map of destination arguments' kotlin type [KType] to its respective custom
* [NavType]. May be empty if [T] does not use custom NavTypes.
*
@@ -520,18 +520,18 @@
*/
@ExperimentalSafeArgsApi
public inline fun <reified T : Any> setUriPattern(
- uriPattern: String,
+ basePath: String,
typeMap: Map<KType, @JvmSuppressWildcards NavType<*>> = emptyMap(),
- ): Builder = setUriPattern(uriPattern, T::class, typeMap)
+ ): Builder = setUriPattern(basePath, T::class, typeMap)
@OptIn(InternalSerializationApi::class)
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) // need to be public for reified delegation
public fun <T : Any> setUriPattern(
- uriPattern: String,
+ basePath: String,
route: KClass<T>,
typeMap: Map<KType, NavType<*>> = emptyMap(),
): Builder {
- this.uriPattern = route.serializer().generateRoutePattern(typeMap, uriPattern)
+ this.uriPattern = route.serializer().generateRoutePattern(typeMap, basePath)
return this
}
@@ -590,10 +590,10 @@
* Creates a [NavDeepLink.Builder] with a set uri pattern.
*
* Arguments extracted from destination [T] will be automatically appended to the
- * base path provided in [uriPattern]
+ * base path provided in [basePath]
*
* @param T The destination's route from KClass
- * @param uriPattern The base path to append arguments onto
+ * @param basePath The base uri path to append arguments onto
* @param typeMap map of destination arguments' kotlin type [KType] to its
* respective custom [NavType]. May be empty if [T] does not use custom NavTypes.
* @return a [Builder] instance
@@ -601,11 +601,11 @@
@JvmStatic
@ExperimentalSafeArgsApi
inline fun <reified T : Any> fromUriPattern(
- uriPattern: String,
+ basePath: String,
typeMap: Map<KType, @JvmSuppressWildcards NavType<*>> = emptyMap(),
): Builder {
val builder = Builder()
- builder.setUriPattern(uriPattern, T::class, typeMap)
+ builder.setUriPattern(basePath, T::class, typeMap)
return builder
}
diff --git a/navigation/navigation-common/src/main/java/androidx/navigation/NavType.kt b/navigation/navigation-common/src/main/java/androidx/navigation/NavType.kt
index 878d6a4..3f09a49 100644
--- a/navigation/navigation-common/src/main/java/androidx/navigation/NavType.kt
+++ b/navigation/navigation-common/src/main/java/androidx/navigation/NavType.kt
@@ -175,14 +175,19 @@
when {
IntType.name == type -> return IntType
IntArrayType.name == type -> return IntArrayType
+ IntListType.name == type -> return IntListType
LongType.name == type -> return LongType
LongArrayType.name == type -> return LongArrayType
+ LongListType.name == type -> return LongListType
BoolType.name == type -> return BoolType
BoolArrayType.name == type -> return BoolArrayType
+ BoolListType.name == type -> return BoolListType
StringType.name == type -> return StringType
StringArrayType.name == type -> return StringArrayType
+ StringListType.name == type -> return StringListType
FloatType.name == type -> return FloatType
FloatArrayType.name == type -> return FloatArrayType
+ FloatListType.name == type -> return FloatListType
ReferenceType.name == type -> return ReferenceType
!type.isNullOrEmpty() -> {
try {
@@ -411,6 +416,46 @@
}
/**
+ * NavType for storing list of Ints.
+ *
+ * Null values are supported.
+ * List NavTypes in Navigation XML files are not supported.
+ */
+ @JvmField
+ public val IntListType: NavType<List<Int>?> = object : CollectionNavType<List<Int>?>(
+ true
+ ) {
+ override val name: String
+ get() = "List<Int>"
+
+ override fun put(bundle: Bundle, key: String, value: List<Int>?) {
+ bundle.putIntArray(key, value?.toIntArray())
+ }
+
+ @Suppress("DEPRECATION")
+ override fun get(bundle: Bundle, key: String): List<Int>? {
+ return (bundle[key] as IntArray?)?.toList()
+ }
+
+ override fun parseValue(value: String): List<Int> {
+ return listOf(IntType.parseValue(value))
+ }
+
+ override fun parseValue(value: String, previousValue: List<Int>?): List<Int>? {
+ return previousValue?.plus(IntType.parseValue(value)) ?: parseValue(value)
+ }
+
+ override fun valueEquals(value: List<Int>?, other: List<Int>?): Boolean {
+ val valueArray = value?.toTypedArray()
+ val otherArray = other?.toTypedArray()
+ return valueArray.contentDeepEquals(otherArray)
+ }
+
+ override fun serializeAsValues(value: List<Int>?): List<String> =
+ value?.map { it.toString() } ?: emptyList()
+ }
+
+ /**
* NavType for storing long values,
* corresponding with the "long" type in a Navigation XML file.
*
@@ -490,6 +535,46 @@
}
/**
+ * NavType for storing list of Longs.
+ *
+ * Null values are supported.
+ * List NavTypes in Navigation XML files are not supported.
+ */
+ @JvmField
+ public val LongListType: NavType<List<Long>?> = object : CollectionNavType<List<Long>?>(
+ true
+ ) {
+ override val name: String
+ get() = "List<Long>"
+
+ override fun put(bundle: Bundle, key: String, value: List<Long>?) {
+ bundle.putLongArray(key, value?.toLongArray())
+ }
+
+ @Suppress("DEPRECATION")
+ override fun get(bundle: Bundle, key: String): List<Long>? {
+ return (bundle[key] as LongArray?)?.toList()
+ }
+
+ override fun parseValue(value: String): List<Long> {
+ return listOf(LongType.parseValue(value))
+ }
+
+ override fun parseValue(value: String, previousValue: List<Long>?): List<Long>? {
+ return previousValue?.plus(LongType.parseValue(value)) ?: parseValue(value)
+ }
+
+ override fun valueEquals(value: List<Long>?, other: List<Long>?): Boolean {
+ val valueArray = value?.toTypedArray()
+ val otherArray = other?.toTypedArray()
+ return valueArray.contentDeepEquals(otherArray)
+ }
+
+ override fun serializeAsValues(value: List<Long>?): List<String> =
+ value?.map { it.toString() } ?: emptyList()
+ }
+
+ /**
* NavType for storing float values,
* corresponding with the "float" type in a Navigation XML file.
*
@@ -556,6 +641,46 @@
}
/**
+ * NavType for storing list of Floats.
+ *
+ * Null values are supported.
+ * List NavTypes in Navigation XML files are not supported.
+ */
+ @JvmField
+ public val FloatListType: NavType<List<Float>?> = object : CollectionNavType<List<Float>?>(
+ true
+ ) {
+ override val name: String
+ get() = "List<Float>"
+
+ override fun put(bundle: Bundle, key: String, value: List<Float>?) {
+ bundle.putFloatArray(key, value?.toFloatArray())
+ }
+
+ @Suppress("DEPRECATION")
+ override fun get(bundle: Bundle, key: String): List<Float>? {
+ return (bundle[key] as FloatArray?)?.toList()
+ }
+
+ override fun parseValue(value: String): List<Float> {
+ return listOf(FloatType.parseValue(value))
+ }
+
+ override fun parseValue(value: String, previousValue: List<Float>?): List<Float>? {
+ return previousValue?.plus(FloatType.parseValue(value)) ?: parseValue(value)
+ }
+
+ override fun valueEquals(value: List<Float>?, other: List<Float>?): Boolean {
+ val valueArray = value?.toTypedArray()
+ val otherArray = other?.toTypedArray()
+ return valueArray.contentDeepEquals(otherArray)
+ }
+
+ override fun serializeAsValues(value: List<Float>?): List<String> =
+ value?.map { it.toString() } ?: emptyList()
+ }
+
+ /**
* NavType for storing boolean values,
* corresponding with the "boolean" type in a Navigation XML file.
*
@@ -629,6 +754,45 @@
}
/**
+ * NavType for storing list of Booleans.
+ *
+ * Null values are supported.
+ * List NavTypes in Navigation XML files are not supported.
+ */
+ @JvmField
+ public val BoolListType: NavType<List<Boolean>?> =
+ object : CollectionNavType<List<Boolean>?>(true) {
+ override val name: String
+ get() = "List<Boolean>"
+
+ override fun put(bundle: Bundle, key: String, value: List<Boolean>?) {
+ bundle.putBooleanArray(key, value?.toBooleanArray())
+ }
+
+ @Suppress("DEPRECATION")
+ override fun get(bundle: Bundle, key: String): List<Boolean>? {
+ return (bundle[key] as BooleanArray?)?.toList()
+ }
+
+ override fun parseValue(value: String): List<Boolean> {
+ return listOf(BoolType.parseValue(value))
+ }
+
+ override fun parseValue(value: String, previousValue: List<Boolean>?): List<Boolean>? {
+ return previousValue?.plus(BoolType.parseValue(value)) ?: parseValue(value)
+ }
+
+ override fun valueEquals(value: List<Boolean>?, other: List<Boolean>?): Boolean {
+ val valueArray = value?.toTypedArray()
+ val otherArray = other?.toTypedArray()
+ return valueArray.contentDeepEquals(otherArray)
+ }
+
+ override fun serializeAsValues(value: List<Boolean>?): List<String> =
+ value?.map { it.toString() } ?: emptyList()
+ }
+
+ /**
* NavType for storing String values,
* corresponding with the "string" type in a Navigation XML file.
*
@@ -704,8 +868,50 @@
value.contentDeepEquals(other)
override fun serializeAsValues(value: Array<String>?): List<String> =
- value?.toList() ?: emptyList()
+ value?.map { Uri.encode(it) } ?: emptyList()
}
+
+ /**
+ * NavType for storing list of Strings.
+ *
+ * Null values are supported.
+ * List NavTypes in Navigation XML files are not supported.
+ */
+ @JvmField
+ public val StringListType: NavType<List<String>?> =
+ object : CollectionNavType<List<String>?>(true) {
+ override val name: String
+ get() = "List<String>"
+
+ override fun put(bundle: Bundle, key: String, value: List<String>?) {
+ bundle.putStringArray(key, value?.toTypedArray())
+ }
+
+ @Suppress("UNCHECKED_CAST", "DEPRECATION")
+ override fun get(bundle: Bundle, key: String): List<String>? {
+ return (bundle[key] as Array<String>?)?.toList()
+ }
+
+ override fun parseValue(value: String): List<String> {
+ return listOf(value)
+ }
+
+ override fun parseValue(
+ value: String,
+ previousValue: List<String>?
+ ): List<String>? {
+ return previousValue?.plus(value) ?: parseValue(value)
+ }
+
+ override fun valueEquals(value: List<String>?, other: List<String>?): Boolean {
+ val valueArray = value?.toTypedArray()
+ val otherArray = other?.toTypedArray()
+ return valueArray.contentDeepEquals(otherArray)
+ }
+
+ override fun serializeAsValues(value: List<String>?): List<String> =
+ value?.map { Uri.encode(it) } ?: emptyList()
+ }
}
/**
diff --git a/navigation/navigation-common/src/main/java/androidx/navigation/serialization/NavTypeConverter.kt b/navigation/navigation-common/src/main/java/androidx/navigation/serialization/NavTypeConverter.kt
index 37f1c75..7f7b754 100644
--- a/navigation/navigation-common/src/main/java/androidx/navigation/serialization/NavTypeConverter.kt
+++ b/navigation/navigation-common/src/main/java/androidx/navigation/serialization/NavTypeConverter.kt
@@ -39,7 +39,7 @@
FLOAT_ARRAY,
LONG_ARRAY,
ARRAY,
- ARRAY_LIST,
+ LIST,
UNKNOWN
}
@@ -66,6 +66,17 @@
val typeParameter = getElementDescriptor(0).toInternalType()
if (typeParameter == InternalType.STRING) NavType.StringArrayType else UNKNOWN
}
+ InternalType.LIST -> {
+ val typeParameter = getElementDescriptor(0).toInternalType()
+ when (typeParameter) {
+ InternalType.INT -> NavType.IntListType
+ InternalType.BOOL -> NavType.BoolListType
+ InternalType.FLOAT -> NavType.FloatListType
+ InternalType.LONG -> NavType.LongListType
+ InternalType.STRING -> NavType.StringListType
+ else -> UNKNOWN
+ }
+ }
else -> UNKNOWN
}
return type
@@ -91,7 +102,7 @@
serialName == "kotlin.LongArray" -> InternalType.LONG_ARRAY
serialName == "kotlin.Array" -> InternalType.ARRAY
// serial name for both List and ArrayList
- serialName.startsWith("kotlin.collections.ArrayList") -> InternalType.ARRAY_LIST
+ serialName.startsWith("kotlin.collections.ArrayList") -> InternalType.LIST
// custom classes or other types without built-in NavTypes
else -> InternalType.UNKNOWN
}
diff --git a/navigation/navigation-common/src/test/java/androidx/navigation/serialization/NavArgumentGeneratorTest.kt b/navigation/navigation-common/src/test/java/androidx/navigation/serialization/NavArgumentGeneratorTest.kt
index 391d320..c0c9214 100644
--- a/navigation/navigation-common/src/test/java/androidx/navigation/serialization/NavArgumentGeneratorTest.kt
+++ b/navigation/navigation-common/src/test/java/androidx/navigation/serialization/NavArgumentGeneratorTest.kt
@@ -183,6 +183,45 @@
}
@Test
+ fun convertToIntList() {
+ @Serializable class TestClass(val arg: List<Int>)
+
+ val converted = serializer<TestClass>().generateNavArguments()
+ val expected = navArgument("arg") {
+ type = NavType.IntListType
+ nullable = false
+ }
+ assertThat(converted).containsExactlyInOrder(expected)
+ assertThat(converted[0].argument.isDefaultValueUnknown).isFalse()
+ }
+
+ @Test
+ fun convertArrayListToIntList() {
+ @Serializable class TestClass(val arg: ArrayList<Int>)
+
+ val converted = serializer<TestClass>().generateNavArguments()
+ val expected = navArgument("arg") {
+ type = NavType.IntListType
+ nullable = false
+ }
+ assertThat(converted).containsExactlyInOrder(expected)
+ assertThat(converted[0].argument.isDefaultValueUnknown).isFalse()
+ }
+
+ @Test
+ fun convertToIntListNullable() {
+ @Serializable class TestClass(val arg: List<Int>?)
+
+ val converted = serializer<TestClass>().generateNavArguments()
+ val expected = navArgument("arg") {
+ type = NavType.IntListType
+ nullable = true
+ }
+ assertThat(converted).containsExactlyInOrder(expected)
+ assertThat(converted[0].argument.isDefaultValueUnknown).isFalse()
+ }
+
+ @Test
fun convertToLongArray() {
@Serializable class TestClass(val arg: LongArray)
@@ -209,6 +248,45 @@
}
@Test
+ fun convertToLongList() {
+ @Serializable class TestClass(val arg: List<Long>)
+
+ val converted = serializer<TestClass>().generateNavArguments()
+ val expected = navArgument("arg") {
+ type = NavType.LongListType
+ nullable = false
+ }
+ assertThat(converted).containsExactlyInOrder(expected)
+ assertThat(converted[0].argument.isDefaultValueUnknown).isFalse()
+ }
+
+ @Test
+ fun convertArrayListToLongList() {
+ @Serializable class TestClass(val arg: ArrayList<Long>)
+
+ val converted = serializer<TestClass>().generateNavArguments()
+ val expected = navArgument("arg") {
+ type = NavType.LongListType
+ nullable = false
+ }
+ assertThat(converted).containsExactlyInOrder(expected)
+ assertThat(converted[0].argument.isDefaultValueUnknown).isFalse()
+ }
+
+ @Test
+ fun convertToLongListNullable() {
+ @Serializable class TestClass(val arg: List<Long>?)
+
+ val converted = serializer<TestClass>().generateNavArguments()
+ val expected = navArgument("arg") {
+ type = NavType.LongListType
+ nullable = true
+ }
+ assertThat(converted).containsExactlyInOrder(expected)
+ assertThat(converted[0].argument.isDefaultValueUnknown).isFalse()
+ }
+
+ @Test
fun convertToFloatArray() {
@Serializable class TestClass(val arg: FloatArray)
@@ -235,6 +313,45 @@
}
@Test
+ fun convertToFloatList() {
+ @Serializable class TestClass(val arg: List<Float>)
+
+ val converted = serializer<TestClass>().generateNavArguments()
+ val expected = navArgument("arg") {
+ type = NavType.FloatListType
+ nullable = false
+ }
+ assertThat(converted).containsExactlyInOrder(expected)
+ assertThat(converted[0].argument.isDefaultValueUnknown).isFalse()
+ }
+
+ @Test
+ fun convertArrayListToFloatList() {
+ @Serializable class TestClass(val arg: ArrayList<Float>)
+
+ val converted = serializer<TestClass>().generateNavArguments()
+ val expected = navArgument("arg") {
+ type = NavType.FloatListType
+ nullable = false
+ }
+ assertThat(converted).containsExactlyInOrder(expected)
+ assertThat(converted[0].argument.isDefaultValueUnknown).isFalse()
+ }
+
+ @Test
+ fun convertToFloatListNullable() {
+ @Serializable class TestClass(val arg: List<Float>?)
+
+ val converted = serializer<TestClass>().generateNavArguments()
+ val expected = navArgument("arg") {
+ type = NavType.FloatListType
+ nullable = true
+ }
+ assertThat(converted).containsExactlyInOrder(expected)
+ assertThat(converted[0].argument.isDefaultValueUnknown).isFalse()
+ }
+
+ @Test
fun convertToBoolArray() {
@Serializable class TestClass(val arg: BooleanArray)
@@ -261,6 +378,45 @@
}
@Test
+ fun convertToBooleanList() {
+ @Serializable class TestClass(val arg: List<Boolean>)
+
+ val converted = serializer<TestClass>().generateNavArguments()
+ val expected = navArgument("arg") {
+ type = NavType.BoolListType
+ nullable = false
+ }
+ assertThat(converted).containsExactlyInOrder(expected)
+ assertThat(converted[0].argument.isDefaultValueUnknown).isFalse()
+ }
+
+ @Test
+ fun convertArrayListToBooleanList() {
+ @Serializable class TestClass(val arg: ArrayList<Boolean>)
+
+ val converted = serializer<TestClass>().generateNavArguments()
+ val expected = navArgument("arg") {
+ type = NavType.BoolListType
+ nullable = false
+ }
+ assertThat(converted).containsExactlyInOrder(expected)
+ assertThat(converted[0].argument.isDefaultValueUnknown).isFalse()
+ }
+
+ @Test
+ fun convertToBooleanListNullable() {
+ @Serializable class TestClass(val arg: List<Boolean>?)
+
+ val converted = serializer<TestClass>().generateNavArguments()
+ val expected = navArgument("arg") {
+ type = NavType.BoolListType
+ nullable = true
+ }
+ assertThat(converted).containsExactlyInOrder(expected)
+ assertThat(converted[0].argument.isDefaultValueUnknown).isFalse()
+ }
+
+ @Test
fun convertToStringArray() {
@Serializable class TestClass(val arg: Array<String>)
@@ -287,6 +443,45 @@
}
@Test
+ fun convertToStringList() {
+ @Serializable class TestClass(val arg: List<String>)
+
+ val converted = serializer<TestClass>().generateNavArguments()
+ val expected = navArgument("arg") {
+ type = NavType.StringListType
+ nullable = false
+ }
+ assertThat(converted).containsExactlyInOrder(expected)
+ assertThat(converted[0].argument.isDefaultValueUnknown).isFalse()
+ }
+
+ @Test
+ fun convertArrayListToStringList() {
+ @Serializable class TestClass(val arg: ArrayList<String>)
+
+ val converted = serializer<TestClass>().generateNavArguments()
+ val expected = navArgument("arg") {
+ type = NavType.StringListType
+ nullable = false
+ }
+ assertThat(converted).containsExactlyInOrder(expected)
+ assertThat(converted[0].argument.isDefaultValueUnknown).isFalse()
+ }
+
+ @Test
+ fun convertToStringListNullable() {
+ @Serializable class TestClass(val arg: List<String>?)
+
+ val converted = serializer<TestClass>().generateNavArguments()
+ val expected = navArgument("arg") {
+ type = NavType.StringListType
+ nullable = true
+ }
+ assertThat(converted).containsExactlyInOrder(expected)
+ assertThat(converted[0].argument.isDefaultValueUnknown).isFalse()
+ }
+
+ @Test
fun convertToParcelable() {
@Serializable
class TestParcelable : Parcelable {
@@ -599,14 +794,14 @@
@Test
fun convertIllegalCustomType() {
- @Serializable class TestClass(val arg: ArrayList<String>)
+ @Serializable class TestClass(val arg: Set<String>)
val exception = assertFailsWith<IllegalArgumentException> {
serializer<TestClass>().generateNavArguments()
}
assertThat(exception.message).isEqualTo(
- "Cannot cast arg of type kotlin.collections.ArrayList to a NavType. " +
+ "Cannot cast arg of type kotlin.collections.LinkedHashSet to a NavType. " +
"Make sure to provide custom NavType for this argument."
)
}
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerRouteTest.kt b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerRouteTest.kt
index e7de37a..31d714e 100644
--- a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerRouteTest.kt
+++ b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerRouteTest.kt
@@ -3390,6 +3390,27 @@
@UiThreadTest
@Test
+ fun testNavigateWithObjectListArg() {
+ @Serializable
+ class TestClass(val arg: MutableList<Boolean>)
+
+ val navController = createNavController()
+ navController.graph = navController.createGraph(
+ startDestination = TestClass(mutableListOf(true, false, true))
+ ) {
+ test<TestClass>()
+ }
+ assertThat(navController.currentDestination?.route).isEqualTo(
+ "androidx.navigation.NavControllerRouteTest.testNavigateWithObjectListArg" +
+ ".TestClass?arg={arg}"
+ )
+ val route = navController.currentBackStackEntry?.toRoute<TestClass>()
+ assertThat(route?.arg is MutableList).isTrue()
+ assertThat(route?.arg).containsExactly(true, false, true).inOrder()
+ }
+
+ @UiThreadTest
+ @Test
fun testDeepLinkFromNavGraph() {
val navController = createNavController()
navController.graph = nav_simple_route_graph
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 11d3a42..192a70e 100644
--- a/pdf/pdf-viewer/src/main/res/values-be/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-be/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"Тып файла"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"Гэты файл абаронены"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"Пароль"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"Няправільны пароль"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"Скасаваць"</string>
+ <string name="button_open" msgid="5445014062438566842">"Адкрыць"</string>
+ <string name="desc_password" msgid="6636473611443746739">"поле ўводу пароля"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"няправільны пароль"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"няправільны пароль"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> з <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"старонка <xliff:g id="PAGE">%1$d</xliff:g> з <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"маштаб <xliff:g id="FIRST">%1$d</xliff:g>%%"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"На старонку <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
<string name="desc_page" msgid="5684226167093594168">"старонка <xliff:g id="PAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"Выберыце тэкст, каб змясціць свой каментарый"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"Націсніце там, дзе будзеце каментаваць"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"Скасаваць"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"Не ўдаецца паказаць PDF-файл \"<xliff:g id="TITLE">%1$s</xliff:g>\" (няправільны фармат)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"Немагчыма адлюстраваць старонку <xliff:g id="PAGE">%1$d</xliff:g> (памылка файла)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"Не ўдаецца загрузіць рэжым анатацый для гэтага элемента."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"Спасылка: вэб-старонка <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g>"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"Спасылка: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"Адрас электроннай пошты: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"Тэлефон: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"пачатак вылучэння"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"канец вылучэння"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g> з <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 b1af046..71a21a7 100644
--- a/pdf/pdf-viewer/src/main/res/values-bg/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-bg/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"Файлов тип"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"Този файл е защитен"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"Парола"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"Неправилна парола"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"Отказ"</string>
+ <string name="button_open" msgid="5445014062438566842">"Отваряне"</string>
+ <string name="desc_password" msgid="6636473611443746739">"поле за парола"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"неправилна парола"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"неправилна парола"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"страница <xliff:g id="PAGE">%1$d</xliff:g> от <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"Процент на промяна на мащаба: <xliff:g id="FIRST">%1$d</xliff:g>"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"Към страница <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
<string name="desc_page" msgid="5684226167093594168">"страница <xliff:g id="PAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"Изберете текст, за да поставите коментара си"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"Докоснете област, която да коментирате"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"Отказ"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"PDF файлът <xliff:g id="TITLE">%1$s</xliff:g> не може да се покаже (форматът е невалиден)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"Страница <xliff:g id="PAGE">%1$d</xliff:g> не може да се покаже (грешка във файла)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"Режимът за пояснения не може да се зареди за този елемент."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"Връзка: уеб страница от <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g>"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"Връзка: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"Имейл адрес: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"Телефон: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"начало на избраното"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"край на избраното"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g> – <xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 f4df82a..ec56333 100644
--- a/pdf/pdf-viewer/src/main/res/values-bn/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-bn/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"ফাইলের ধরন"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"এই ফাইল সুরক্ষিত আছে"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"পাসওয়ার্ড"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"পাসওয়ার্ড সঠিক নয়"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"বাতিল করুন"</string>
+ <string name="button_open" msgid="5445014062438566842">"খুলুন"</string>
+ <string name="desc_password" msgid="6636473611443746739">"পাসওয়ার্ড ফিল্ড"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"পাসওয়ার্ড সঠিক নয়"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"পাসওয়ার্ড সঠিক নয়"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"<xliff:g id="TOTAL">%2$d</xliff:g>টির মধ্যে <xliff:g id="PAGE">%1$d</xliff:g> নম্বর পৃষ্ঠা"</string>
<string name="desc_zoom" msgid="7318480946145947242">"<xliff:g id="FIRST">%1$d</xliff:g> শতাংশ জুম করুন"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"<xliff:g id="PAGE_NUMBER">%1$d</xliff:g> নম্বর পৃষ্ঠায় যান"</string>
<string name="desc_page" msgid="5684226167093594168">"পৃষ্ঠা <xliff:g id="PAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"আপনার কমেন্ট করতে টেক্সট বেছে নিন"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"কমেন্ট করতে কোনও একটি অংশে ট্যাপ করুন"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"বাতিল করুন"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"পিডিএফ দেখানো যাবে না (<xliff:g id="TITLE">%1$s</xliff:g> ভুল ফর্ম্যাটে আছে)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"<xliff:g id="PAGE">%1$d</xliff:g> পৃষ্ঠা দেখানো যাবে না (ফাইলে সমস্যা)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"এই আইটেমের জন্য অ্যানোটেশন মোড লোড করা যায়নি।"</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"লিঙ্ক: <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g>-এ ওয়েবপেজ"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"লিঙ্ক: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"ইমেল: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"ফোন: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"বেছে নেওয়া টেক্সটের শুরু"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"বেছে নেওয়া টেক্সটের শেষ"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 cc94c57..6f0cfa1 100644
--- a/pdf/pdf-viewer/src/main/res/values-bs/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-bs/strings.xml
@@ -18,29 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"Vrsta fajla"</string>
- <string name="title_dialog_password" msgid="2018068413709926925">"Datoteka je zaštićena"</string>
- <string name="label_password_first" msgid="4456258714097111908">"Zaporka"</string>
- <string name="label_password_incorrect" msgid="8449142641187704667">"Zaporka nije točna"</string>
- <string name="button_cancel" msgid="5855794576957267334">"Odustani"</string>
+ <string name="title_dialog_password" msgid="2018068413709926925">"Fajl je zaštićen"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"Lozinka"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"Pogrešna lozinka"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"Otkaži"</string>
<string name="button_open" msgid="5445014062438566842">"Otvori"</string>
- <string name="desc_password" msgid="6636473611443746739">"polje zaporke"</string>
- <string name="desc_password_incorrect" msgid="4718823067483963845">"zaporka nije točna"</string>
- <string name="desc_password_incorrect_message" msgid="4849541619951023025">"zaporka nije točna"</string>
+ <string name="desc_password" msgid="6636473611443746739">"polje za lozinku"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"pogrešna lozinka"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"pogrešna lozinka"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"<xliff:g id="PAGE">%1$d</xliff:g>. stranica od <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"zumiranje <xliff:g id="FIRST">%1$d</xliff:g> posto"</string>
- <string name="desc_goto_link" msgid="2461368384824849714">"Idite na stranicu <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
+ <string name="desc_goto_link" msgid="2461368384824849714">"Odlazak na <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>. stranicu"</string>
<string name="desc_page" msgid="5684226167093594168">"<xliff:g id="PAGE">%1$d</xliff:g>. stranica"</string>
- <string name="message_select_text_to_comment" msgid="5725327644007067522">"Odaberite tekst za postavljanje komentara"</string>
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"Odaberite tekst da unesete komentar"</string>
<string name="message_tap_to_comment" msgid="7820801719181709999">"Dodirnite područje koje ćete komentirati"</string>
- <string name="action_cancel" msgid="5494417739210197522">"Odustani"</string>
- <string name="error_file_format_pdf" msgid="7567006188638831878">"PDF se ne može prikazati (format <xliff:g id="TITLE">%1$s</xliff:g> nije važeći)"</string>
- <string name="error_on_page" msgid="1592475819957182385">"Stranica <xliff:g id="PAGE">%1$d</xliff:g> ne može se prikazati (pogreška datoteke)"</string>
- <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"Način za napomene za tu stavku ne može se učitati."</string>
- <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"Veza: web-stranica na domeni <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g>"</string>
- <string name="desc_web_link" msgid="2776023299237058419">"Veza: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
- <string name="desc_email_link" msgid="7027325672358507448">"E-adresa: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
- <string name="desc_phone_link" msgid="4986414958429253135">"Telefonski broj: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"Otkaži"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"Nije moguće prikazati PDF (fajl <xliff:g id="TITLE">%1$s</xliff:g> ima nevažeći format)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"Nije moguće prikazati <xliff:g id="PAGE">%1$d</xliff:g>. stranicu (greška fajla)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"Nije moguće učitati način rada za bilješke za ovu stavku."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"Link: web stranica na <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g>"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"Link: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"Adresa e-pošte: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"Broj telefona: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"početak odabira"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"kraj odabira"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>.–<xliff:g id="LAST">%2$d</xliff:g>. / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 8318f9e..0c26917 100644
--- a/pdf/pdf-viewer/src/main/res/values-ca/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ca/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"Tipus de fitxer"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"Aquest fitxer està protegit"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"Contrasenya"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"Contrasenya incorrecta"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"Cancel·la"</string>
+ <string name="button_open" msgid="5445014062438566842">"Obre"</string>
+ <string name="desc_password" msgid="6636473611443746739">"camp de contrasenya"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"contrasenya incorrecta"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"contrasenya incorrecta"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"pàgina <xliff:g id="PAGE">%1$d</xliff:g> de <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"zoom <xliff:g id="FIRST">%1$d</xliff:g> per cent"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"Ves a la pàgina <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
<string name="desc_page" msgid="5684226167093594168">"pàgina <xliff:g id="PAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"Selecciona text per escriure un comentari"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"Toca una zona per comentar-la"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"Cancel·la"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"No es pot mostrar el PDF (<xliff:g id="TITLE">%1$s</xliff:g> no és un format vàlid)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"No es pot mostrar la pàgina <xliff:g id="PAGE">%1$d</xliff:g> (error del fitxer)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"No es pot carregar el mode d\'anotació per a aquest element."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"Enllaç: pàgina web a <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g>"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"Enllaç: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"Adreça electrònica: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"Telèfon: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"inici de la selecció"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"final de la selecció"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 2bb8adc..baf857b 100644
--- a/pdf/pdf-viewer/src/main/res/values-cs/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-cs/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"Typ souboru"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"Tento soubor je chráněn"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"Heslo"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"Nesprávné heslo"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"Zrušit"</string>
+ <string name="button_open" msgid="5445014062438566842">"Otevřít"</string>
+ <string name="desc_password" msgid="6636473611443746739">"pole hesla"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"nesprávné heslo"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"nesprávné heslo"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"stránka <xliff:g id="PAGE">%1$d</xliff:g> z <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"lupa <xliff:g id="FIRST">%1$d</xliff:g> procent"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"Přejít na stránku <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
<string name="desc_page" msgid="5684226167093594168">"stránka <xliff:g id="PAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"Vyberte text k umístění komentáře"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"Klepněte na oblast k okomentování"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"Zrušit"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"PDF nelze zobrazit (<xliff:g id="TITLE">%1$s</xliff:g> má neplatný formát)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"Stránku <xliff:g id="PAGE">%1$d</xliff:g> nelze zobrazit (chyba souboru)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"Pro tuto položku nelze načíst režim poznámek."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"Odkaz: stránka na webu <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g>"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"Odkaz: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"E‑mail: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"Telefon: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"začátek výběru"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"konec výběru"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>–<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 fea00587..d4aa7e3 100644
--- a/pdf/pdf-viewer/src/main/res/values-da/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-da/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"Filtype"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"Denne fil er beskyttet"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"Adgangskode"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"Adgangskoden er forkert"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"Annuller"</string>
+ <string name="button_open" msgid="5445014062438566842">"Åbn"</string>
+ <string name="desc_password" msgid="6636473611443746739">"felt til adgangskode"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"adgangskoden er forkert"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"adgangskoden er forkert"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"side <xliff:g id="PAGE">%1$d</xliff:g> af <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"zoom <xliff:g id="FIRST">%1$d</xliff:g> %%"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"Gå til side <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
<string name="desc_page" msgid="5684226167093594168">"side <xliff:g id="PAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"Markér tekst for at indsætte din kommentar"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"Tryk på et område, du vil kommentere"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"Annuller"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"PDF kan ikke vises (<xliff:g id="TITLE">%1$s</xliff:g> er i et ugyldigt format)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"Side <xliff:g id="PAGE">%1$d</xliff:g> kan ikke vises (filfejl)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"Annoteringstilstanden for dette element kan ikke indlæses."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"Link: webside på <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g>"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"Link: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"Mailadresse: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"Telefonnummer: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"start på markering"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"slut på markering"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 fcfc0f1..b341d91 100644
--- a/pdf/pdf-viewer/src/main/res/values-de/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-de/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"Dateityp"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"Diese Datei ist geschützt"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"Passwort"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"Falsches Passwort"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"Abbrechen"</string>
+ <string name="button_open" msgid="5445014062438566842">"Öffnen"</string>
+ <string name="desc_password" msgid="6636473611443746739">"Passwortfeld"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"Falsches Passwort"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"Falsches Passwort"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"Seite <xliff:g id="PAGE">%1$d</xliff:g> von <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"Zoom: <xliff:g id="FIRST">%1$d</xliff:g> %%"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"Gehe zu Seite <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
<string name="desc_page" msgid="5684226167093594168">"Seite <xliff:g id="PAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"Zum Einfügen des Kommentars Text auswählen"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"Auf Bereich tippen, um zu kommentieren"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"Abbrechen"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"Anzeige von PDF nicht möglich („<xliff:g id="TITLE">%1$s</xliff:g>“ hat ungültiges Dateiformat)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"Anzeige von Seite <xliff:g id="PAGE">%1$d</xliff:g> nicht möglich (Dateifehler)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"Anmerkungsmodus für dieses Element kann nicht geladen werden."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"Link: Webseite unter <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g>"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"Link: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"E-Mail-Adresse: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"Telefonnummer: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"Beginn der Auswahl"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"Ende der Auswahl"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>–<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 20a3d0d..14926d4 100644
--- a/pdf/pdf-viewer/src/main/res/values-el/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-el/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"Τύπος αρχείου"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"Αυτό το αρχείο είναι προστατευμένο"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"Κωδικός πρόσβασης"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"Εσφαλμένος κωδικός πρόσβασης"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"Ακύρωση"</string>
+ <string name="button_open" msgid="5445014062438566842">"Άνοιγμα"</string>
+ <string name="desc_password" msgid="6636473611443746739">"πεδίο κωδικού πρόσβασης"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"εσφαλμένος κωδικός πρόσβασης"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"εσφαλμένος κωδικός πρόσβασης"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"σελίδα <xliff:g id="PAGE">%1$d</xliff:g> από <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"εστίαση <xliff:g id="FIRST">%1$d</xliff:g> τοις εκατό"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"Μετάβαση στη σελίδα <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
<string name="desc_page" msgid="5684226167093594168">"σελίδα <xliff:g id="PAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"Επιλέξτε κείμενο για να τοποθετήσετε σχόλιο"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"Πατήστε μια περιοχή για να σχολιάσετε"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"Ακύρωση"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"Δεν είναι δυνατή η προβολή του PDF (μη έγκυρη μορφή του <xliff:g id="TITLE">%1$s</xliff:g>)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"Δεν είναι δυνατή η προβολή της σελίδας <xliff:g id="PAGE">%1$d</xliff:g> (σφάλμα αρχείου)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"Δεν είναι δυνατή η φόρτωση της λειτουργίας σχολιασμού για αυτό το στοιχείο."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"Σύνδεσμος: ιστοσελίδα στη διεύθυνση <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g>"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"Σύνδεσμος: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"Διεύθυνση ηλεκτρονικού ταχυδρομείου: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"Τηλέφωνο: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"αρχή επιλογής"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"τέλος επιλογής"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 abd87b6..dcb8ff3 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
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"File type"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"This file is protected"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"Password"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"Password incorrect"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"Cancel"</string>
+ <string name="button_open" msgid="5445014062438566842">"Open"</string>
+ <string name="desc_password" msgid="6636473611443746739">"password field"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"password incorrect"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"password incorrect"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"page <xliff:g id="PAGE">%1$d</xliff:g> of <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"zoom <xliff:g id="FIRST">%1$d</xliff:g> per cent"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"Go to page <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
<string name="desc_page" msgid="5684226167093594168">"page <xliff:g id="PAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"Select text to place your comment"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"Tap an area to comment on it"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"Cancel"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"Cannot display PDF (<xliff:g id="TITLE">%1$s</xliff:g> is of invalid format)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"Cannot display page <xliff:g id="PAGE">%1$d</xliff:g> (file error)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"Can\'t load annotation mode for this item."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"Link: web page at <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g>"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"Link: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"Email: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"Phone: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"selection start"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"selection end"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>–<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 abd87b6..dcb8ff3 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
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"File type"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"This file is protected"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"Password"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"Password incorrect"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"Cancel"</string>
+ <string name="button_open" msgid="5445014062438566842">"Open"</string>
+ <string name="desc_password" msgid="6636473611443746739">"password field"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"password incorrect"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"password incorrect"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"page <xliff:g id="PAGE">%1$d</xliff:g> of <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"zoom <xliff:g id="FIRST">%1$d</xliff:g> per cent"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"Go to page <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
<string name="desc_page" msgid="5684226167093594168">"page <xliff:g id="PAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"Select text to place your comment"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"Tap an area to comment on it"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"Cancel"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"Cannot display PDF (<xliff:g id="TITLE">%1$s</xliff:g> is of invalid format)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"Cannot display page <xliff:g id="PAGE">%1$d</xliff:g> (file error)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"Can\'t load annotation mode for this item."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"Link: web page at <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g>"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"Link: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"Email: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"Phone: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"selection start"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"selection end"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>–<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 abd87b6..dcb8ff3 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
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"File type"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"This file is protected"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"Password"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"Password incorrect"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"Cancel"</string>
+ <string name="button_open" msgid="5445014062438566842">"Open"</string>
+ <string name="desc_password" msgid="6636473611443746739">"password field"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"password incorrect"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"password incorrect"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"page <xliff:g id="PAGE">%1$d</xliff:g> of <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"zoom <xliff:g id="FIRST">%1$d</xliff:g> per cent"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"Go to page <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
<string name="desc_page" msgid="5684226167093594168">"page <xliff:g id="PAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"Select text to place your comment"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"Tap an area to comment on it"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"Cancel"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"Cannot display PDF (<xliff:g id="TITLE">%1$s</xliff:g> is of invalid format)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"Cannot display page <xliff:g id="PAGE">%1$d</xliff:g> (file error)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"Can\'t load annotation mode for this item."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"Link: web page at <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g>"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"Link: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"Email: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"Phone: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"selection start"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"selection end"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>–<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 4bca430..0db6b02 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
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"Tipo de archivo"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"Este archivo está protegido"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"Contraseña"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"La contraseña es incorrecta"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"Cancelar"</string>
+ <string name="button_open" msgid="5445014062438566842">"Abrir"</string>
+ <string name="desc_password" msgid="6636473611443746739">"campo de contraseña"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"contraseña incorrecta"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"contraseña incorrecta"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> de <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"página <xliff:g id="PAGE">%1$d</xliff:g> de <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"zoom <xliff:g id="FIRST">%1$d</xliff:g> por ciento"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"Ir a la página <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
<string name="desc_page" msgid="5684226167093594168">"página <xliff:g id="PAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"Selecciona texto para colocar tu comentario"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"Presiona un área para comentar"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"Cancelar"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"No se puede mostrar el PDF (<xliff:g id="TITLE">%1$s</xliff:g> tiene un formato no válido)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"No se puede mostrar la página <xliff:g id="PAGE">%1$d</xliff:g> (error de archivo)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"No se puede cargar el modo de anotación para este elemento."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"Vínculo: página web en <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g>"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"Vínculo: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"Correo electrónico: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"Teléfono: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"inicio de la selección"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"fin de la selección"</string>
<string name="label_page_range" msgid="8290964180158460076">"<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>
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 4f07b38..a416a41 100644
--- a/pdf/pdf-viewer/src/main/res/values-es/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-es/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"Tipo de archivo"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"Este archivo está protegido"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"Contraseña"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"Contraseña incorrecta"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"Cancelar"</string>
+ <string name="button_open" msgid="5445014062438566842">"Abrir"</string>
+ <string name="desc_password" msgid="6636473611443746739">"campo de contraseña"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"contraseña incorrecta"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"contraseña incorrecta"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"página <xliff:g id="PAGE">%1$d</xliff:g> de <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"zoom <xliff:g id="FIRST">%1$d</xliff:g> por ciento"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"Ir a la página <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
<string name="desc_page" msgid="5684226167093594168">"página <xliff:g id="PAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"Selecciona el texto para añadir tu comentario"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"Toca una zona para dejar un comentario"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"Cancelar"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"No se puede mostrar el PDF (el formato de <xliff:g id="TITLE">%1$s</xliff:g> no es válido)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"No se puede mostrar la página <xliff:g id="PAGE">%1$d</xliff:g> (error de archivo)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"No se puede cargar el modo de anotación en este elemento."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"Enlace: página web en <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g>"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"Enlace: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"Correo: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"Teléfono: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"inicio de la selección"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"final de la selección"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 18e68f0..88b7755 100644
--- a/pdf/pdf-viewer/src/main/res/values-et/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-et/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"Faili tüüp"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"Fail on kaitstud"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"Parool"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"Parool on vale"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"Tühista"</string>
+ <string name="button_open" msgid="5445014062438566842">"Ava"</string>
+ <string name="desc_password" msgid="6636473611443746739">"parooliväli"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"parool on vale"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"parool on vale"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"lk <xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"suum <xliff:g id="FIRST">%1$d</xliff:g> protsenti"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"Mine lehele <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
<string name="desc_page" msgid="5684226167093594168">"lk <xliff:g id="PAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"Valige tekst, millele kommentaar lisada"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"Puudutage ala, millele kommentaar lisada"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"Tühista"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"PDF-i ei saa kuvada (<xliff:g id="TITLE">%1$s</xliff:g> on vales vormingus)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"Lehekülge <xliff:g id="PAGE">%1$d</xliff:g> ei saa kuvada (viga failis)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"Selle üksuse jaoks ei saa märkuste lisamise režiimi laadida."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"Link: veebileht aadressil <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g>"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"Link: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"E-posti aadress: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"Telefon: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"valiku algus"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"valiku lõpp"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>–<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 dd0b571..1f0b66d 100644
--- a/pdf/pdf-viewer/src/main/res/values-eu/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-eu/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"Fitxategi mota"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"Fitxategi hau babestuta dago"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"Pasahitza"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"Pasahitza okerra da"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"Utzi"</string>
+ <string name="button_open" msgid="5445014062438566842">"Ireki"</string>
+ <string name="desc_password" msgid="6636473611443746739">"pasahitzaren eremua"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"pasahitza okerra da"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"pasahitza okerra da"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"<xliff:g id="TOTAL">%2$d</xliff:g> orritatik <xliff:g id="PAGE">%1$d</xliff:g>garrena"</string>
<string name="desc_zoom" msgid="7318480946145947242">"zooma ehuneko <xliff:g id="FIRST">%1$d</xliff:g>"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"Joan <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>. orrira"</string>
<string name="desc_page" msgid="5684226167093594168">"<xliff:g id="PAGE">%1$d</xliff:g>garren orria"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"Iruzkina egiteko, hautatu testua"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"Sakatu iruzkindu beharreko eremua"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"Utzi"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"Ezin da erakutsi PDFa (<xliff:g id="TITLE">%1$s</xliff:g> fitxategiaren formatuak ez du balio)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"Ezin da erakutsi <xliff:g id="PAGE">%1$d</xliff:g> orria (fitxategi-errorea)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"Ezin da kargatu oharpenen modua elementu honetarako."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"Esteka: <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g> domeinuko web-orria"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"Esteka: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"Helbide elektronikoa: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"Telefono-zenbakia: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"hautapenaren hasiera"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"hautapenaren amaiera"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 482722b..c1ad329 100644
--- a/pdf/pdf-viewer/src/main/res/values-fi/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-fi/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"Tiedostotyyppi"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"Tämä tiedosto on suojattu"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"Salasana"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"Väärä salasana"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"Peruuta"</string>
+ <string name="button_open" msgid="5445014062438566842">"Avaa"</string>
+ <string name="desc_password" msgid="6636473611443746739">"salasanakenttä"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"väärä salasana"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"väärä salasana"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"sivu <xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"zoomaus <xliff:g id="FIRST">%1$d</xliff:g> prosenttia"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"Siirry sivulle <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
<string name="desc_page" msgid="5684226167093594168">"sivu <xliff:g id="PAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"Valitse teksti, johon haluat lisätä kommentin"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"Napauta kommentoitavaa aluetta"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"Peruuta"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"PDF:ää ei voi näyttää (<xliff:g id="TITLE">%1$s</xliff:g> on virheellinen muoto)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"Sivua <xliff:g id="PAGE">%1$d</xliff:g> ei voi näyttää (tiedostovirhe)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"Kohteen muistiinpanotilaa ei voi ladata."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"Linkki: verkkosivu osoitteessa <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g>"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"Linkki: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"Sähköpostiosoite: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"Puhelinnumero: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"valinnan alku"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"valinnan loppu"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 bb4469d..d1cf1e2 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
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"Type de fichier"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"Ce fichier est protégé"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"Mot de passe"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"Mot de passe incorrect"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"Annuler"</string>
+ <string name="button_open" msgid="5445014062438566842">"Ouvrir"</string>
+ <string name="desc_password" msgid="6636473611443746739">"champ du mot de passe"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"mot de passe incorrect"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"mot de passe incorrect"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"page <xliff:g id="PAGE">%1$d</xliff:g> sur <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"zoom <xliff:g id="FIRST">%1$d</xliff:g> pour cent"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"Accéder à la page <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
<string name="desc_page" msgid="5684226167093594168">"page <xliff:g id="PAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"Sélect. du texte pour placer le commentaire"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"Touchez une zone à commenter"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"Annuler"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"Impossible d\'afficher le PDF (format de <xliff:g id="TITLE">%1$s</xliff:g> non valide)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"Impossible d\'afficher la page <xliff:g id="PAGE">%1$d</xliff:g> (erreur de fichier)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"Impossible de charger le mode d\'annotation pour cet élément."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"Lien : page Web sur <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g>"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"Lien : <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"Adresse de courriel : <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"Numéro de téléphone : <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"début de la sélection"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"fin de la sélection"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>–<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 37ae617..9ea14c1 100644
--- a/pdf/pdf-viewer/src/main/res/values-fr/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-fr/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"Type de fichier"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"Ce fichier est protégé"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"Mot de passe"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"Mot de passe incorrect"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"Annuler"</string>
+ <string name="button_open" msgid="5445014062438566842">"Ouvrir"</string>
+ <string name="desc_password" msgid="6636473611443746739">"champ de mot de passe"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"mot de passe incorrect"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"mot de passe incorrect"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"page <xliff:g id="PAGE">%1$d</xliff:g> sur <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"zoom : <xliff:g id="FIRST">%1$d</xliff:g> pour cent"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"Accéder à la page <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
<string name="desc_page" msgid="5684226167093594168">"page <xliff:g id="PAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"Sélectionnez le texte à commenter"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"Appuyez sur la zone à commenter"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"Annuler"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"Impossible d\'afficher le PDF (format non valide pour <xliff:g id="TITLE">%1$s</xliff:g>)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"Impossible d\'afficher la page <xliff:g id="PAGE">%1$d</xliff:g> (erreur de fichier)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"Impossible de charger le mode Annotation pour cet élément."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"Lien : page Web du domaine <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g>"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"Lien : <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"Adresse e-mail : <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"Téléphone : <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"début de la sélection"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"fin de la sélection"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 ce8c6b3..4a85719 100644
--- a/pdf/pdf-viewer/src/main/res/values-gl/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-gl/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"Tipo de ficheiro"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"Este ficheiro está protexido"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"Contrasinal"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"Contrasinal incorrecto"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"Cancelar"</string>
+ <string name="button_open" msgid="5445014062438566842">"Abrir"</string>
+ <string name="desc_password" msgid="6636473611443746739">"campo do contrasinal"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"contrasinal incorrecto"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"contrasinal incorrecto"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"páxina <xliff:g id="PAGE">%1$d</xliff:g> de <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"zoom ao <xliff:g id="FIRST">%1$d</xliff:g> por cento"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"Vai á páxina <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
<string name="desc_page" msgid="5684226167093594168">"páxina <xliff:g id="PAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"Selecciona o texto para engadir un comentario"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"Toca a zona na que engadir un comentario"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"Cancelar"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"Non se puido mostrar o PDF (<xliff:g id="TITLE">%1$s</xliff:g> ten un formato non válido)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"Non se puido mostrar a páxina <xliff:g id="PAGE">%1$d</xliff:g> (hai un erro no ficheiro)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"Non se puido cargar o modo de anotación para este elemento."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"Ligazón: páxina web en <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g>"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"Ligazón: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"Correo electrónico: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"Teléfono: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"inicio da selección"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"fin da selección"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 618d6b0..72c88d9 100644
--- a/pdf/pdf-viewer/src/main/res/values-hu/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-hu/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"Fájltípus"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"A fájl védett"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"Jelszó"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"Helytelen jelszó"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"Mégse"</string>
+ <string name="button_open" msgid="5445014062438566842">"Megnyitás"</string>
+ <string name="desc_password" msgid="6636473611443746739">"jelszómező"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"helytelen jelszó"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"helytelen jelszó"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="TOTAL">%2$d</xliff:g>/<xliff:g id="PAGE">%1$d</xliff:g>."</string>
<string name="desc_page_single" msgid="8459583146661044094">"<xliff:g id="TOTAL">%2$d</xliff:g>/<xliff:g id="PAGE">%1$d</xliff:g>. oldal"</string>
<string name="desc_zoom" msgid="7318480946145947242">"<xliff:g id="FIRST">%1$d</xliff:g> százalékos nagyítás"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"Ugrás erre az oldalra: <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
<string name="desc_page" msgid="5684226167093594168">"<xliff:g id="PAGE">%1$d</xliff:g>. oldal"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"Válassza ki a szöveget a megjegyzéshez"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"Koppintson arra a területre, amelyhez megjegyzést szeretne fűzni"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"Mégse"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"Nem sikerült megjeleníteni a PDF-et (<xliff:g id="TITLE">%1$s</xliff:g> formátuma érvénytelen)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"Nem sikerült megjeleníteni a(z) <xliff:g id="PAGE">%1$d</xliff:g> oldalt (fájlhiba)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"Az elemhez nem lehet betölteni kommentár módot."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"Link: weboldal helye: <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g>"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"Link: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"E-mail-cím: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"Telefon: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"kijelölés kezdete"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"kijelölés vége"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="TOTAL">%3$d</xliff:g>/<xliff:g id="FIRST">%1$d</xliff:g>–<xliff:g id="LAST">%2$d</xliff:g>."</string>
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 376f583..bcf72be 100644
--- a/pdf/pdf-viewer/src/main/res/values-hy/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-hy/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"Ֆայլի տեսակը"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"Այս ֆայլը պաշտպանված է"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"Գաղտնաբառ"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"Գաղտնաբառը սխալ է"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"Չեղարկել"</string>
+ <string name="button_open" msgid="5445014062438566842">"Բացել"</string>
+ <string name="desc_password" msgid="6636473611443746739">"գաղտնաբառի դաշտ"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"գաղտնաբառը սխալ է"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"գաղտնաբառը սխալ է"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"Էջ <xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"մասշտաբը՝ <xliff:g id="FIRST">%1$d</xliff:g> տոկոս"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"Անցեք <xliff:g id="PAGE_NUMBER">%1$d</xliff:g> էջ"</string>
<string name="desc_page" msgid="5684226167093594168">"էջ <xliff:g id="PAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"Նշեք տեքստը՝ մեկնաբանություն տեղադրելու համար"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"Հպեք՝ մեկնաբանություն ավելացնելու համար"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"Չեղարկել"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"Հնարավոր չէ ցուցադրել PDF-ը (<xliff:g id="TITLE">%1$s</xliff:g> ֆայլը անվավեր ձևաչափ ունի)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"Հնարավոր չէ ցուցադրել էջ <xliff:g id="PAGE">%1$d</xliff:g>-ը (ֆայլի սխալ)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"Չհաջողվեց բեռնել ծանոթագրության ռեժիմն այս տարրի համար։"</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"Հղում՝ կայքէջ <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g>-ում"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"Հղում՝ <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"Էլ․ հասցե՝ <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"Հեռախոս՝ <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"ընտրվածքի սկիզբ"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"ընտրվածքի վերջ"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>–<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 a6bc398..fb24fa5 100644
--- a/pdf/pdf-viewer/src/main/res/values-it/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-it/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"Tipo di file"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"Questo file è protetto"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"Password"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"Password errata"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"Annulla"</string>
+ <string name="button_open" msgid="5445014062438566842">"Apri"</string>
+ <string name="desc_password" msgid="6636473611443746739">"campo password"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"password errata"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"password errata"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"pagina <xliff:g id="PAGE">%1$d</xliff:g> di <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"zoom <xliff:g id="FIRST">%1$d</xliff:g>%%"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"Vai alla pagina <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
<string name="desc_page" msgid="5684226167093594168">"pagina <xliff:g id="PAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"Seleziona il testo da inserire nel commento"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"Tocca un\'area per inserire un commento"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"Annulla"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"Impossibile visualizzare PDF (<xliff:g id="TITLE">%1$s</xliff:g> è in un formato non valido)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"Impossibile visualizzare la pagina <xliff:g id="PAGE">%1$d</xliff:g> (errore del file)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"Impossibile caricare la modalità di annotazione per questo elemento."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"Link: pagina web all\'indirizzo <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g>"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"Link: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"Email: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"Telefono: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"inizio selezione"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"fine selezione"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 c7b0d79..411301a 100644
--- a/pdf/pdf-viewer/src/main/res/values-iw/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-iw/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"סוג הקובץ"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"הקובץ הזה מוגן"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"סיסמה"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"הסיסמה שגויה"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"ביטול"</string>
+ <string name="button_open" msgid="5445014062438566842">"פתיחה"</string>
+ <string name="desc_password" msgid="6636473611443746739">"שדה הסיסמה"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"הסיסמה שגויה"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"הסיסמה שגויה"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"דף <xliff:g id="PAGE">%1$d</xliff:g> מתוך <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"שינוי מרחק התצוגה ב-<xliff:g id="FIRST">%1$d</xliff:g> אחוזים"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"מעבר לדף <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
<string name="desc_page" msgid="5684226167093594168">"עמוד <xliff:g id="PAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"צריך לבחור את הטקסט לתגובה"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"אפשר להקיש על אזור כדי להגיב עליו"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"ביטול"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"אי אפשר להציג קובץ PDF (<xliff:g id="TITLE">%1$s</xliff:g> בפורמט לא תקין)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"לא ניתן להציג את הדף <xliff:g id="PAGE">%1$d</xliff:g> (שגיאת קובץ)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"אי אפשר לטעון את מצב ההערות בפריט הזה."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"קישור: דף אינטרנט בדומיין <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g>"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"קישור: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"אימייל: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"טלפון: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"התחלת הבחירה"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"סיום הבחירה"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 34f3ae5..13cb183 100644
--- a/pdf/pdf-viewer/src/main/res/values-ja/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ja/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"ファイル形式"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"このファイルは保護されています"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"パスワード"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"パスワードが正しくありません"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"キャンセル"</string>
+ <string name="button_open" msgid="5445014062438566842">"開く"</string>
+ <string name="desc_password" msgid="6636473611443746739">"パスワードを入力する項目です"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"パスワードが正しくありません"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"パスワードが正しくありません"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g> ページ"</string>
<string name="desc_zoom" msgid="7318480946145947242">"ズーム <xliff:g id="FIRST">%1$d</xliff:g> %%"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"<xliff:g id="PAGE_NUMBER">%1$d</xliff:g> ページに移動します"</string>
<string name="desc_page" msgid="5684226167093594168">"<xliff:g id="PAGE">%1$d</xliff:g> ページ"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"コメントを配置するテキストを選択してください"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"コメント対象の範囲をタップしてください"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"キャンセル"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"PDF を表示できません(<xliff:g id="TITLE">%1$s</xliff:g> の形式が無効です)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"<xliff:g id="PAGE">%1$d</xliff:g> ページを表示できません(ファイルエラー)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"このアイテムのアノテーション モードを読み込めません。"</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"リンク: ウェブページ(<xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g>)"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"リンク: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"メールアドレス: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"電話番号: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"選択範囲の最初"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"選択範囲の最後"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>~<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 dc8c626..115eceb 100644
--- a/pdf/pdf-viewer/src/main/res/values-ka/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ka/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"ფაილის ტიპი"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"ეს ფაილი დაცულია"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"პაროლი"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"პაროლი არასწორია"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"გაუქმება"</string>
+ <string name="button_open" msgid="5445014062438566842">"გახსნა"</string>
+ <string name="desc_password" msgid="6636473611443746739">"პაროლის ველი"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"პაროლი არასწორია"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"პაროლი არასწორია"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>-დან"</string>
<string name="desc_page_single" msgid="8459583146661044094">"გვერდი <xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>-დან"</string>
<string name="desc_zoom" msgid="7318480946145947242">"მასშტაბი <xliff:g id="FIRST">%1$d</xliff:g> პროცენტი"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"<xliff:g id="PAGE_NUMBER">%1$d</xliff:g>-ე გვერდზე გადასვლა"</string>
<string name="desc_page" msgid="5684226167093594168">"გვერდი <xliff:g id="PAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"კომენტარის განსათავსებლად მონიშნეთ ტექსტი"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"შეეხეთ ველს კომენტარის დასატოვებლად"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"გაუქმება"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"PDF-ის ჩვენება შეუძლებელია (<xliff:g id="TITLE">%1$s</xliff:g> არასწორ ფორმატშია)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"გვერდის ჩვენება შეუძლებელია <xliff:g id="PAGE">%1$d</xliff:g> (ფაილის შეცდომა)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"ამ ერთეულისთვის ანოტაციის რეჟიმის ჩატვირთვა არ არის შესაძლებელი."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"ბმული: ვებგვერდი <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g>"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"ბმული: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"ელფოსტა: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"ტელეფონის ნომერი: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"არჩევანის დასაწყისი"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"არჩევანის დასასრული"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>-დან"</string>
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 678c988..27917cf 100644
--- a/pdf/pdf-viewer/src/main/res/values-kk/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-kk/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"Файл түрі"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"Бұл файл қорғалған"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"Құпия сөз"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"Құпия сөз қате"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"Бас тарту"</string>
+ <string name="button_open" msgid="5445014062438566842">"Ашу"</string>
+ <string name="desc_password" msgid="6636473611443746739">"құпия сөз өрісі"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"құпия сөз қате"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"құпия сөз қате"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"Бет: <xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"<xliff:g id="FIRST">%1$d</xliff:g> пайызға масштабтау"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"<xliff:g id="PAGE_NUMBER">%1$d</xliff:g>-бетке өтіңіз."</string>
<string name="desc_page" msgid="5684226167093594168">"<xliff:g id="PAGE">%1$d</xliff:g>-бет"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"Пікір қалдыру үшін мәтін таңдаңыз."</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"Пікір қалдыру үшін аймақты түртіңіз."</string>
+ <string name="action_cancel" msgid="5494417739210197522">"Бас тарту"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"PDF файлын көрсету мүмкін емес (<xliff:g id="TITLE">%1$s</xliff:g> форматы жарамсыз)."</string>
+ <string name="error_on_page" msgid="1592475819957182385">"<xliff:g id="PAGE">%1$d</xliff:g>-бетті көрсету мүмкін емес (файл қатесі)."</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"Бұл элемент үшін aннотация режимін жүктеу мүмкін емес."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"Сілтеме: <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g> веб-беті"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"Сілтеме: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"Электрондық мекенжай: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"Телефон: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"таңдалған мәтіннің басы"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"таңдалған мәтіннің соңы"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 dca93d4..eef4ed4 100644
--- a/pdf/pdf-viewer/src/main/res/values-kn/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-kn/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"ಫೈಲ್ ಪ್ರಕಾರ"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"ಈ ಫೈಲ್ ಅನ್ನು ಸಂರಕ್ಷಿಸಲಾಗಿದೆ"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"ಪಾಸ್ವರ್ಡ್"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"ಪಾಸ್ವರ್ಡ್ ತಪ್ಪಾಗಿದೆ"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"ರದ್ದುಮಾಡಿ"</string>
+ <string name="button_open" msgid="5445014062438566842">"ತೆರೆಯಿರಿ"</string>
+ <string name="desc_password" msgid="6636473611443746739">"ಪಾಸ್ವರ್ಡ್ ಫೀಲ್ಡ್"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"ಪಾಸ್ವರ್ಡ್ ತಪ್ಪಾಗಿದೆ"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"ಪಾಸ್ವರ್ಡ್ ತಪ್ಪಾಗಿದೆ"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"<xliff:g id="TOTAL">%2$d</xliff:g> ರಲ್ಲಿ <xliff:g id="PAGE">%1$d</xliff:g> ಪುಟ"</string>
<string name="desc_zoom" msgid="7318480946145947242">"ಶೇಕಡಾ <xliff:g id="FIRST">%1$d</xliff:g> ರಷ್ಟು ಝೂಮ್ ಮಾಡಿ"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"ಪುಟ <xliff:g id="PAGE_NUMBER">%1$d</xliff:g> ಕ್ಕೆ ತೆರಳಿ"</string>
<string name="desc_page" msgid="5684226167093594168">"ಪುಟ <xliff:g id="PAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"ನಿಮ್ಮ ಕಾಮೆಂಟ್ ಅನ್ನು ಇರಿಸಲು ಪಠ್ಯವನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"ಕಾಮೆಂಟ್ ಮಾಡಲು ಪ್ರದೇಶವನ್ನು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"ರದ್ದುಮಾಡಿ"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"PDF ಅನ್ನು ಪ್ರದರ್ಶಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ (<xliff:g id="TITLE">%1$s</xliff:g> ಅಮಾನ್ಯವಾದ ಫಾರ್ಮ್ಯಾಟ್ ಆಗಿದೆ)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"<xliff:g id="PAGE">%1$d</xliff:g> ಪುಟವನ್ನು ಪ್ರದರ್ಶಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ (ಫೈಲ್ ದೋಷ)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"ಈ ಐಟಂಗೆ ಟಿಪ್ಪಣಿಯನ್ನು ಲೋಡ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"ಲಿಂಕ್: <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g> ನಲ್ಲಿರುವ ವೆಬ್ಪುಟ"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"ಲಿಂಕ್: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"ಇಮೇಲ್: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"ಫೋನ್: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"ಆಯ್ಕೆ ಪ್ರಾರಂಭ"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"ಆಯ್ಕೆ ಮುಕ್ತಾಯ"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 c473a9f..f183833 100644
--- a/pdf/pdf-viewer/src/main/res/values-ko/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ko/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"파일 형식"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"보호된 파일임"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"비밀번호"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"비밀번호가 잘못됨"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"취소"</string>
+ <string name="button_open" msgid="5445014062438566842">"열기"</string>
+ <string name="desc_password" msgid="6636473611443746739">"비밀번호 입력란"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"비밀번호가 잘못됨"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"비밀번호가 잘못됨"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"<xliff:g id="TOTAL">%2$d</xliff:g>페이지 중 <xliff:g id="PAGE">%1$d</xliff:g>페이지"</string>
<string name="desc_zoom" msgid="7318480946145947242">"<xliff:g id="FIRST">%1$d</xliff:g>%% 확대/축소"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"<xliff:g id="PAGE_NUMBER">%1$d</xliff:g> 페이지로 이동"</string>
<string name="desc_page" msgid="5684226167093594168">"<xliff:g id="PAGE">%1$d</xliff:g>페이지"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"댓글을 달려는 텍스트를 선택하세요."</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"댓글을 달려는 영역을 탭하세요."</string>
+ <string name="action_cancel" msgid="5494417739210197522">"취소"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"<xliff:g id="TITLE">%1$s</xliff:g>의 형식이 잘못되어 PDF를 표시할 수 없음"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"파일 오류로 <xliff:g id="PAGE">%1$d</xliff:g> 페이지를 표시할 수 없음"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"이 항목에 대한 주석 모드를 로드할 수 없습니다."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"링크: <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g>의 웹페이지"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"링크: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"이메일: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"전화번호: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"선택 영역 시작"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"선택 영역 끝"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>~<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 ca512f2..6742411 100644
--- a/pdf/pdf-viewer/src/main/res/values-lt/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-lt/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"Failo tipas"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"Šis failas yra apsaugotas"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"Slaptažodis"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"Slaptažodis netinkamas"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"Atšaukti"</string>
+ <string name="button_open" msgid="5445014062438566842">"Atidaryti"</string>
+ <string name="desc_password" msgid="6636473611443746739">"slaptažodžio laukas"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"slaptažodis netinkamas"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"slaptažodis netinkamas"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> iš <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"<xliff:g id="PAGE">%1$d</xliff:g> psl. iš <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"keisti mastelį <xliff:g id="FIRST">%1$d</xliff:g> proc."</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"Eiti į <xliff:g id="PAGE_NUMBER">%1$d</xliff:g> puslapį"</string>
<string name="desc_page" msgid="5684226167093594168">"<xliff:g id="PAGE">%1$d</xliff:g> psl."</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"Pasirinkite tekstą, kad pateiktumėte komentarą"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"Palieskite komentuotiną sritį"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"Atšaukti"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"Negalima pateikti PDF („<xliff:g id="TITLE">%1$s</xliff:g>“ netinkamo formato)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"Negalima pateikti <xliff:g id="PAGE">%1$d</xliff:g> puslapio (failo klaida)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"Nepavyko įkelti šio elemento komentaro režimo."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"Nuoroda: tinklalapis adresu <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g>"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"Nuoroda: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"El. pašto adresas: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"Telefono numeris: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"pasirinkimo pradžia"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"pasirinkimo pabaiga"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>–<xliff:g id="LAST">%2$d</xliff:g> iš <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 6807178..00d9d40 100644
--- a/pdf/pdf-viewer/src/main/res/values-lv/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-lv/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"Faila tips"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"Šis fails ir aizsargāts"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"Parole"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"Parole nav pareiza"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"Atcelt"</string>
+ <string name="button_open" msgid="5445014062438566842">"Atvērt"</string>
+ <string name="desc_password" msgid="6636473611443746739">"paroles lauks"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"parole nav pareiza"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"parole nav pareiza"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"<xliff:g id="PAGE">%1$d</xliff:g>. lapa no <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"tālummaiņa procentos ir <xliff:g id="FIRST">%1$d</xliff:g>"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"Doties uz <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>. lapu"</string>
<string name="desc_page" msgid="5684226167093594168">"<xliff:g id="PAGE">%1$d</xliff:g>. lapa"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"Atlasiet tekstu, lai pievienotu komentāru"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"Pieskarieties komentējamajam apgabalam"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"Atcelt"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"Nevar parādīt PDF (faila “<xliff:g id="TITLE">%1$s</xliff:g>” formāts nav derīgs)."</string>
+ <string name="error_on_page" msgid="1592475819957182385">"Nevar parādīt <xliff:g id="PAGE">%1$d</xliff:g>. lapu (faila kļūda)."</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"Šim vienumam nevar ielādēt anotēšanas režīmu."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"Saite: tīmekļa lapa domēnā <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g>"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"Saite: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"E-pasta adrese: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"Tālruņa numurs: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"atlases sākums"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"atlases beigas"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>–<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 2c8826b..d128f84 100644
--- a/pdf/pdf-viewer/src/main/res/values-mk/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-mk/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"Вид датотека"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"Датотекава е заштитена"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"Лозинка"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"Лозинката е неточна"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"Откажи"</string>
+ <string name="button_open" msgid="5445014062438566842">"Отвори"</string>
+ <string name="desc_password" msgid="6636473611443746739">"поле за лозинка"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"лозинката е неточна"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"лозинката е неточна"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"страница <xliff:g id="PAGE">%1$d</xliff:g> од <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"зумирајте <xliff:g id="FIRST">%1$d</xliff:g> проценти"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"Одете на страницата <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
<string name="desc_page" msgid="5684226167093594168">"страница <xliff:g id="PAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"Изберете текст за поставување на коментарот"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"Допрете област за која ќе коментирате"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"Откажи"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"Не може да се прикаже PDF (<xliff:g id="TITLE">%1$s</xliff:g> е со неважечки формат)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"Не може да се прикаже страницата <xliff:g id="PAGE">%1$d</xliff:g> (грешка во датотеката)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"Не може да се вчита „Режимот за прибелешки“ за ставкава."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"Линк: веб-страница на <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g>"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"Линк: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"Е-пошта: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"Телефон: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"почеток на изборот"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"крај на изборот"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g> – <xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 aacb03b..e746e33 100644
--- a/pdf/pdf-viewer/src/main/res/values-mn/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-mn/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"Файлын төрөл"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"Энэ файл хамгаалагдсан"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"Нууц үг"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"Нууц үг буруу байна"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"Цуцлах"</string>
+ <string name="button_open" msgid="5445014062438566842">"Нээх"</string>
+ <string name="desc_password" msgid="6636473611443746739">"нууц үгний талбар"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"нууц үг буруу байна"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"нууц үг буруу байна"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="TOTAL">%2$d</xliff:g>-н <xliff:g id="PAGE">%1$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"<xliff:g id="TOTAL">%2$d</xliff:g>-н <xliff:g id="PAGE">%1$d</xliff:g>-р хуудас"</string>
<string name="desc_zoom" msgid="7318480946145947242">"<xliff:g id="FIRST">%1$d</xliff:g> хувь томруулсан"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"<xliff:g id="PAGE_NUMBER">%1$d</xliff:g>-р хуудсанд очих"</string>
<string name="desc_page" msgid="5684226167093594168">"<xliff:g id="PAGE">%1$d</xliff:g>-р хуудас"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"Сэтгэгдлээ байрлуулахын тулд текст сонгоно уу"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"Сэтгэгдэл бичих хэсэг дээр товшино уу"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"Цуцлах"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"PDF-г үзүүлэх боломжгүй (<xliff:g id="TITLE">%1$s</xliff:g>-н формат буруу байна)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"<xliff:g id="PAGE">%1$d</xliff:g> хуудсыг үзүүлэх боломжгүй (файлын алдаа)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"Энэ зүйлд тэмдэглэгээний горимыг ачаалах боломжгүй."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"Холбоос: <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g> дээрх веб хуудас"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"Холбоос: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"Имэйл: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"Утас: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"сонголтын эхлэл"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"сонголтын төгсгөл"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 1d11dc7..4da35f6 100644
--- a/pdf/pdf-viewer/src/main/res/values-mr/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-mr/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"फाइल प्रकार"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"ही फाइल संरक्षित आहे"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"पासवर्ड"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"पासवर्ड चुकीचा आहे"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"रद्द करा"</string>
+ <string name="button_open" msgid="5445014062438566842">"उघडा"</string>
+ <string name="desc_password" msgid="6636473611443746739">"पासवर्ड फील्ड"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"पासवर्ड चुकीचा आहे"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"पासवर्ड चुकीचा आहे"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"<xliff:g id="TOTAL">%2$d</xliff:g> पैकी पेज <xliff:g id="PAGE">%1$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"<xliff:g id="FIRST">%1$d</xliff:g> टक्के झूम करा"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"पेज <xliff:g id="PAGE_NUMBER">%1$d</xliff:g> वर जा"</string>
<string name="desc_page" msgid="5684226167093594168">"पेज <xliff:g id="PAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"तुमच्या टिप्पणी ठेवण्यासाठी मजकूर निवडा"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"टिप्पणी करायच्या क्षेत्रावर टॅप करा"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"रद्द करा"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"PDF दाखवू शकत नाही (<xliff:g id="TITLE">%1$s</xliff:g> चा फॉरमॅट चुकीचा आहे)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"<xliff:g id="PAGE">%1$d</xliff:g> पेज दाखवू शकत नाही (फाइल एरर)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"या आयटमसाठी भाष्य मोड लोड करू शकत नाही."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"लिंक: <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g> वरील वेबपेज"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"लिंक: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"ईमेल: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"फोन: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"निवडणे सुरू करा"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"निवडणे संपवा"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 dbeac1d..085401d 100644
--- a/pdf/pdf-viewer/src/main/res/values-my/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-my/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"ဖိုင်အမျိုးအစား"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"ဤဖိုင်ကို ကာကွယ်ထားသည်"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"စကားဝှက်"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"စကားဝှက် မှားနေသည်"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"မလုပ်တော့"</string>
+ <string name="button_open" msgid="5445014062438566842">"ဖွင့်ရန်"</string>
+ <string name="desc_password" msgid="6636473611443746739">"စကားဝှက်ထည့်ရန် အကွက်"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"စကားဝှက် မှားနေသည်"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"စကားဝှက် မှားနေသည်"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"စာမျက်နှာ <xliff:g id="TOTAL">%2$d</xliff:g> အနက် <xliff:g id="PAGE">%1$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"ဇူးမ် <xliff:g id="FIRST">%1$d</xliff:g> ရာခိုင်နှုန်း"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"စာမျက်နှာ <xliff:g id="PAGE_NUMBER">%1$d</xliff:g> သို့"</string>
<string name="desc_page" msgid="5684226167093594168">"စာမျက်နှာ <xliff:g id="PAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"သင်၏မှတ်ချက်ထည့်ရန် စာသားကို ရွေးပါ"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"မှတ်ချက်ပေးရန် နေရာကို တို့ပါ"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"မလုပ်တော့"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"PDF ကို ပြ၍မရပါ (<xliff:g id="TITLE">%1$s</xliff:g> သည် မမှန်ကန်သော ဖော်မက်ဖြစ်သည်)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"စာမျက်နှာ <xliff:g id="PAGE">%1$d</xliff:g> ကို ပြ၍မရပါ (ဖိုင်အမှား)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"ဤအကြောင်းအရာအတွက် မှတ်ချက်မုဒ် ဖွင့်၍မရပါ။"</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"လင့်ခ်- <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g> ရှိ ဝဘ်စာမျက်နှာ"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"လင့်ခ်- <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"အီးမေးလ်- <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"ဖုန်း- <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"ရွေးချယ်မှုအစ"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"ရွေးချယ်မှုအဆုံး"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 189acf8..c1c2c18 100644
--- a/pdf/pdf-viewer/src/main/res/values-nb/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-nb/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"Filtype"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"Denne filen er beskyttet"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"Passord"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"Passordet er feil"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"Avbryt"</string>
+ <string name="button_open" msgid="5445014062438566842">"Åpne"</string>
+ <string name="desc_password" msgid="6636473611443746739">"passordfelt"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"feil passord"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"feil passord"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"side <xliff:g id="PAGE">%1$d</xliff:g> av <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"zoom <xliff:g id="FIRST">%1$d</xliff:g> prosent"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"Gå til side <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
<string name="desc_page" msgid="5684226167093594168">"side <xliff:g id="PAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"Merk tekst for å sette inn kommentaren din"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"Trykk på et område du vil kommentere"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"Avbryt"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"Kan ikke vise PDF-filen (<xliff:g id="TITLE">%1$s</xliff:g> har ugyldig format)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"Kan ikke vise side <xliff:g id="PAGE">%1$d</xliff:g> (filfeil)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"Kan ikke laste inn annoteringsmodus for dette elementet."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"Link: nettside på <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g>"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"Link: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"E-postadresse: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"Telefonnummer: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"starten av utvalget"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"slutten av utvalget"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>–<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 caadce5..cf1802a 100644
--- a/pdf/pdf-viewer/src/main/res/values-nl/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-nl/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"Bestandstype"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"Dit bestand is beveiligd"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"Wachtwoord"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"Wachtwoord is onjuist"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"Annuleren"</string>
+ <string name="button_open" msgid="5445014062438566842">"Openen"</string>
+ <string name="desc_password" msgid="6636473611443746739">"wachtwoordveld"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"wachtwoord is onjuist"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"wachtwoord is onjuist"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"pagina <xliff:g id="PAGE">%1$d</xliff:g> van <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"zoom <xliff:g id="FIRST">%1$d</xliff:g> procent"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"Ga naar pagina <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
<string name="desc_page" msgid="5684226167093594168">"pagina <xliff:g id="PAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"Selecteer tekst om je reactie te plaatsen"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"Tik op een gedeelte waarop je wilt reageren"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"Annuleren"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"Kan pdf niet weergeven (indeling van <xliff:g id="TITLE">%1$s</xliff:g> is ongeldig)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"Kan pagina <xliff:g id="PAGE">%1$d</xliff:g> niet weergeven (bestandsfout)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"Kan de annotatiemodus voor dit item niet laden."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"Link: webpagina op <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g>"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"Link: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"E-mail: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"Telefoonnummer: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"begin selectie"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"einde selectie"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 7579ef3..284fce6 100644
--- a/pdf/pdf-viewer/src/main/res/values-pa/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-pa/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"ਫ਼ਾਈਲ ਦੀ ਕਿਸਮ"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"ਇਹ ਫ਼ਾਈਲ ਸੁਰੱਖਿਅਤ ਹੈ"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"ਪਾਸਵਰਡ"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"ਪਾਸਵਰਡ ਗਲਤ ਹੈ"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"ਰੱਦ ਕਰੋ"</string>
+ <string name="button_open" msgid="5445014062438566842">"ਖੋਲ੍ਹੋ"</string>
+ <string name="desc_password" msgid="6636473611443746739">"ਪਾਸਵਰਡ ਖੇਤਰ"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"ਪਾਸਵਰਡ ਗਲਤ ਹੈ"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"ਪਾਸਵਰਡ ਗਲਤ ਹੈ"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"<xliff:g id="TOTAL">%2$d</xliff:g> ਵਿੱਚੋਂ <xliff:g id="PAGE">%1$d</xliff:g> ਪੰਨਾ"</string>
<string name="desc_zoom" msgid="7318480946145947242">"ਜ਼ੂਮ <xliff:g id="FIRST">%1$d</xliff:g> ਫ਼ੀਸਦ"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"<xliff:g id="PAGE_NUMBER">%1$d</xliff:g> ਪੰਨੇ \'ਤੇ ਜਾਓ"</string>
<string name="desc_page" msgid="5684226167093594168">"ਪੰਨਾ <xliff:g id="PAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"ਆਪਣੀ ਟਿੱਪਣੀ ਕਰਨ ਲਈ ਲਿਖਤ ਨੂੰ ਚੁਣੋ"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"ਟਿੱਪਣੀ ਕਰਨ ਲਈ ਕਿਸੇ ਖੇਤਰ \'ਤੇ ਟੈਪ ਕਰੋ"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"ਰੱਦ ਕਰੋ"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"PDF ਨੂੰ ਦਿਖਾਇਆ ਨਹੀਂ ਜਾ ਸਕਦਾ (<xliff:g id="TITLE">%1$s</xliff:g> ਅਵੈਧ ਫਾਰਮੈਟ ਦਾ ਹੈ)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"<xliff:g id="PAGE">%1$d</xliff:g> ਪੰਨੇ ਨੂੰ ਦਿਖਾਇਆ ਨਹੀਂ ਜਾ ਸਕਦਾ (ਫ਼ਾਈਲ ਗੜਬੜ)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"ਇਸ ਆਈਟਮ ਲਈ ਐਨੋਟੇਸ਼ਨ ਮੋਡ ਨੂੰ ਲੋਡ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ।"</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"ਲਿੰਕ: ਵੈੱਬ-ਪੰਨਾ <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g> \'ਤੇ ਹੈ"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"ਲਿੰਕ: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"ਈਮੇਲ: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"ਫ਼ੋਨ ਨੰਬਰ: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"ਚੁਣੀ ਲਿਖਤ ਦਾ ਸ਼ੁਰੂਆਤੀ ਸਿਰਾ"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"ਚੁਣੀ ਲਿਖਤ ਦਾ ਆਖਰੀ ਸਿਰਾ"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 42e6c8c..439ec80 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
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"Tipo de arquivo"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"Este arquivo está protegido"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"Senha"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"Senha incorreta"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"Cancelar"</string>
+ <string name="button_open" msgid="5445014062438566842">"Abrir"</string>
+ <string name="desc_password" msgid="6636473611443746739">"campo senha"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"senha incorreta"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"senha incorreta"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"página <xliff:g id="PAGE">%1$d</xliff:g> de <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"zoom de <xliff:g id="FIRST">%1$d</xliff:g>%%"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"Ir para a página <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
<string name="desc_page" msgid="5684226167093594168">"página <xliff:g id="PAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"Selecione o texto para inserir seu comentário"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"Toque em uma área para comentar"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"Cancelar"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"Não é possível mostrar o PDF (<xliff:g id="TITLE">%1$s</xliff:g>: formato inválido)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"Não é possível mostrar a página <xliff:g id="PAGE">%1$d</xliff:g> (erro de arquivo)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"Não foi possível carregar o modo de anotação deste item."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"Link: página da Web de <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g>"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"Link: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"E-mail: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"Telefone: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"início da seleção"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"fim da seleção"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g> a <xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
diff --git a/pdf/pdf-viewer/src/main/res/values-pt/strings.xml b/pdf/pdf-viewer/src/main/res/values-pt/strings.xml
index 42e6c8c..439ec80 100644
--- a/pdf/pdf-viewer/src/main/res/values-pt/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-pt/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"Tipo de arquivo"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"Este arquivo está protegido"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"Senha"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"Senha incorreta"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"Cancelar"</string>
+ <string name="button_open" msgid="5445014062438566842">"Abrir"</string>
+ <string name="desc_password" msgid="6636473611443746739">"campo senha"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"senha incorreta"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"senha incorreta"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"página <xliff:g id="PAGE">%1$d</xliff:g> de <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"zoom de <xliff:g id="FIRST">%1$d</xliff:g>%%"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"Ir para a página <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
<string name="desc_page" msgid="5684226167093594168">"página <xliff:g id="PAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"Selecione o texto para inserir seu comentário"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"Toque em uma área para comentar"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"Cancelar"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"Não é possível mostrar o PDF (<xliff:g id="TITLE">%1$s</xliff:g>: formato inválido)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"Não é possível mostrar a página <xliff:g id="PAGE">%1$d</xliff:g> (erro de arquivo)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"Não foi possível carregar o modo de anotação deste item."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"Link: página da Web de <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g>"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"Link: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"E-mail: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"Telefone: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"início da seleção"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"fim da seleção"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g> a <xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 25f3894..ca17732 100644
--- a/pdf/pdf-viewer/src/main/res/values-ro/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ro/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"Tip de fișier"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"Acest fișier este protejat"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"Parolă"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"Parolă incorectă"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"Anulează"</string>
+ <string name="button_open" msgid="5445014062438566842">"Deschide"</string>
+ <string name="desc_password" msgid="6636473611443746739">"câmpul pentru parolă"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"parolă incorectă"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"parolă incorectă"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"pagina <xliff:g id="PAGE">%1$d</xliff:g> din <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"zoom de <xliff:g id="FIRST">%1$d</xliff:g> %%"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"Accesează pagina <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
<string name="desc_page" msgid="5684226167093594168">"pagina <xliff:g id="PAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"Selectează textul pentru a comenta"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"Atinge o zonă pentru a comenta"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"Anulează"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"Nu se poate afișa ca PDF (<xliff:g id="TITLE">%1$s</xliff:g> are un format nevalid)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"Nu se poate afișa pagina <xliff:g id="PAGE">%1$d</xliff:g> (eroare de fișier)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"Nu se poate încărca modul de adnotare pentru acest element."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"Link: pagina web de la <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g>"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"Link: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"E-mail: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"Telefon: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"începutul selecției"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"sfârșitul selecției"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 79a1229..23cd51b 100644
--- a/pdf/pdf-viewer/src/main/res/values-si/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-si/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"ගොනු වර්ගය"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"මෙම ගොනුව ආරක්ෂා කර ඇත"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"මුරපදය"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"මුරපදය වැරදියි"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"අවලංගු කරන්න"</string>
+ <string name="button_open" msgid="5445014062438566842">"විවෘත කරන්න"</string>
+ <string name="desc_password" msgid="6636473611443746739">"මුරපද ක්ෂේත්රය"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"මුරපදය වැරදියි"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"මුරපදය වැරදියි"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"<xliff:g id="TOTAL">%2$d</xliff:g> න් <xliff:g id="PAGE">%1$d</xliff:g> වෙනි පිටුව"</string>
<string name="desc_zoom" msgid="7318480946145947242">"විශාලනය සියයට <xliff:g id="FIRST">%1$d</xliff:g>"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"<xliff:g id="PAGE_NUMBER">%1$d</xliff:g> වෙනි පිටුවට යන්න"</string>
<string name="desc_page" msgid="5684226167093594168">"පිටුව <xliff:g id="PAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"ඔබේ අදහස තැබීමට පෙළ තෝරන්න"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"අදහස් දැක්වීමට ප්රදේශයක් තට්ටු කරන්න"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"අවලංගු කරන්න"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"PDF සංදර්ශනය කළ නොහැක (<xliff:g id="TITLE">%1$s</xliff:g> අවලංගු ආකෘතියකි)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"පිටුව සංදර්ශනය කළ නොහැක <xliff:g id="PAGE">%1$d</xliff:g> (ගොනු දෝෂය)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"මෙම අයිතමය සඳහා අනුසටහන් ප්රකාරය පූරණය කළ නොහැක."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"සබැඳිය: <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g> හි දී වෙබ් පිටුව"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"සබැඳිය: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"ඉ-තැපෑල: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"දුරකථනය: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"තේරීම ආරම්භය"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"තේරීම අවසානය"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 b478e0c..caebc77 100644
--- a/pdf/pdf-viewer/src/main/res/values-sq/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-sq/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"Lloji i skedarit"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"Ky skedar është i mbrojtur."</string>
+ <string name="label_password_first" msgid="4456258714097111908">"Fjalëkalimi"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"Fjalëkalimi është i pasaktë"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"Anulo"</string>
+ <string name="button_open" msgid="5445014062438566842">"Hap"</string>
+ <string name="desc_password" msgid="6636473611443746739">"fusha e fjalëkalimit"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"fjalëkalimi është i pasaktë"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"fjalëkalimi është i pasaktë"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"faqja <xliff:g id="PAGE">%1$d</xliff:g> nga <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"zmadho me <xliff:g id="FIRST">%1$d</xliff:g> për qind"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"Shko te faqja <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
<string name="desc_page" msgid="5684226167093594168">"faqja <xliff:g id="PAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"Zgjidh tekstin për të vendosur komentin"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"Trokit te një zonë për të komentuar"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"Anulo"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"Skedari PDF nuk mund të shfaqet (\"<xliff:g id="TITLE">%1$s</xliff:g>\" ka format të pavlefshëm)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"Nuk mund të shfaqet faqja <xliff:g id="PAGE">%1$d</xliff:g> (gabim i skedarit)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"Modaliteti i shënimit nuk mund të ngarkohet për këtë artikull."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"Lidhja: faqe uebi te <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g>"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"Lidhja: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"Email-i: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"Telefoni: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"fillimi i përzgjedhjes"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"fundi i përzgjedhjes"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 925f7d9..99b7d55 100644
--- a/pdf/pdf-viewer/src/main/res/values-sv/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-sv/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"Filtyp"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"Den här filen är skyddad"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"Lösenord"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"Felaktigt lösenord"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"Avbryt"</string>
+ <string name="button_open" msgid="5445014062438566842">"Öppna"</string>
+ <string name="desc_password" msgid="6636473611443746739">"lösenordsfält"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"felaktigt lösenord"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"felaktigt lösenord"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"sida <xliff:g id="PAGE">%1$d</xliff:g> av <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"zooma <xliff:g id="FIRST">%1$d</xliff:g> procent"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"Öppna sidan <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
<string name="desc_page" msgid="5684226167093594168">"sida <xliff:g id="PAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"Markera text för att kommentera"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"Tryck på ett område för att kommentera"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"Avbryt"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"Det går inte att visa PDF-filen (<xliff:g id="TITLE">%1$s</xliff:g> har ett ogiltigt format)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"Det går inte att visa sidan <xliff:g id="PAGE">%1$d</xliff:g> (filfel)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"Det gick inte att läsa in kommentarsläget för det här objektet."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"Länk: webbsida på <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g>"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"Länk: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"E-postadress: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"Telefonnummer: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"textmarkeringens början"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"textmarkeringens slut"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>–<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 c2f1606..63788a8 100644
--- a/pdf/pdf-viewer/src/main/res/values-ta/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-ta/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"ஃபைல் வகை"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"இந்த ஃபைல் பாதுகாக்கப்பட்டுள்ளது"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"கடவுச்சொல்"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"கடவுச்சொல் தவறானது"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"ரத்துசெய்"</string>
+ <string name="button_open" msgid="5445014062438566842">"திற"</string>
+ <string name="desc_password" msgid="6636473611443746739">"கடவுச்சொல் புலம்"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"கடவுச்சொல் தவறானது"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"கடவுச்சொல் தவறானது"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"பக்கம் <xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"<xliff:g id="FIRST">%1$d</xliff:g> சதவீத அளவை மாற்று"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"பக்கம் <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>க்கு செல்லும்"</string>
<string name="desc_page" msgid="5684226167093594168">"பக்கம் <xliff:g id="PAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"கருத்து வழங்க வார்த்தையைத் தேர்ந்தெடுக்கவும்"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"கருத்து வழங்குவதற்கான பகுதியைத் தட்டவும்"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"ரத்துசெய்"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"PDFஐக் காட்ட முடியவில்லை (<xliff:g id="TITLE">%1$s</xliff:g> தவறான வடிவத்தில் உள்ளது)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"பக்கம் <xliff:g id="PAGE">%1$d</xliff:g>ஐக் காட்ட முடியவில்லை (ஃபைல் பிழை)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"இதற்கான விரிவுரைப் பயன்முறையை ஏற்ற முடியவில்லை."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"இணைப்பு: <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g> இல் உள்ள இணையப் பக்கம்"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"இணைப்பு: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"மின்னஞ்சல்: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"ஃபோன் எண்: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"தேர்வுசெய்த வார்த்தையின் தொடக்கம்"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"தேர்வுசெய்த வார்த்தையின் முடிவு"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 e789a8a..db0c86c7 100644
--- a/pdf/pdf-viewer/src/main/res/values-tr/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-tr/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"Dosya türü"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"Bu dosya korunuyor"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"Şifre"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"Şifre yanlış"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"İptal"</string>
+ <string name="button_open" msgid="5445014062438566842">"Aç"</string>
+ <string name="desc_password" msgid="6636473611443746739">"şifre alanı"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"şifre yanlış"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"şifre yanlış"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"sayfa <xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"yakınlaştırma yüzdesi <xliff:g id="FIRST">%1$d</xliff:g>"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"Şu sayfaya git <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
<string name="desc_page" msgid="5684226167093594168">"sayfa <xliff:g id="PAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"Yorumunuzu yerleştireceğiniz metni seçin"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"Hakkında yorum yapacağınız alana dokunun"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"İptal"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"PDF görüntülenemiyor (<xliff:g id="TITLE">%1$s</xliff:g> geçersiz biçimde)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"<xliff:g id="PAGE">%1$d</xliff:g>. sayfa görüntülenemiyor (dosya hatası)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"Bu öğe için ek açıklama modu yüklenemiyor."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"Bağlantı: <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g> alan adındaki web sayfası"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"Bağlantı: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"E-posta gönder: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"Telefon: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"seçim başlangıcı"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"seçim sonu"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 de608a9..216d16c 100644
--- a/pdf/pdf-viewer/src/main/res/values-uk/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-uk/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"Тип файлу"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"Цей файл захищено"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"Пароль"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"Неправильний пароль"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"Скасувати"</string>
+ <string name="button_open" msgid="5445014062438566842">"Відкрити"</string>
+ <string name="desc_password" msgid="6636473611443746739">"поле пароля"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"неправильний пароль"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"неправильний пароль"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"сторінка <xliff:g id="PAGE">%1$d</xliff:g> з <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"масштаб <xliff:g id="FIRST">%1$d</xliff:g>%%"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"Перейти на сторінку <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
<string name="desc_page" msgid="5684226167093594168">"сторінка <xliff:g id="PAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"Виділіть текст, щоб додати коментар"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"Торкніться місця, яке хочете коментувати"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"Скасувати"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"Не вдається відобразити PDF (недійсний формат файлу \"<xliff:g id="TITLE">%1$s</xliff:g>\")"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"Не вдається відобразити сторінку <xliff:g id="PAGE">%1$d</xliff:g> (помилка файлу)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"Не вдається завантажити режим анотацій для цього об’єкта."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"Посилання: вебсторінка в домені <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g>"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"Посилання: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"Електронна адреса: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"Номер телефону: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"початок виділеного тексту"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"кінець виділеного тексту"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>–<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 7ac1e17..2a6902f 100644
--- a/pdf/pdf-viewer/src/main/res/values-uz/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-uz/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"Fayl turi"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"Ushbu fayl himoyalangan"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"Parol"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"Parol xato"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"Bekor qilish"</string>
+ <string name="button_open" msgid="5445014062438566842">"Ochish"</string>
+ <string name="desc_password" msgid="6636473611443746739">"parol maydonchasi"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"parol xato"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"parol xato"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"sahifa: <xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"zum: <xliff:g id="FIRST">%1$d</xliff:g> foiz"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"<xliff:g id="PAGE_NUMBER">%1$d</xliff:g>-sahifaga oʻtish"</string>
<string name="desc_page" msgid="5684226167093594168">"sahifa: <xliff:g id="PAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"Fikr bildirish uchun matnni tanlang."</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"Fikr qoʻshish uchun biror maydonni bosing"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"Bekor qilish"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"PDF fayl koʻrsatilmaydi (<xliff:g id="TITLE">%1$s</xliff:g> yaroqsiz formatda)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"<xliff:g id="PAGE">%1$d</xliff:g>-sahifa koʻrsatilmaydi (fayl xatosi)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"Bu obyekt uchun izoh rejimi yuklanmadi."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"Havola: <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g> sahifasi"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"Havola: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"Email: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"Telefon: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"belgilashni boshlash"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"belgilashni yakunlash"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 463515d..0f5033a 100644
--- a/pdf/pdf-viewer/src/main/res/values-vi/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-vi/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"Loại tệp"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"Tệp này đang được bảo vệ"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"Mật khẩu"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"Mật khẩu không chính xác"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"Huỷ"</string>
+ <string name="button_open" msgid="5445014062438566842">"Mở"</string>
+ <string name="desc_password" msgid="6636473611443746739">"trường nhập mật khẩu"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"mật khẩu không chính xác"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"mật khẩu không chính xác"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"trang <xliff:g id="PAGE">%1$d</xliff:g> trong số <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"thu phóng <xliff:g id="FIRST">%1$d</xliff:g> phần trăm"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"Chuyển đến trang <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
<string name="desc_page" msgid="5684226167093594168">"trang <xliff:g id="PAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"Chọn văn bản để đưa ra nhận xét"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"Nhấn vào một khu vực để thêm nhận xét"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"Huỷ"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"Không hiển thị được tệp PDF (<xliff:g id="TITLE">%1$s</xliff:g> có định dạng không hợp lệ)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"Không hiển thị được trang <xliff:g id="PAGE">%1$d</xliff:g> (lỗi tệp)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"Không tải được chế độ chú giải cho mục này."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"Đường liên kết: trang trên trang web <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g>"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"Đường liên kết: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"Email: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"Số điện thoại: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"đầu văn bản đã chọn"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"cuối văn bản đã chọn"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g> – <xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 57799bb..3094e5c 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
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"文件类型"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"此文件受密码保护"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"密码"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"密码不正确"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"取消"</string>
+ <string name="button_open" msgid="5445014062438566842">"打开"</string>
+ <string name="desc_password" msgid="6636473611443746739">"密码字段"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"密码不正确"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"密码不正确"</string>
<string name="label_page_single" msgid="456123685879261101">"第 <xliff:g id="PAGE">%1$d</xliff:g> 页,共 <xliff:g id="TOTAL">%2$d</xliff:g> 页"</string>
<string name="desc_page_single" msgid="8459583146661044094">"第 <xliff:g id="PAGE">%1$d</xliff:g> 页,共 <xliff:g id="TOTAL">%2$d</xliff:g> 页"</string>
<string name="desc_zoom" msgid="7318480946145947242">"缩放比例为百分之 <xliff:g id="FIRST">%1$d</xliff:g>"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"前往第 <xliff:g id="PAGE_NUMBER">%1$d</xliff:g> 页"</string>
<string name="desc_page" msgid="5684226167093594168">"第 <xliff:g id="PAGE">%1$d</xliff:g> 页"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"选择要添加评论的文本"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"点按要添加评论的区域"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"取消"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"无法显示 PDF(“<xliff:g id="TITLE">%1$s</xliff:g>”的格式无效)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"无法显示第 <xliff:g id="PAGE">%1$d</xliff:g> 页(文件错误)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"无法为此内容加载注解模式。"</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"链接:位于 <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g> 的网页"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"链接:<xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"电子邮件地址:<xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"电话号码:<xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"所选文本的开头"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"所选文本的结尾"</string>
<string name="label_page_range" msgid="8290964180158460076">"第 <xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g> 页,共 <xliff:g id="TOTAL">%3$d</xliff:g> 页"</string>
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 0711b16..34b36c0 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
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"檔案類型"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"此檔案受到保護"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"密碼"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"密碼不正確"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"取消"</string>
+ <string name="button_open" msgid="5445014062438566842">"開啟"</string>
+ <string name="desc_password" msgid="6636473611443746739">"密碼欄位"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"密碼不正確"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"密碼不正確"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"第 <xliff:g id="PAGE">%1$d</xliff:g> 頁,共 <xliff:g id="TOTAL">%2$d</xliff:g> 頁"</string>
<string name="desc_zoom" msgid="7318480946145947242">"縮放 <xliff:g id="FIRST">%1$d</xliff:g>%%"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"前往第 <xliff:g id="PAGE_NUMBER">%1$d</xliff:g> 頁"</string>
<string name="desc_page" msgid="5684226167093594168">"第 <xliff:g id="PAGE">%1$d</xliff:g> 頁"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"選取文字以加入留言"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"輕按要加入留言的區域"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"取消"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"無法顯示 PDF (<xliff:g id="TITLE">%1$s</xliff:g> 格式無效)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"無法顯示第 <xliff:g id="PAGE">%1$d</xliff:g> 頁 (檔案錯誤)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"無法為此項目載入註釋模式。"</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"連結:位於 <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g> 的網頁"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"連結:<xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"電郵地址:<xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"電話:<xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"選取開頭"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"選取結尾"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g> - <xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 1f08710..f836592 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
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"檔案類型"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"這個檔案受密碼保護"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"密碼"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"密碼不正確"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"取消"</string>
+ <string name="button_open" msgid="5445014062438566842">"開啟"</string>
+ <string name="desc_password" msgid="6636473611443746739">"密碼欄位"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"密碼不正確"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"密碼不正確"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"第 <xliff:g id="PAGE">%1$d</xliff:g> 頁,共 <xliff:g id="TOTAL">%2$d</xliff:g> 頁"</string>
<string name="desc_zoom" msgid="7318480946145947242">"縮放 <xliff:g id="FIRST">%1$d</xliff:g>%%"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"前往第 <xliff:g id="PAGE_NUMBER">%1$d</xliff:g> 頁"</string>
<string name="desc_page" msgid="5684226167093594168">"第 <xliff:g id="PAGE">%1$d</xliff:g> 頁"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"選取要加註的文字"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"輕觸要加註的區域"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"取消"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"無法顯示 PDF (「<xliff:g id="TITLE">%1$s</xliff:g>」的格式無效)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"無法顯示第 <xliff:g id="PAGE">%1$d</xliff:g> 頁 (檔案錯誤)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"無法載入這個項目的註解模式。"</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"連結:位於 <xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g> 的網頁"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"連結:<xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"電子郵件地址:<xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"電話號碼:<xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"選取開頭"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"選取結尾"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g> - <xliff:g id="LAST">%2$d</xliff:g>/<xliff:g id="TOTAL">%3$d</xliff:g>"</string>
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 11347a3..103f045 100644
--- a/pdf/pdf-viewer/src/main/res/values-zu/strings.xml
+++ b/pdf/pdf-viewer/src/main/res/values-zu/strings.xml
@@ -18,48 +18,29 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="desc_file_type" msgid="8918077960128045611">"Uhlobo lwefayela"</string>
- <!-- no translation found for title_dialog_password (2018068413709926925) -->
- <skip />
- <!-- no translation found for label_password_first (4456258714097111908) -->
- <skip />
- <!-- no translation found for label_password_incorrect (8449142641187704667) -->
- <skip />
- <!-- no translation found for button_cancel (5855794576957267334) -->
- <skip />
- <!-- no translation found for button_open (5445014062438566842) -->
- <skip />
- <!-- no translation found for desc_password (6636473611443746739) -->
- <skip />
- <!-- no translation found for desc_password_incorrect (4718823067483963845) -->
- <skip />
- <!-- no translation found for desc_password_incorrect_message (4849541619951023025) -->
- <skip />
+ <string name="title_dialog_password" msgid="2018068413709926925">"Leli fayela livikelwe"</string>
+ <string name="label_password_first" msgid="4456258714097111908">"Iphasiwedi"</string>
+ <string name="label_password_incorrect" msgid="8449142641187704667">"Iphasiwedi ayilungile"</string>
+ <string name="button_cancel" msgid="5855794576957267334">"Khansela"</string>
+ <string name="button_open" msgid="5445014062438566842">"Vula"</string>
+ <string name="desc_password" msgid="6636473611443746739">"inkambu yephasiwedi"</string>
+ <string name="desc_password_incorrect" msgid="4718823067483963845">"iphasiwedi ayilungile"</string>
+ <string name="desc_password_incorrect_message" msgid="4849541619951023025">"iphasiwedi ayilungile"</string>
<string name="label_page_single" msgid="456123685879261101">"<xliff:g id="PAGE">%1$d</xliff:g> / <xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_page_single" msgid="8459583146661044094">"ikhasi <xliff:g id="PAGE">%1$d</xliff:g> kwangu-<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="desc_zoom" msgid="7318480946145947242">"sondeza iphesenti elingu-<xliff:g id="FIRST">%1$d</xliff:g>"</string>
- <!-- no translation found for desc_goto_link (2461368384824849714) -->
- <skip />
+ <string name="desc_goto_link" msgid="2461368384824849714">"Iya ekhasini <xliff:g id="PAGE_NUMBER">%1$d</xliff:g>"</string>
<string name="desc_page" msgid="5684226167093594168">"ikhasi <xliff:g id="PAGE">%1$d</xliff:g>"</string>
- <!-- no translation found for message_select_text_to_comment (5725327644007067522) -->
- <skip />
- <!-- no translation found for message_tap_to_comment (7820801719181709999) -->
- <skip />
- <!-- no translation found for action_cancel (5494417739210197522) -->
- <skip />
- <!-- no translation found for error_file_format_pdf (7567006188638831878) -->
- <skip />
- <!-- no translation found for error_on_page (1592475819957182385) -->
- <skip />
- <!-- no translation found for annotation_mode_failed_to_open (1659648756255912463) -->
- <skip />
- <!-- no translation found for desc_web_link_shortened_to_domain (3323639528531061592) -->
- <skip />
- <!-- no translation found for desc_web_link (2776023299237058419) -->
- <skip />
- <!-- no translation found for desc_email_link (7027325672358507448) -->
- <skip />
- <!-- no translation found for desc_phone_link (4986414958429253135) -->
- <skip />
+ <string name="message_select_text_to_comment" msgid="5725327644007067522">"Khetha umbhalo ukuze ubeke amazwana akho"</string>
+ <string name="message_tap_to_comment" msgid="7820801719181709999">"Thepha indawo ukuze ubeke amazwana kuyo"</string>
+ <string name="action_cancel" msgid="5494417739210197522">"Khansela"</string>
+ <string name="error_file_format_pdf" msgid="7567006188638831878">"Ayikwazi ukubonisa i-PDF (i-<xliff:g id="TITLE">%1$s</xliff:g> ingeyefomethi engavumelekile)"</string>
+ <string name="error_on_page" msgid="1592475819957182385">"Ayikwazi ukubonisa ikhasi elingu-<xliff:g id="PAGE">%1$d</xliff:g> (iphutha lefayela)"</string>
+ <string name="annotation_mode_failed_to_open" msgid="1659648756255912463">"Ayikwazi ukulayisha imodi yesichasiselo yale nto."</string>
+ <string name="desc_web_link_shortened_to_domain" msgid="3323639528531061592">"Ilinki: ikhasi lewebhu ku-<xliff:g id="DESTINATION_DOMAIN">%1$s</xliff:g>"</string>
+ <string name="desc_web_link" msgid="2776023299237058419">"Ilinki: <xliff:g id="DESTINATION">%1$s</xliff:g>"</string>
+ <string name="desc_email_link" msgid="7027325672358507448">"I-imeyili: <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
+ <string name="desc_phone_link" msgid="4986414958429253135">"Ifoni: <xliff:g id="PHONE_NUMBER">%1$s</xliff:g>"</string>
<string name="desc_selection_start" msgid="3249210022376857070">"isiqalo sokukhetha"</string>
<string name="desc_selection_stop" msgid="2690168835536726898">"isiphetho sokukhetha"</string>
<string name="label_page_range" msgid="8290964180158460076">"<xliff:g id="FIRST">%1$d</xliff:g>-<xliff:g id="LAST">%2$d</xliff:g> / <xliff:g id="TOTAL">%3$d</xliff:g>"</string>
diff --git a/playground-common/playground-plugin/src/main/kotlin/androidx/playground/PlaygroundExtension.kt b/playground-common/playground-plugin/src/main/kotlin/androidx/playground/PlaygroundExtension.kt
index 9c8e31f..74144d5 100644
--- a/playground-common/playground-plugin/src/main/kotlin/androidx/playground/PlaygroundExtension.kt
+++ b/playground-common/playground-plugin/src/main/kotlin/androidx/playground/PlaygroundExtension.kt
@@ -111,6 +111,7 @@
val propertiesFile = File(supportRoot, "playground-common/playground.properties")
playgroundProperties.load(propertiesFile.inputStream())
settings.gradle.beforeProject { project ->
+ project.extensions.extraProperties["supportRootFolder"] = supportRoot
// load playground properties. These are not kept in the playground projects to prevent
// AndroidX build from reading them.
playgroundProperties.forEach {
@@ -142,7 +143,7 @@
}
val supportSettingsFile = File(supportRootDir, "settings.gradle")
val playgroundProjectDependencyGraph =
- ProjectDependencyGraph(settings, true /*isPlayground*/)
+ ProjectDependencyGraph(settings, true /*isPlayground*/, false /*constraintsEnabled*/)
// also get full graph that treats projectOrArtifact equal to project
val aospProjectDependencyGraph = ProjectDependencyGraph(settings, false /*isPlayground*/)
diff --git a/playground-common/playground.properties b/playground-common/playground.properties
index 390858c..5503e198 100644
--- a/playground-common/playground.properties
+++ b/playground-common/playground.properties
@@ -26,5 +26,5 @@
# Disable docs
androidx.enableDocumentation=false
androidx.playground.snapshotBuildId=11349412
-androidx.playground.metalavaBuildId=11659063
+androidx.playground.metalavaBuildId=11717657
androidx.studio.type=playground
\ No newline at end of file
diff --git a/room/integration-tests/multiplatformtestapp/schemas-ksp/androidx.room.integration.multiplatformtestapp.test.BaseAutoMigrationTest.AutoMigrationDatabase/3.json b/room/integration-tests/multiplatformtestapp/schemas-ksp/androidx.room.integration.multiplatformtestapp.test.BaseAutoMigrationTest.AutoMigrationDatabase/3.json
new file mode 100644
index 0000000..08c9605
--- /dev/null
+++ b/room/integration-tests/multiplatformtestapp/schemas-ksp/androidx.room.integration.multiplatformtestapp.test.BaseAutoMigrationTest.AutoMigrationDatabase/3.json
@@ -0,0 +1,45 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 3,
+ "identityHash": "a6f449a18d75d8cad5ca340d6f7f30e2",
+ "entities": [
+ {
+ "tableName": "AutoMigrationEntity",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`pk` INTEGER NOT NULL, `data` INTEGER NOT NULL DEFAULT 0, `moreData` TEXT NOT NULL DEFAULT '', PRIMARY KEY(`pk`))",
+ "fields": [
+ {
+ "fieldPath": "pk",
+ "columnName": "pk",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "data",
+ "columnName": "data",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ },
+ {
+ "fieldPath": "moreData",
+ "columnName": "moreData",
+ "affinity": "TEXT",
+ "notNull": true,
+ "defaultValue": "''"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "pk"
+ ]
+ }
+ }
+ ],
+ "setupQueries": [
+ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'a6f449a18d75d8cad5ca340d6f7f30e2')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/room/integration-tests/multiplatformtestapp/schemas-ksp/androidx.room.integration.multiplatformtestapp.test.BaseMigrationTest.MigrationDatabase/1.json b/room/integration-tests/multiplatformtestapp/schemas-ksp/androidx.room.integration.multiplatformtestapp.test.BaseMigrationTest.MigrationDatabase/1.json
new file mode 100644
index 0000000..18dd2b7
--- /dev/null
+++ b/room/integration-tests/multiplatformtestapp/schemas-ksp/androidx.room.integration.multiplatformtestapp.test.BaseMigrationTest.MigrationDatabase/1.json
@@ -0,0 +1,31 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 1,
+ "identityHash": "f530cb775d66c33d8b3cb1abaf2efd74",
+ "entities": [
+ {
+ "tableName": "MigrationEntity",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`pk` INTEGER NOT NULL, PRIMARY KEY(`pk`))",
+ "fields": [
+ {
+ "fieldPath": "pk",
+ "columnName": "pk",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "pk"
+ ]
+ }
+ }
+ ],
+ "setupQueries": [
+ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f530cb775d66c33d8b3cb1abaf2efd74')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/room/integration-tests/multiplatformtestapp/schemas-ksp/androidx.room.integration.multiplatformtestapp.test.BaseMigrationTest.MigrationDatabase/2.json b/room/integration-tests/multiplatformtestapp/schemas-ksp/androidx.room.integration.multiplatformtestapp.test.BaseMigrationTest.MigrationDatabase/2.json
new file mode 100644
index 0000000..bc57e8c
--- /dev/null
+++ b/room/integration-tests/multiplatformtestapp/schemas-ksp/androidx.room.integration.multiplatformtestapp.test.BaseMigrationTest.MigrationDatabase/2.json
@@ -0,0 +1,36 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 2,
+ "identityHash": "0062aa551ade7dc88d151a9bd43592c0",
+ "entities": [
+ {
+ "tableName": "MigrationEntity",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`pk` INTEGER NOT NULL, `addedInV2` TEXT, PRIMARY KEY(`pk`))",
+ "fields": [
+ {
+ "fieldPath": "pk",
+ "columnName": "pk",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "addedInV2",
+ "columnName": "addedInV2",
+ "affinity": "TEXT"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "pk"
+ ]
+ }
+ }
+ ],
+ "setupQueries": [
+ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '0062aa551ade7dc88d151a9bd43592c0')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/room/integration-tests/multiplatformtestapp/schemas-ksp/androidx.room.integration.multiplatformtestapp.test.BaseMigrationTest.MigrationDatabase/99.json b/room/integration-tests/multiplatformtestapp/schemas-ksp/androidx.room.integration.multiplatformtestapp.test.BaseMigrationTest.MigrationDatabase/99.json
new file mode 100644
index 0000000..26d59cd
--- /dev/null
+++ b/room/integration-tests/multiplatformtestapp/schemas-ksp/androidx.room.integration.multiplatformtestapp.test.BaseMigrationTest.MigrationDatabase/99.json
@@ -0,0 +1,36 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 99,
+ "identityHash": "0062aa551ade7dc88d151a9bd43592c0",
+ "entities": [
+ {
+ "tableName": "MigrationEntity",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`pk` INTEGER NOT NULL, `addedInV2` TEXT, PRIMARY KEY(`pk`))",
+ "fields": [
+ {
+ "fieldPath": "pk",
+ "columnName": "pk",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "addedInV2",
+ "columnName": "addedInV2",
+ "affinity": "TEXT"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "pk"
+ ]
+ }
+ }
+ ],
+ "setupQueries": [
+ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '0062aa551ade7dc88d151a9bd43592c0')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/room/integration-tests/multiplatformtestapp/src/androidInstrumentedTest/kotlin/androidx/room/integration/multiplatformtestapp/test/AutoMigrationTest.kt b/room/integration-tests/multiplatformtestapp/src/androidInstrumentedTest/kotlin/androidx/room/integration/multiplatformtestapp/test/AutoMigrationTest.kt
index a8c82ee..c308918 100644
--- a/room/integration-tests/multiplatformtestapp/src/androidInstrumentedTest/kotlin/androidx/room/integration/multiplatformtestapp/test/AutoMigrationTest.kt
+++ b/room/integration-tests/multiplatformtestapp/src/androidInstrumentedTest/kotlin/androidx/room/integration/multiplatformtestapp/test/AutoMigrationTest.kt
@@ -17,18 +17,14 @@
package androidx.room.integration.multiplatformtestapp.test
import androidx.kruth.assertThat
-import androidx.kruth.assertThrows
import androidx.room.Room
-import androidx.room.migration.Migration
+import androidx.room.RoomDatabase
import androidx.room.testing.MigrationTestHelper
import androidx.sqlite.SQLiteDriver
-import androidx.sqlite.db.SupportSQLiteDatabase
import androidx.sqlite.driver.bundled.BundledSQLiteDriver
import androidx.test.platform.app.InstrumentationRegistry
import kotlin.test.AfterTest
import kotlin.test.BeforeTest
-import kotlin.test.Test
-import kotlinx.coroutines.test.runTest
import org.junit.Rule
class AutoMigrationTest : BaseAutoMigrationTest() {
@@ -41,35 +37,17 @@
instrumentation = instrumentation,
driver = driver,
databaseClass = AutoMigrationDatabase::class,
- file = file
+ file = file,
+ autoMigrationSpecs = listOf(ProvidedSpecFrom2To3())
)
override fun getTestHelper() = migrationTestHelper
- override fun getRoomDatabase(): AutoMigrationDatabase {
+ override fun getDatabaseBuilder(): RoomDatabase.Builder<AutoMigrationDatabase> {
return Room.databaseBuilder<AutoMigrationDatabase>(
context = instrumentation.targetContext,
name = file.path
- ).setDriver(driver).build()
- }
-
- @Test
- fun migrationWithWrongOverride() = runTest {
- // Create database in V1
- val connection = migrationTestHelper.createDatabase(1)
- connection.close()
-
- // Auto migrate to V2
- val v2Db = Room.databaseBuilder<AutoMigrationDatabase>(
- context = instrumentation.targetContext,
- name = file.path
- ).setDriver(driver).addMigrations(object : Migration(1, 2) {
- override fun migrate(db: SupportSQLiteDatabase) {} }
- ).build()
- assertThrows<NotImplementedError> {
- v2Db.dao().insert(AutoMigrationEntity(1, 1))
- }
- v2Db.close()
+ ).setDriver(driver)
}
@BeforeTest
diff --git a/room/integration-tests/multiplatformtestapp/src/androidInstrumentedTest/kotlin/androidx/room/integration/multiplatformtestapp/test/MigrationTest.kt b/room/integration-tests/multiplatformtestapp/src/androidInstrumentedTest/kotlin/androidx/room/integration/multiplatformtestapp/test/MigrationTest.kt
new file mode 100644
index 0000000..63a17f7
--- /dev/null
+++ b/room/integration-tests/multiplatformtestapp/src/androidInstrumentedTest/kotlin/androidx/room/integration/multiplatformtestapp/test/MigrationTest.kt
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.integration.multiplatformtestapp.test
+
+import androidx.kruth.assertThat
+import androidx.kruth.assertThrows
+import androidx.room.Room
+import androidx.room.RoomDatabase
+import androidx.room.migration.Migration
+import androidx.room.testing.MigrationTestHelper
+import androidx.sqlite.SQLiteDriver
+import androidx.sqlite.db.SupportSQLiteDatabase
+import androidx.sqlite.driver.bundled.BundledSQLiteDriver
+import androidx.test.platform.app.InstrumentationRegistry
+import kotlin.test.AfterTest
+import kotlin.test.BeforeTest
+import kotlinx.coroutines.test.runTest
+import org.junit.Rule
+import org.junit.Test
+
+class MigrationTest : BaseMigrationTest() {
+ private val instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val file = instrumentation.targetContext.getDatabasePath("test.db")
+ private val driver: SQLiteDriver = BundledSQLiteDriver()
+
+ @get:Rule
+ val migrationTestHelper = MigrationTestHelper(
+ instrumentation = instrumentation,
+ driver = driver,
+ databaseClass = MigrationDatabase::class,
+ file = file
+ )
+
+ override fun getTestHelper() = migrationTestHelper
+
+ override fun getDatabaseBuilder(): RoomDatabase.Builder<MigrationDatabase> {
+ return Room.databaseBuilder<MigrationDatabase>(
+ context = instrumentation.targetContext,
+ name = file.path
+ ).setDriver(driver)
+ }
+
+ @Test
+ fun migrationWithWrongOverride() = runTest {
+ // Create database in V1
+ val connection = migrationTestHelper.createDatabase(1)
+ connection.close()
+
+ // Create database with a migration overriding the wrong function
+ val v2Db = Room.databaseBuilder<MigrationDatabase>(
+ context = instrumentation.targetContext,
+ name = file.path
+ ).setDriver(driver).addMigrations(
+ object : Migration(1, 2) {
+ override fun migrate(db: SupportSQLiteDatabase) {}
+ }
+ ).build()
+ // Expect failure due to database being configured with driver but migration object is
+ // overriding SupportSQLite* version.
+ assertThrows<NotImplementedError> {
+ v2Db.dao().getSingleItem(1)
+ }.hasMessageThat().isEqualTo(
+ "Migration functionality with a provided SQLiteDriver requires overriding the " +
+ "migrate(SQLiteConnection) function."
+ )
+ v2Db.close()
+ }
+
+ @BeforeTest
+ fun before() {
+ assertThat(file).isNotNull()
+ file.parentFile?.mkdirs()
+ deleteDatabaseFile()
+ }
+
+ @AfterTest
+ fun after() {
+ deleteDatabaseFile()
+ }
+
+ private fun deleteDatabaseFile() {
+ instrumentation.targetContext.deleteDatabase(file.name)
+ }
+}
diff --git a/room/integration-tests/multiplatformtestapp/src/androidInstrumentedTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SimpleQueryTest.kt b/room/integration-tests/multiplatformtestapp/src/androidInstrumentedTest/kotlin/androidx/room/integration/multiplatformtestapp/test/QueryTest.kt
similarity index 96%
rename from room/integration-tests/multiplatformtestapp/src/androidInstrumentedTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SimpleQueryTest.kt
rename to room/integration-tests/multiplatformtestapp/src/androidInstrumentedTest/kotlin/androidx/room/integration/multiplatformtestapp/test/QueryTest.kt
index 300ff8c..765680c 100644
--- a/room/integration-tests/multiplatformtestapp/src/androidInstrumentedTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SimpleQueryTest.kt
+++ b/room/integration-tests/multiplatformtestapp/src/androidInstrumentedTest/kotlin/androidx/room/integration/multiplatformtestapp/test/QueryTest.kt
@@ -21,7 +21,7 @@
import androidx.test.platform.app.InstrumentationRegistry
import kotlinx.coroutines.Dispatchers
-class SimpleQueryTest : BaseSimpleQueryTest() {
+class QueryTest : BaseQueryTest() {
private val instrumentation = InstrumentationRegistry.getInstrumentation()
diff --git a/room/integration-tests/multiplatformtestapp/src/androidInstrumentedTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SimpleQueryTest.kt b/room/integration-tests/multiplatformtestapp/src/androidInstrumentedTest/kotlin/androidx/room/integration/multiplatformtestapp/test/TypeConverterTest.kt
similarity index 78%
copy from room/integration-tests/multiplatformtestapp/src/androidInstrumentedTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SimpleQueryTest.kt
copy to room/integration-tests/multiplatformtestapp/src/androidInstrumentedTest/kotlin/androidx/room/integration/multiplatformtestapp/test/TypeConverterTest.kt
index 300ff8c..d89288c 100644
--- a/room/integration-tests/multiplatformtestapp/src/androidInstrumentedTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SimpleQueryTest.kt
+++ b/room/integration-tests/multiplatformtestapp/src/androidInstrumentedTest/kotlin/androidx/room/integration/multiplatformtestapp/test/TypeConverterTest.kt
@@ -17,19 +17,17 @@
package androidx.room.integration.multiplatformtestapp.test
import androidx.room.Room
+import androidx.room.RoomDatabase
import androidx.sqlite.driver.bundled.BundledSQLiteDriver
import androidx.test.platform.app.InstrumentationRegistry
-import kotlinx.coroutines.Dispatchers
-class SimpleQueryTest : BaseSimpleQueryTest() {
+class TypeConverterTest : BaseTypeConverterTest() {
private val instrumentation = InstrumentationRegistry.getInstrumentation()
- override fun getRoomDatabase(): SampleDatabase {
- return Room.inMemoryDatabaseBuilder<SampleDatabase>(
+ override fun getDatabaseBuilder(): RoomDatabase.Builder<TestDatabase> {
+ return Room.inMemoryDatabaseBuilder<TestDatabase>(
context = instrumentation.targetContext
).setDriver(BundledSQLiteDriver())
- .setQueryCoroutineContext(Dispatchers.IO)
- .build()
}
}
diff --git a/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseAutoMigrationTest.kt b/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseAutoMigrationTest.kt
index a5a991a..221b7d5 100644
--- a/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseAutoMigrationTest.kt
+++ b/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseAutoMigrationTest.kt
@@ -25,21 +25,22 @@
import androidx.room.Entity
import androidx.room.Insert
import androidx.room.PrimaryKey
+import androidx.room.ProvidedAutoMigrationSpec
import androidx.room.Query
import androidx.room.RoomDatabase
-import androidx.room.Update
+import androidx.room.migration.AutoMigrationSpec
import androidx.room.testing.MigrationTestHelper
+import androidx.sqlite.SQLiteConnection
+import androidx.sqlite.execSQL
import androidx.sqlite.use
-import kotlin.test.Ignore
import kotlin.test.Test
import kotlinx.coroutines.test.runTest
abstract class BaseAutoMigrationTest {
abstract fun getTestHelper(): MigrationTestHelper
- abstract fun getRoomDatabase(): AutoMigrationDatabase
+ abstract fun getDatabaseBuilder(): RoomDatabase.Builder<AutoMigrationDatabase>
@Test
- @Ignore // TODO (b/331622149) Investigate, there seems to be an issue in native.
fun migrateFromV1ToLatest() = runTest {
val migrationTestHelper = getTestHelper()
@@ -61,11 +62,14 @@
connection.close()
// Auto migrate to latest
- val dbVersion2 = getRoomDatabase()
- assertThat(dbVersion2.dao().update(AutoMigrationEntity(1, 5))).isEqualTo(1)
- assertThat(dbVersion2.dao().getSingleItem().pk).isEqualTo(1)
- assertThat(dbVersion2.dao().getSingleItem().data).isEqualTo(5)
- dbVersion2.close()
+ val dbVersion3 = getDatabaseBuilder()
+ .addAutoMigrationSpec(ProvidedSpecFrom2To3())
+ .build()
+ val dao = dbVersion3.dao()
+ assertThat(dao.getSingleItem().pk).isEqualTo(1)
+ assertThat(dao.getSingleItem().data).isEqualTo(0)
+ assertThat(dao.getSingleItem().moreData).isEqualTo("5")
+ dbVersion3.close()
}
@Test
@@ -73,47 +77,41 @@
val migrationTestHelper = getTestHelper()
// Create database V1
- val connection = migrationTestHelper.createDatabase(1)
+ val connectionV1 = migrationTestHelper.createDatabase(1)
// Insert some data, we'll validate it is present after migration
- connection.prepare("INSERT INTO AutoMigrationEntity (pk) VALUES (?)").use {
+ connectionV1.prepare("INSERT INTO AutoMigrationEntity (pk) VALUES (?)").use {
it.bindLong(1, 1)
assertThat(it.step()).isFalse() // SQLITE_DONE
}
- connection.close()
+ connectionV1.close()
- // Auto migrate to V2
- migrationTestHelper.runMigrationsAndValidate(2)
+ // Auto migrate to V2 and validate data is still present
+ val connectionV2 = migrationTestHelper.runMigrationsAndValidate(2)
+ connectionV2.prepare("SELECT count(*) FROM AutoMigrationEntity").use {
+ assertThat(it.step()).isTrue() // SQLITE_ROW
+ assertThat(it.getInt(0)).isEqualTo(1)
+ }
+ connectionV2.close()
}
@Test
- fun misuseTestHelperAlreadyCreatedDatabase() {
- val migrationTestHelper = getTestHelper()
-
- // Create database V1
- migrationTestHelper.createDatabase(1).close()
-
- // When trying to create at V1 again, fail due to database file being already created.
- assertThrows<IllegalStateException> {
- migrationTestHelper.createDatabase(1)
- }.hasMessageThat()
- .contains("Creation of tables didn't occur while creating a new database.")
-
- // If trying to create at V2, migration will try to run and fail.
- assertThrows<IllegalStateException> {
- migrationTestHelper.createDatabase(2)
- }.hasMessageThat()
- .contains("A migration from 1 to 2 was required but not found.")
+ fun missingProvidedAutoMigrationSpec() {
+ assertThrows<IllegalArgumentException> {
+ getDatabaseBuilder().build()
+ }.hasMessageThat().contains(
+ "A required auto migration spec (${ProvidedSpecFrom2To3::class.qualifiedName}) is " +
+ "missing in the database configuration."
+ )
}
@Test
- fun misuseTestHelperMissingDatabaseForValidateMigrations() {
- val migrationTestHelper = getTestHelper()
-
- // Try to validate migrations, but fail due to no previous database created.
- assertThrows<IllegalStateException> {
- migrationTestHelper.runMigrationsAndValidate(2, emptyList())
- }.hasMessageThat()
- .contains("Creation of tables should never occur while validating migrations.")
+ fun extraProvidedAutoMigrationSpec() {
+ assertThrows<IllegalArgumentException> {
+ getDatabaseBuilder()
+ .addAutoMigrationSpec(ProvidedSpecFrom2To3())
+ .addAutoMigrationSpec(ExtraProvidedSpec())
+ .build()
+ }.hasMessageThat().contains("Unexpected auto migration specs found.")
}
@Entity
@@ -121,7 +119,9 @@
@PrimaryKey
val pk: Long,
@ColumnInfo(defaultValue = "0")
- val data: Long
+ val data: Long,
+ @ColumnInfo(defaultValue = "")
+ val moreData: String
)
@Dao
@@ -129,20 +129,29 @@
@Insert
suspend fun insert(entity: AutoMigrationEntity)
- @Update
- suspend fun update(entity: AutoMigrationEntity): Int
-
@Query("SELECT * FROM AutoMigrationEntity")
suspend fun getSingleItem(): AutoMigrationEntity
}
@Database(
entities = [AutoMigrationEntity::class],
- version = 2,
+ version = 3,
exportSchema = true,
- autoMigrations = [AutoMigration(from = 1, to = 2)]
+ autoMigrations = [
+ AutoMigration(from = 1, to = 2),
+ AutoMigration(from = 2, to = 3, spec = ProvidedSpecFrom2To3::class)
+ ]
)
abstract class AutoMigrationDatabase : RoomDatabase() {
abstract fun dao(): AutoMigrationDao
}
+
+ @ProvidedAutoMigrationSpec
+ open class ProvidedSpecFrom2To3 : AutoMigrationSpec {
+ override fun onPostMigrate(connection: SQLiteConnection) {
+ connection.execSQL("UPDATE AutoMigrationEntity SET moreData = '5'")
+ }
+ }
+
+ class ExtraProvidedSpec : AutoMigrationSpec
}
diff --git a/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseBuilderTest.kt b/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseBuilderTest.kt
index 90e3311..dea6886 100644
--- a/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseBuilderTest.kt
+++ b/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseBuilderTest.kt
@@ -19,9 +19,18 @@
import androidx.kruth.assertThat
import androidx.kruth.assertThrows
import androidx.room.RoomDatabase
+import androidx.room.useReaderConnection
import androidx.sqlite.SQLiteConnection
import kotlin.coroutines.EmptyCoroutineContext
import kotlin.test.Test
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.IO
+import kotlinx.coroutines.awaitAll
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.joinAll
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
abstract class BaseBuilderTest {
@@ -70,10 +79,87 @@
}
@Test
+ fun onOpenExactlyOnce() = runTest {
+ var onOpenInvoked = 0
+
+ val onOpenBlocker = CompletableDeferred<Unit>()
+ val database = getRoomDatabaseBuilder()
+ .addCallback(
+ object : RoomDatabase.Callback() {
+ // This onOpen callback will block database initialization until the
+ // onOpenLatch is released.
+ override fun onOpen(connection: SQLiteConnection) {
+ onOpenInvoked++
+ runBlocking { onOpenBlocker.await() }
+ }
+ }
+ )
+ .build()
+
+ // Start 4 concurrent coroutines that try to open the database and use its connections,
+ // initialization should be done exactly once
+ val launchBlockers = List(4) { CompletableDeferred<Unit>() }
+ val jobs = List(4) { index ->
+ launch(Dispatchers.IO) {
+ launchBlockers[index].complete(Unit)
+ database.useReaderConnection { }
+ }
+ }
+
+ // Wait all launch coroutines to start then release the latch
+ launchBlockers.awaitAll()
+ delay(100) // A bit more waiting so useReaderConnection reaches the exclusive lock
+ onOpenBlocker.complete(Unit)
+
+ jobs.joinAll()
+ database.close()
+
+ // Initialization should be done exactly once
+ assertThat(onOpenInvoked).isEqualTo(1)
+ }
+
+ @Test
+ fun onOpenRecursive() = runTest {
+ var database: SampleDatabase? = null
+ database = getRoomDatabaseBuilder()
+ .setQueryCoroutineContext(Dispatchers.Unconfined)
+ .addCallback(
+ object : RoomDatabase.Callback() {
+ // Use a bad open callback that will recursively try to open the database
+ // again, this is a user error.
+ override fun onOpen(connection: SQLiteConnection) {
+ runBlocking {
+ checkNotNull(database).dao().getItemList()
+ }
+ }
+ }
+ ).build()
+ assertThrows<IllegalStateException> {
+ database.dao().getItemList()
+ }.hasMessageThat().contains("Recursive database initialization detected.")
+ database.close()
+ }
+
+ @Test
fun setCoroutineContextWithoutDispatcher() {
assertThrows<IllegalArgumentException> {
getRoomDatabaseBuilder().setQueryCoroutineContext(EmptyCoroutineContext)
}.hasMessageThat()
.contains("It is required that the coroutine context contain a dispatcher.")
}
+
+ @Test
+ fun setJournalMode() = runTest {
+ val database = getRoomDatabaseBuilder()
+ .setJournalMode(RoomDatabase.JournalMode.TRUNCATE)
+ .build()
+ val journalMode = database.useReaderConnection { connection ->
+ connection.usePrepared("PRAGMA journal_mode") {
+ it.step()
+ it.getText(0)
+ }
+ }
+ assertThat(journalMode).isEqualTo("truncate")
+ database.close()
+ }
}
diff --git a/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseMigrationTest.kt b/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseMigrationTest.kt
new file mode 100644
index 0000000..8b7b4a2
--- /dev/null
+++ b/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseMigrationTest.kt
@@ -0,0 +1,250 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.integration.multiplatformtestapp.test
+
+import androidx.kruth.assertThat
+import androidx.kruth.assertThrows
+import androidx.room.Dao
+import androidx.room.Database
+import androidx.room.Entity
+import androidx.room.Insert
+import androidx.room.PrimaryKey
+import androidx.room.Query
+import androidx.room.RoomDatabase
+import androidx.room.migration.Migration
+import androidx.room.testing.MigrationTestHelper
+import androidx.sqlite.SQLiteConnection
+import androidx.sqlite.execSQL
+import androidx.sqlite.use
+import kotlin.test.Test
+import kotlin.test.assertNotNull
+import kotlin.test.assertNull
+import kotlinx.coroutines.test.runTest
+
+abstract class BaseMigrationTest {
+ abstract fun getTestHelper(): MigrationTestHelper
+ abstract fun getDatabaseBuilder(): RoomDatabase.Builder<MigrationDatabase>
+
+ @Test
+ fun migrateFromV1ToLatest() = runTest {
+ val migrationTestHelper = getTestHelper()
+
+ // Create database V1
+ val connection = migrationTestHelper.createDatabase(1)
+ // Insert some data, we'll validate it is present after migration
+ connection.prepare("INSERT INTO MigrationEntity (pk) VALUES (?)").use {
+ it.bindLong(1, 1)
+ assertThat(it.step()).isFalse() // SQLITE_DONE
+ }
+ connection.close()
+
+ // Migrate to latest
+ val dbVersion2 = getDatabaseBuilder()
+ .addMigrations(object : Migration(1, 2) {
+ override fun migrate(connection: SQLiteConnection) {
+ connection.execSQL("ALTER TABLE MigrationEntity ADD COLUMN addedInV2 TEXT")
+ }
+ })
+ .build()
+ val item = dbVersion2.dao().getSingleItem(1)
+ assertNotNull(item)
+ assertThat(item.pk).isEqualTo(1)
+ assertThat(item.addedInV2).isNull()
+ dbVersion2.close()
+ }
+
+ @Test
+ fun migrateFromV1ToV2() = runTest {
+ val migrationTestHelper = getTestHelper()
+
+ // Create database V1
+ val connectionV1 = migrationTestHelper.createDatabase(1)
+ // Insert some data, we'll validate it is present after migration
+ connectionV1.prepare("INSERT INTO MigrationEntity (pk) VALUES (?)").use {
+ it.bindLong(1, 1)
+ assertThat(it.step()).isFalse() // SQLITE_DONE
+ }
+ connectionV1.close()
+
+ val migration = object : Migration(1, 2) {
+ override fun migrate(connection: SQLiteConnection) {
+ connection.execSQL("ALTER TABLE MigrationEntity ADD COLUMN addedInV2 TEXT")
+ }
+ }
+ // Migrate to V2 and validate data is still present
+ val connectionV2 = migrationTestHelper.runMigrationsAndValidate(2, listOf(migration))
+ connectionV2.prepare("SELECT count(*) FROM MigrationEntity").use {
+ assertThat(it.step()).isTrue() // SQLITE_ROW
+ assertThat(it.getInt(0)).isEqualTo(1)
+ }
+ connectionV2.close()
+ }
+
+ @Test
+ fun missingMigration() = runTest {
+ val migrationTestHelper = getTestHelper()
+ // Create database V1
+ val connection = migrationTestHelper.createDatabase(1)
+ connection.close()
+
+ // Create database missing migration
+ val dbVersion2 = getDatabaseBuilder().build()
+ // Fail to migrate
+ assertThrows<IllegalStateException> {
+ dbVersion2.dao().getSingleItem(1)
+ }.hasMessageThat().contains("A migration from 1 to 2 was required but not found.")
+ dbVersion2.close()
+ }
+
+ @Test
+ fun invalidMigration() = runTest {
+ val migrationTestHelper = getTestHelper()
+ // Create database V1
+ val connection = migrationTestHelper.createDatabase(1)
+ connection.close()
+
+ // Create database with a migration that doesn't properly changes the schema
+ val dbVersion2 = getDatabaseBuilder()
+ .addMigrations(object : Migration(1, 2) {
+ override fun migrate(connection: SQLiteConnection) {}
+ })
+ .build()
+ // Fail to migrate
+ assertThrows<IllegalStateException> {
+ dbVersion2.dao().getSingleItem(1)
+ }.hasMessageThat().contains("Migration didn't properly handle: MigrationEntity")
+ dbVersion2.close()
+ }
+
+ @Test
+ fun destructiveMigration() = runTest {
+ val migrationTestHelper = getTestHelper()
+ // Create database V1
+ val connection = migrationTestHelper.createDatabase(1)
+ connection.close()
+
+ // Create database with destructive migrations enabled
+ val dbVersion2 = getDatabaseBuilder()
+ .fallbackToDestructiveMigration(dropAllTables = true)
+ .build()
+ // Migrate via fallback destructive deletion
+ val item = dbVersion2.dao().getSingleItem(1)
+ assertNull(item)
+ dbVersion2.close()
+ }
+
+ @Test
+ fun destructiveMigrationOnDowngrade() = runTest {
+ val migrationTestHelper = getTestHelper()
+ // Create database from a future far away
+ val connection = migrationTestHelper.createDatabase(99)
+ connection.close()
+
+ // Create database with destructive migrations on downgrade enabled
+ val dbVersion2 = getDatabaseBuilder()
+ .fallbackToDestructiveMigrationOnDowngrade(dropAllTables = true)
+ .build()
+ // Migrate via fallback destructive deletion
+ val item = dbVersion2.dao().getSingleItem(1)
+ assertNull(item)
+ dbVersion2.close()
+ }
+
+ @Test
+ fun destructiveMigrationFrom() = runTest {
+ val migrationTestHelper = getTestHelper()
+ // Create database V1
+ val connection = migrationTestHelper.createDatabase(1)
+ connection.close()
+
+ // Create database missing migration
+ val dbVersion2 = getDatabaseBuilder()
+ .fallbackToDestructiveMigrationFrom(dropAllTables = true, 1)
+ .build()
+ // Migrate via fallback destructive deletion
+ val item = dbVersion2.dao().getSingleItem(1)
+ assertNull(item)
+ dbVersion2.close()
+ }
+
+ @Test
+ fun invalidDestructiveMigrationFrom() {
+ // Create database with an invalid combination of destructive migration from
+ assertThrows<IllegalArgumentException> {
+ getDatabaseBuilder()
+ .addMigrations(object : Migration(1, 2) {})
+ .fallbackToDestructiveMigrationFrom(dropAllTables = true, 1)
+ .build()
+ }.hasMessageThat().contains("Inconsistency detected.")
+ }
+
+ @Test
+ fun misuseTestHelperAlreadyCreatedDatabase() {
+ val migrationTestHelper = getTestHelper()
+
+ // Create database V1
+ migrationTestHelper.createDatabase(1).close()
+
+ // When trying to create at V1 again, fail due to database file being already created.
+ assertThrows<IllegalStateException> {
+ migrationTestHelper.createDatabase(1)
+ }.hasMessageThat()
+ .contains("Creation of tables didn't occur while creating a new database.")
+
+ // If trying to create at V2, migration will try to run and fail.
+ assertThrows<IllegalStateException> {
+ migrationTestHelper.createDatabase(2)
+ }.hasMessageThat()
+ .contains("A migration from 1 to 2 was required but not found.")
+ }
+
+ @Test
+ fun misuseTestHelperMissingDatabaseForValidateMigrations() {
+ val migrationTestHelper = getTestHelper()
+
+ // Try to validate migrations, but fail due to no previous database created.
+ assertThrows<IllegalStateException> {
+ migrationTestHelper.runMigrationsAndValidate(2, emptyList())
+ }.hasMessageThat()
+ .contains("Creation of tables should never occur while validating migrations.")
+ }
+
+ @Entity
+ data class MigrationEntity(
+ @PrimaryKey
+ val pk: Long,
+ val addedInV2: String?
+ )
+
+ @Dao
+ interface MigrationDao {
+ @Insert
+ suspend fun insert(entity: MigrationEntity)
+
+ @Query("SELECT * FROM MigrationEntity WHERE pk = :pk")
+ suspend fun getSingleItem(pk: Long): MigrationEntity?
+ }
+
+ @Database(
+ entities = [MigrationEntity::class],
+ version = 2,
+ exportSchema = true,
+ )
+ abstract class MigrationDatabase : RoomDatabase() {
+ abstract fun dao(): MigrationDao
+ }
+}
diff --git a/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseSimpleQueryTest.kt b/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseQueryTest.kt
similarity index 90%
rename from room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseSimpleQueryTest.kt
rename to room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseQueryTest.kt
index 836f9c4..1eb28cf 100644
--- a/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseSimpleQueryTest.kt
+++ b/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseQueryTest.kt
@@ -26,6 +26,7 @@
import kotlin.test.AfterTest
import kotlin.test.BeforeTest
import kotlin.test.Test
+import kotlin.test.assertContentEquals
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
@@ -37,7 +38,7 @@
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.yield
-abstract class BaseSimpleQueryTest {
+abstract class BaseQueryTest {
private lateinit var db: SampleDatabase
@@ -377,4 +378,42 @@
}
assertThat(db.dao().getItemList()).containsExactly(SampleEntity(1))
}
+
+ @Test
+ fun insertAndDeleteArray() = runTest {
+ val entityArray = arrayOf(
+ SampleEntity(1, 1),
+ SampleEntity(2, 2)
+ )
+ val dao = getRoomDatabase().dao()
+
+ dao.insertArray(entityArray)
+
+ val result = dao.getItemArray()
+ assertThat(result[0].pk).isEqualTo(1)
+ assertThat(result[1].pk).isEqualTo(2)
+
+ dao.deleteArray(entityArray)
+ assertThrows<IllegalStateException> {
+ dao.getSingleItemWithColumn()
+ }.hasMessageThat().contains("The query result was empty")
+ }
+
+ @Test
+ fun insertAndReadArrays() = runTest {
+ val expected = arrayOf(
+ SampleEntity(1, 1),
+ SampleEntity(2, 2)
+ )
+ val dao = getRoomDatabase().dao()
+ dao.insertArray(expected)
+
+ val resultArray = dao.queryOfArray()
+ val resultArrayWithLong = dao.queryOfArrayWithLong()
+ val resultLongArray = dao.queryOfLongArray()
+
+ assertContentEquals(expected, resultArray)
+ assertContentEquals(arrayOf(1, 2), resultArrayWithLong)
+ assertContentEquals(longArrayOf(1, 2), resultLongArray)
+ }
}
diff --git a/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseTypeConverterTest.kt b/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseTypeConverterTest.kt
new file mode 100644
index 0000000..93b06c4
--- /dev/null
+++ b/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/BaseTypeConverterTest.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.integration.multiplatformtestapp.test
+
+import androidx.kruth.assertThat
+import androidx.kruth.assertThrows
+import androidx.room.Dao
+import androidx.room.Database
+import androidx.room.Entity
+import androidx.room.Insert
+import androidx.room.PrimaryKey
+import androidx.room.ProvidedTypeConverter
+import androidx.room.Query
+import androidx.room.RoomDatabase
+import androidx.room.TypeConverter
+import androidx.room.TypeConverters
+import kotlin.test.Test
+import kotlinx.coroutines.test.runTest
+
+abstract class BaseTypeConverterTest {
+ abstract fun getDatabaseBuilder(): RoomDatabase.Builder<TestDatabase>
+
+ @Test
+ fun entityWithConverter() = runTest {
+ val database = getDatabaseBuilder()
+ .addTypeConverter(BarConverter())
+ .build()
+ val entity = TestEntity(1, Foo(1979), Bar("Mujer Boricua"))
+ database.getDao().insertItem(entity)
+ assertThat(database.getDao().getItem(1)).isEqualTo(entity)
+ database.close()
+ }
+
+ @Test
+ fun missingTypeConverter() {
+ assertThrows<IllegalArgumentException> {
+ getDatabaseBuilder().build()
+ }.hasMessageThat().isEqualTo("A required type converter (" +
+ "${BarConverter::class.qualifiedName}) for ${TestDao::class.qualifiedName} is " +
+ "missing in the database configuration.")
+ }
+
+ @Database(entities = [TestEntity::class], version = 1, exportSchema = false)
+ @TypeConverters(FooConverter::class, BarConverter::class)
+ abstract class TestDatabase : RoomDatabase() {
+ abstract fun getDao(): TestDao
+ }
+
+ @Dao
+ interface TestDao {
+ @Insert
+ suspend fun insertItem(item: TestEntity)
+
+ @Query("SELECT * FROM TestEntity WHERE id = :id")
+ suspend fun getItem(id: Long): TestEntity
+ }
+
+ @Entity
+ data class TestEntity(@PrimaryKey val id: Long, val foo: Foo, val bar: Bar)
+
+ data class Foo(val number: Int)
+
+ data class Bar(val text: String)
+
+ object FooConverter {
+ @TypeConverter
+ fun toFoo(number: Int): Foo = Foo(number)
+
+ @TypeConverter
+ fun fromFoo(foo: Foo): Int = foo.number
+ }
+
+ @ProvidedTypeConverter
+ class BarConverter {
+ @TypeConverter
+ fun toBar(text: String): Bar = Bar(text)
+
+ @TypeConverter
+ fun fromBar(bar: Bar): String = bar.text
+ }
+}
diff --git a/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SampleDatabase.kt b/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SampleDatabase.kt
index 1deccba..72420e1 100644
--- a/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SampleDatabase.kt
+++ b/room/integration-tests/multiplatformtestapp/src/commonTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SampleDatabase.kt
@@ -80,6 +80,9 @@
suspend fun getItemList(): List<SampleEntity>
@Query("SELECT * FROM SampleEntity")
+ suspend fun getItemArray(): Array<SampleEntity>
+
+ @Query("SELECT * FROM SampleEntity")
fun getItemListFlow(): Flow<List<SampleEntity>>
@Transaction
@@ -88,6 +91,12 @@
pks.forEach { deleteItem(it) }
}
+ @Transaction
+ suspend fun deleteArray(entities: Array<SampleEntity>, withError: Boolean = false) {
+ require(!withError)
+ entities.forEach { delete(it) }
+ }
+
@Query("SELECT * FROM SampleEntity")
suspend fun getSingleItemWithColumn(): SampleEntity
@@ -123,6 +132,9 @@
suspend fun insert(entity: SampleEntity)
@Insert
+ suspend fun insertArray(entities: Array<SampleEntity>)
+
+ @Insert
suspend fun insert(entity: SampleEntity2)
@Insert
@@ -139,6 +151,15 @@
@Update
suspend fun update(entity: SampleEntity)
+
+ @Query("SELECT * FROM SampleEntity")
+ suspend fun queryOfArray(): Array<SampleEntity>
+
+ @Query("SELECT pk FROM SampleEntity")
+ suspend fun queryOfArrayWithLong(): Array<Long>
+
+ @Query("SELECT pk FROM SampleEntity")
+ suspend fun queryOfLongArray(): LongArray
}
@Database(
diff --git a/room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/AutoMigrationTest.kt b/room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/AutoMigrationTest.kt
index 3e36969..2423be8 100644
--- a/room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/AutoMigrationTest.kt
+++ b/room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/AutoMigrationTest.kt
@@ -17,6 +17,7 @@
package androidx.room.integration.multiplatformtestapp.test
import androidx.room.Room
+import androidx.room.RoomDatabase
import androidx.room.testing.MigrationTestHelper
import androidx.sqlite.SQLiteDriver
import androidx.sqlite.driver.bundled.BundledSQLiteDriver
@@ -33,13 +34,14 @@
schemaDirectoryPath = Path("schemas-ksp"),
databasePath = tempFilePath,
driver = driver,
- databaseClass = AutoMigrationDatabase::class
+ databaseClass = AutoMigrationDatabase::class,
+ autoMigrationSpecs = listOf(ProvidedSpecFrom2To3())
)
override fun getTestHelper() = migrationTestHelper
- override fun getRoomDatabase(): AutoMigrationDatabase {
+ override fun getDatabaseBuilder(): RoomDatabase.Builder<AutoMigrationDatabase> {
return Room.databaseBuilder<AutoMigrationDatabase>(tempFilePath.toString())
- .setDriver(driver).build()
+ .setDriver(driver)
}
}
diff --git a/room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/MigrationTest.kt b/room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/MigrationTest.kt
new file mode 100644
index 0000000..017fdac
--- /dev/null
+++ b/room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/MigrationTest.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.integration.multiplatformtestapp.test
+
+import androidx.room.Room
+import androidx.room.RoomDatabase
+import androidx.room.testing.MigrationTestHelper
+import androidx.sqlite.SQLiteDriver
+import androidx.sqlite.driver.bundled.BundledSQLiteDriver
+import kotlin.io.path.Path
+import kotlin.io.path.createTempFile
+import org.junit.Rule
+
+class MigrationTest : BaseMigrationTest() {
+ private val tempFilePath = createTempFile("test.db").also { it.toFile().deleteOnExit() }
+ private val driver: SQLiteDriver = BundledSQLiteDriver()
+
+ @get:Rule
+ val migrationTestHelper = MigrationTestHelper(
+ schemaDirectoryPath = Path("schemas-ksp"),
+ databasePath = tempFilePath,
+ driver = driver,
+ databaseClass = MigrationDatabase::class
+ )
+
+ override fun getTestHelper() = migrationTestHelper
+
+ override fun getDatabaseBuilder(): RoomDatabase.Builder<MigrationDatabase> {
+ return Room.databaseBuilder<MigrationDatabase>(tempFilePath.toString())
+ .setDriver(driver)
+ }
+}
diff --git a/room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SimpleQueryTest.kt b/room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/QueryTest.kt
similarity index 95%
rename from room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SimpleQueryTest.kt
rename to room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/QueryTest.kt
index 6be6358f..fa91c34 100644
--- a/room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SimpleQueryTest.kt
+++ b/room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/QueryTest.kt
@@ -20,7 +20,7 @@
import androidx.sqlite.driver.bundled.BundledSQLiteDriver
import kotlinx.coroutines.Dispatchers
-class SimpleQueryTest : BaseSimpleQueryTest() {
+class QueryTest : BaseQueryTest() {
override fun getRoomDatabase(): SampleDatabase {
return Room.inMemoryDatabaseBuilder<SampleDatabase>()
diff --git a/room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SimpleQueryTest.kt b/room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/TypeConverterTest.kt
similarity index 74%
copy from room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SimpleQueryTest.kt
copy to room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/TypeConverterTest.kt
index 6be6358f..35bdee5 100644
--- a/room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SimpleQueryTest.kt
+++ b/room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/TypeConverterTest.kt
@@ -17,15 +17,13 @@
package androidx.room.integration.multiplatformtestapp.test
import androidx.room.Room
+import androidx.room.RoomDatabase
import androidx.sqlite.driver.bundled.BundledSQLiteDriver
-import kotlinx.coroutines.Dispatchers
-class SimpleQueryTest : BaseSimpleQueryTest() {
+class TypeConverterTest : BaseTypeConverterTest() {
- override fun getRoomDatabase(): SampleDatabase {
- return Room.inMemoryDatabaseBuilder<SampleDatabase>()
+ override fun getDatabaseBuilder(): RoomDatabase.Builder<TestDatabase> {
+ return Room.inMemoryDatabaseBuilder<TestDatabase>()
.setDriver(BundledSQLiteDriver())
- .setQueryCoroutineContext(Dispatchers.IO)
- .build()
}
}
diff --git a/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/AutoMigrationTest.kt b/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/AutoMigrationTest.kt
index 4300bb2..0d349e8 100644
--- a/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/AutoMigrationTest.kt
+++ b/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/AutoMigrationTest.kt
@@ -17,6 +17,7 @@
package androidx.room.integration.multiplatformtestapp.test
import androidx.room.Room
+import androidx.room.RoomDatabase
import androidx.room.testing.MigrationTestHelper
import androidx.sqlite.SQLiteDriver
import androidx.sqlite.driver.bundled.BundledSQLiteDriver
@@ -29,21 +30,22 @@
private val filename = "/tmp/test-${Random.nextInt()}.db"
private val driver: SQLiteDriver = BundledSQLiteDriver()
+ private val dbFactory = { AutoMigrationDatabase::class.instantiateImpl() }
+
private val migrationTestHelper = MigrationTestHelper(
schemaDirectoryPath = getSchemaDirectoryPath(),
fileName = filename,
driver = driver,
databaseClass = AutoMigrationDatabase::class,
- databaseFactory = { AutoMigrationDatabase::class.instantiateImpl() }
+ databaseFactory = dbFactory,
+ autoMigrationSpecs = listOf(ProvidedSpecFrom2To3())
)
override fun getTestHelper() = migrationTestHelper
- override fun getRoomDatabase(): AutoMigrationDatabase {
- return Room.databaseBuilder<AutoMigrationDatabase>(filename) {
- AutoMigrationDatabase::class.instantiateImpl()
- }
- .setDriver(driver).build()
+ override fun getDatabaseBuilder(): RoomDatabase.Builder<AutoMigrationDatabase> {
+ return Room.databaseBuilder<AutoMigrationDatabase>(filename, dbFactory)
+ .setDriver(driver)
}
@BeforeTest
diff --git a/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/MigrationTest.kt b/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/MigrationTest.kt
new file mode 100644
index 0000000..bddb56a
--- /dev/null
+++ b/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/MigrationTest.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.integration.multiplatformtestapp.test
+
+import androidx.room.Room
+import androidx.room.RoomDatabase
+import androidx.room.testing.MigrationTestHelper
+import androidx.sqlite.SQLiteDriver
+import androidx.sqlite.driver.bundled.BundledSQLiteDriver
+import kotlin.random.Random
+import kotlin.test.AfterTest
+import kotlin.test.BeforeTest
+import platform.posix.remove
+
+class MigrationTest : BaseMigrationTest() {
+ private val filename = "/tmp/test-${Random.nextInt()}.db"
+ private val driver: SQLiteDriver = BundledSQLiteDriver()
+
+ private val dbFactory = { MigrationDatabase::class.instantiateImpl() }
+
+ private val migrationTestHelper = MigrationTestHelper(
+ schemaDirectoryPath = getSchemaDirectoryPath(),
+ fileName = filename,
+ driver = driver,
+ databaseClass = MigrationDatabase::class,
+ databaseFactory = dbFactory
+ )
+
+ override fun getTestHelper() = migrationTestHelper
+
+ override fun getDatabaseBuilder(): RoomDatabase.Builder<MigrationDatabase> {
+ return Room.databaseBuilder<MigrationDatabase>(filename, dbFactory)
+ .setDriver(driver)
+ }
+
+ @BeforeTest
+ fun before() {
+ deleteDatabaseFile()
+ }
+
+ @AfterTest
+ fun after() {
+ migrationTestHelper.finished()
+ deleteDatabaseFile()
+ }
+
+ private fun deleteDatabaseFile() {
+ remove(filename)
+ remove("$filename-wal")
+ remove("$filename-shm")
+ }
+}
diff --git a/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SimpleQueryTest.kt b/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/QueryTest.kt
similarity index 86%
rename from room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SimpleQueryTest.kt
rename to room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/QueryTest.kt
index 5a0d3c9..9ad0d65 100644
--- a/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SimpleQueryTest.kt
+++ b/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/QueryTest.kt
@@ -21,10 +21,12 @@
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.IO
-class SimpleQueryTest : BaseSimpleQueryTest() {
+class QueryTest : BaseQueryTest() {
override fun getRoomDatabase(): SampleDatabase {
- return Room.inMemoryDatabaseBuilder { SampleDatabase::class.instantiateImpl() }
+ return Room.inMemoryDatabaseBuilder<SampleDatabase> {
+ SampleDatabase::class.instantiateImpl()
+ }
.setDriver(BundledSQLiteDriver())
.setQueryCoroutineContext(Dispatchers.IO)
.build()
diff --git a/room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SimpleQueryTest.kt b/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/TypeConverterTest.kt
similarity index 74%
copy from room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SimpleQueryTest.kt
copy to room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/TypeConverterTest.kt
index 6be6358f..de89cdd 100644
--- a/room/integration-tests/multiplatformtestapp/src/jvmTest/kotlin/androidx/room/integration/multiplatformtestapp/test/SimpleQueryTest.kt
+++ b/room/integration-tests/multiplatformtestapp/src/nativeTest/kotlin/androidx/room/integration/multiplatformtestapp/test/TypeConverterTest.kt
@@ -17,15 +17,13 @@
package androidx.room.integration.multiplatformtestapp.test
import androidx.room.Room
+import androidx.room.RoomDatabase
import androidx.sqlite.driver.bundled.BundledSQLiteDriver
-import kotlinx.coroutines.Dispatchers
-class SimpleQueryTest : BaseSimpleQueryTest() {
+class TypeConverterTest : BaseTypeConverterTest() {
- override fun getRoomDatabase(): SampleDatabase {
- return Room.inMemoryDatabaseBuilder<SampleDatabase>()
+ override fun getDatabaseBuilder(): RoomDatabase.Builder<TestDatabase> {
+ return Room.inMemoryDatabaseBuilder<TestDatabase> { TestDatabase::class.instantiateImpl() }
.setDriver(BundledSQLiteDriver())
- .setQueryCoroutineContext(Dispatchers.IO)
- .build()
}
}
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspType.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspType.kt
index 8c0aeb1..18ce9fa 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspType.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspType.kt
@@ -72,8 +72,20 @@
*/
private val xTypeName: XTypeName by lazy {
val jvmWildcardType = env.resolveWildcards(typeAlias ?: ksType, scope).let {
- if (it == ksType) {
- this
+ if (ksType == it) {
+ if (ksType.arguments != it.arguments) {
+ // Replacing the type arguments to retain the variances resolved in
+ // `resolveWildcards`. See https://github.com/google/ksp/issues/1778.
+ copy(
+ env = env,
+ ksType = ksType.replace(it.arguments),
+ originalKSAnnotations = originalKSAnnotations,
+ scope = scope,
+ typeAlias = typeAlias
+ )
+ } else {
+ this
+ }
} else {
env.wrap(
ksType = it,
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt b/room/room-compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt
index 2cdf066..e879e60 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt
@@ -782,6 +782,10 @@
val INVALID_RELATION_IN_PARTIAL_ENTITY = "Partial entities cannot have relations."
+ fun invalidQueryForSingleColumnArray(returnType: String) = "If a DAO function has a " +
+ "primitive array or an array of String return type, a single column must be returned. " +
+ "Please check the query of the DAO function with the `$returnType` return type."
+
val EXPAND_PROJECTION_ALONG_WITH_REMOVE_UNUSED = """
Using @${RewriteQueriesToDropUnusedColumns::class.simpleName} annotation when
room.expandProjection compiler flag is enabled will disable expandProjection for queries
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/processor/TransactionMethodProcessor.kt b/room/room-compiler/src/main/kotlin/androidx/room/processor/TransactionMethodProcessor.kt
index 59ca629..1d59ad0 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/processor/TransactionMethodProcessor.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/processor/TransactionMethodProcessor.kt
@@ -44,16 +44,33 @@
val returnType = delegate.extractReturnType()
val rawReturnType = returnType.rawType
- DEFERRED_TYPES.firstOrNull { className ->
+ val deferredReturnTypeName = DEFERRED_TYPES.firstOrNull { className ->
context.processingEnv.findType(className.canonicalName)
?.rawType?.isAssignableFrom(rawReturnType) ?: false
- }?.let { returnTypeName ->
+ }
+ if (deferredReturnTypeName != null) {
context.logger.e(
- ProcessorErrors.transactionMethodAsync(returnTypeName.toString()),
+ ProcessorErrors.transactionMethodAsync(
+ deferredReturnTypeName.toString(context.codeLanguage)
+ ),
executableElement
)
}
+ val isSuspendFunction = delegate.executableElement.isSuspendFunction()
+ if (
+ !isSuspendFunction &&
+ deferredReturnTypeName == null &&
+ !context.isAndroidOnlyTarget()
+ ) {
+ // A blocking transaction wrapper function is not allowed if the target platforms
+ // include non-Android targets.
+ context.logger.e(
+ executableElement,
+ ProcessorErrors.INVALID_BLOCKING_DAO_FUNCTION_NON_ANDROID
+ )
+ }
+
val callType = when {
containingElement.isInterface() && executableElement.isJavaDefault() ->
TransactionMethod.CallType.DEFAULT_JAVA8
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt
index f398487..20b75c2 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt
@@ -44,6 +44,7 @@
import androidx.room.processor.PojoProcessor
import androidx.room.processor.ProcessorErrors
import androidx.room.processor.ProcessorErrors.DO_NOT_USE_GENERIC_IMMUTABLE_MULTIMAP
+import androidx.room.processor.ProcessorErrors.invalidQueryForSingleColumnArray
import androidx.room.solver.binderprovider.CoroutineFlowResultBinderProvider
import androidx.room.solver.binderprovider.CursorQueryResultBinderProvider
import androidx.room.solver.binderprovider.DataSourceFactoryQueryResultBinderProvider
@@ -507,15 +508,46 @@
// TODO: (b/192068912) Refactor the following since this if-else cascade has gotten large
if (typeMirror.isArray() && typeMirror.componentType.isNotByte()) {
+ val componentType = typeMirror.componentType
checkTypeNullability(
typeMirror,
extras,
"Array",
- arrayComponentType = typeMirror.componentType
+ arrayComponentType = componentType
)
- val rowAdapter =
- findRowAdapter(typeMirror.componentType, query) ?: return null
- return ArrayQueryResultAdapter(typeMirror, rowAdapter)
+ val isSingleColumnArray = componentType.asTypeName().isPrimitive ||
+ componentType.isTypeOf(String::class)
+ val queryResultInfo = query.resultInfo
+ if (
+ isSingleColumnArray &&
+ queryResultInfo != null &&
+ queryResultInfo.columns.size > 1
+ ) {
+ context.logger.e(
+ invalidQueryForSingleColumnArray(
+ typeMirror.asTypeName().toString(context.codeLanguage)
+ )
+ )
+ return null
+ }
+
+ // Create a type mirror for a regular List in order to use ListQueryResultAdapter. This
+ // avoids code duplication as an Array can be initialized using a list.
+ val listType = context.processingEnv.getDeclaredType(
+ context.processingEnv.requireTypeElement(List::class),
+ componentType.boxed().makeNonNullable()
+ ).makeNonNullable()
+
+ val listResultAdapter = findQueryResultAdapter(
+ typeMirror = listType,
+ query = query,
+ extras = extras
+ ) ?: return null
+
+ return ArrayQueryResultAdapter(
+ typeMirror,
+ listResultAdapter as ListQueryResultAdapter
+ )
} else if (typeMirror.typeArguments.isEmpty()) {
val rowAdapter = findRowAdapter(typeMirror, query) ?: return null
return SingleItemQueryResultAdapter(rowAdapter)
@@ -568,7 +600,6 @@
typeMirror,
extras
)
-
val typeArg = typeMirror.typeArguments.first().extendsBoundOrSelf()
val rowAdapter = findRowAdapter(typeArg, query) ?: return null
return ListQueryResultAdapter(
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/ArrayQueryResultAdapter.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/ArrayQueryResultAdapter.kt
index 35eb742..09d9d07 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/ArrayQueryResultAdapter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/ArrayQueryResultAdapter.kt
@@ -18,103 +18,106 @@
import androidx.room.compiler.codegen.CodeLanguage
import androidx.room.compiler.codegen.XCodeBlock
+import androidx.room.compiler.codegen.XCodeBlock.Builder.Companion.beginForEachControlFlow
import androidx.room.compiler.codegen.XTypeName
-import androidx.room.compiler.codegen.box
import androidx.room.compiler.processing.XArrayType
-import androidx.room.compiler.processing.XNullability
-import androidx.room.ext.KotlinCollectionMemberNames.ARRAY_OF_NULLS
import androidx.room.ext.getToArrayFunction
import androidx.room.solver.CodeGenScope
class ArrayQueryResultAdapter(
private val arrayType: XArrayType,
- private val rowAdapter: RowAdapter
-) : QueryResultAdapter(listOf(rowAdapter)) {
+ private val listResultAdapter: ListQueryResultAdapter
+) : QueryResultAdapter(listResultAdapter.rowAdapters) {
+ private val componentTypeName: XTypeName = arrayType.componentType.asTypeName()
+ private val arrayTypeName = XTypeName.getArrayName(componentTypeName)
+
override fun convert(outVarName: String, cursorVarName: String, scope: CodeGenScope) {
scope.builder.apply {
- rowAdapter.onCursorReady(cursorVarName = cursorVarName, scope = scope)
- val componentTypeName: XTypeName = arrayType.componentType.asTypeName()
- val arrayTypeName = XTypeName.getArrayName(componentTypeName)
+ val listVarName = scope.getTmpVar("_listResult")
+ // Delegate to the ListQueryResultAdapter to convert query result to a List.
+ listResultAdapter.convert(listVarName, cursorVarName, scope)
- // For Java, instantiate a new array of a size using the bracket syntax, for Kotlin
- // create the array using the std-lib function arrayOfNulls.
- val tmpResultName = scope.getTmpVar("_tmpResult")
- addLocalVariable(
- name = tmpResultName,
- typeName = XTypeName.getArrayName(componentTypeName.copy(nullable = true)),
- assignExpr = when (language) {
- CodeLanguage.KOTLIN ->
- XCodeBlock.of(
- language = language,
- format = "%M<%T>(%L.getCount())",
- ARRAY_OF_NULLS,
- componentTypeName,
- cursorVarName
- )
- CodeLanguage.JAVA ->
- XCodeBlock.of(
- language = language,
- format = "new %T[%L.getCount()]",
- componentTypeName,
- cursorVarName
- )
- }
- )
+ // Initialize _result to be returned, using the list result we have.
+ val tmpArrayResult = scope.getTmpVar("_tmpArrayResult")
- val tmpVarName = scope.getTmpVar("_item")
- val indexVar = scope.getTmpVar("_index")
- addLocalVariable(
- name = indexVar,
- typeName = XTypeName.PRIMITIVE_INT,
- assignExpr = XCodeBlock.of(language, "0"),
- isMutable = true
- )
- beginControlFlow("while (%L.moveToNext())", cursorVarName).apply {
- addLocalVariable(
- name = tmpVarName,
- typeName = componentTypeName
- )
- rowAdapter.convert(tmpVarName, cursorVarName, scope)
- addStatement("%L[%L] = %L", tmpResultName, indexVar, tmpVarName)
- addStatement("%L++", indexVar)
- }
- endControlFlow()
-
- // Finally initialize _result to be returned. Will avoid an unnecessary cast in
- // Kotlin if the Entity was already nullable.
val assignCode = XCodeBlock.of(
language = language,
format = "%L",
- tmpResultName
+ listVarName
).let {
- if (
- language == CodeLanguage.KOTLIN &&
- componentTypeName.nullability == XNullability.NONNULL
- ) {
- XCodeBlock.ofCast(
- language = language,
- typeName = XTypeName.getArrayName(componentTypeName.box()),
- expressionBlock = it
- )
- } else {
- it
- }
- }.let {
- // If the component is a primitive type and the language is Kotlin, we need to use
- // an additional built-in function to cast from the boxed to the primitive array
- // type, i.e. Array<Int> to IntArray.
- if (
- language == CodeLanguage.KOTLIN &&
- componentTypeName.isPrimitive
- ) {
- XCodeBlock.of(
- language = language,
- format = "(%L).%L",
- it,
- getToArrayFunction(componentTypeName)
- )
- } else {
- it
+ when (language) {
+ CodeLanguage.KOTLIN -> {
+ if (componentTypeName.isPrimitive) {
+ // If we have a primitive array like LongArray or ShortArray,
+ // we use conversion functions like toLongArray() or toShortArray().
+ XCodeBlock.of(
+ language = language,
+ format = "%L.%L",
+ it,
+ getToArrayFunction(componentTypeName)
+ )
+ } else {
+ XCodeBlock.of(
+ language = language,
+ format = "%L.%L",
+ it,
+ "toTypedArray()"
+ )
+ }
+ }
+ CodeLanguage.JAVA -> {
+ if (componentTypeName.isPrimitive) {
+ // In Java, initializing an Array using a List is not
+ // straightforward, and requires we create an empty array that will be
+ // initialized using the list contents.
+ addLocalVariable(
+ name = tmpArrayResult,
+ typeName = arrayTypeName,
+ assignExpr = XCodeBlock.of(
+ language = language,
+ format = "new %T[%L.size()]",
+ componentTypeName,
+ listVarName
+ )
+ )
+ // If the array is primitive, we have to loop over the list to copy
+ // contents, as we cannot use toArray() on primitive array types.
+ val indexVarName = scope.getTmpVar("_index")
+ addLocalVariable(
+ name = indexVarName,
+ typeName = componentTypeName,
+ isMutable = true,
+ assignExpr = XCodeBlock.of(language, "0")
+ )
+ val itrVar = scope.getTmpVar("_listItem")
+ beginForEachControlFlow(
+ iteratorVarName = listVarName,
+ typeName = componentTypeName,
+ itemVarName = itrVar
+ ).apply {
+ addStatement(
+ "%L[%L] = %L",
+ tmpArrayResult,
+ indexVarName,
+ itrVar
+ )
+ addStatement("%L++", indexVarName)
+ }.endControlFlow()
+ XCodeBlock.of(
+ language = language,
+ format = "%L",
+ tmpArrayResult
+ )
+ } else {
+ // If the array is not primitive, we use the List.toArray() utility.
+ XCodeBlock.of(
+ language = language,
+ format = "%L.toArray(new %T[0])",
+ listVarName,
+ componentTypeName
+ )
+ }
+ }
}
}
addLocalVariable(
@@ -124,4 +127,6 @@
)
}
}
+
+ override fun isMigratedToDriver(): Boolean = true
}
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/QueryMethodProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/QueryMethodProcessorTest.kt
index 636a827..e4e2382 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/QueryMethodProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/QueryMethodProcessorTest.kt
@@ -121,7 +121,7 @@
fun testReadNoParams() {
singleQueryMethod<ReadQueryMethod>(
"""
- @Query("SELECT * from User")
+ @Query("SELECT uid from User")
abstract public int[] foo();
"""
) { parsedQuery, _ ->
@@ -1980,4 +1980,46 @@
}
}
}
+
+ @Test
+ fun testStringArraySingleColumnQuery() {
+ if (!enableVerification) {
+ return
+ }
+ singleQueryMethod<ReadQueryMethod>(
+ """
+ @Query("select * from User")
+ abstract String[] stringArray();
+ """
+ ) { _, invocation ->
+ invocation.assertCompilationResult {
+ hasErrorContaining(
+ ProcessorErrors.invalidQueryForSingleColumnArray(
+ "java.lang.String[]"
+ )
+ )
+ }
+ }
+ }
+
+ @Test
+ fun testLongArraySingleColumnQuery() {
+ if (!enableVerification) {
+ return
+ }
+ singleQueryMethod<ReadQueryMethod>(
+ """
+ @Query("select * from User")
+ abstract long[] longArray();
+ """
+ ) { _, invocation ->
+ invocation.assertCompilationResult {
+ hasErrorContaining(
+ ProcessorErrors.invalidQueryForSingleColumnArray(
+ "long[]"
+ )
+ )
+ }
+ }
+ }
}
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/TransactionMethodProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/TransactionMethodProcessorTest.kt
index 8dd1492..10e43f7 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/TransactionMethodProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/TransactionMethodProcessorTest.kt
@@ -19,6 +19,7 @@
import COMMON
import androidx.room.Dao
import androidx.room.Transaction
+import androidx.room.compiler.codegen.CodeLanguage
import androidx.room.compiler.processing.XTypeElement
import androidx.room.compiler.processing.util.Source
import androidx.room.compiler.processing.util.XTestInvocation
@@ -114,7 +115,7 @@
invocation.assertCompilationResult {
hasErrorContaining(
ProcessorErrors.transactionMethodAsync(
- FLOW.rawTypeName.toString()
+ FLOW.rawTypeName.toString(CodeLanguage.JAVA)
)
)
}
@@ -133,7 +134,7 @@
invocation.assertCompilationResult {
hasErrorContaining(
ProcessorErrors.transactionMethodAsync(
- LIVE_DATA.rawTypeName.toString()
+ LIVE_DATA.rawTypeName.toString(CodeLanguage.JAVA)
)
)
}
@@ -152,7 +153,7 @@
invocation.assertCompilationResult {
hasErrorContaining(
ProcessorErrors.transactionMethodAsync(
- COMPUTABLE_LIVE_DATA.rawTypeName.toString()
+ COMPUTABLE_LIVE_DATA.rawTypeName.toString(CodeLanguage.JAVA)
)
)
}
@@ -171,7 +172,7 @@
invocation.assertCompilationResult {
hasErrorContaining(
ProcessorErrors.transactionMethodAsync(
- RxJava2TypeNames.FLOWABLE.rawTypeName.toString()
+ RxJava2TypeNames.FLOWABLE.rawTypeName.toString(CodeLanguage.JAVA)
)
)
}
@@ -192,7 +193,7 @@
invocation.assertCompilationResult {
hasErrorContaining(
ProcessorErrors.transactionMethodAsync(
- RxJava3TypeNames.FLOWABLE.rawTypeName.toString()
+ RxJava3TypeNames.FLOWABLE.rawTypeName.toString(CodeLanguage.JAVA)
)
)
}
@@ -211,7 +212,7 @@
invocation.assertCompilationResult {
hasErrorContaining(
ProcessorErrors.transactionMethodAsync(
- RxJava2TypeNames.COMPLETABLE.rawTypeName.toString()
+ RxJava2TypeNames.COMPLETABLE.rawTypeName.toString(CodeLanguage.JAVA)
)
)
}
@@ -232,7 +233,7 @@
invocation.assertCompilationResult {
hasErrorContaining(
ProcessorErrors.transactionMethodAsync(
- RxJava3TypeNames.COMPLETABLE.rawTypeName.toString()
+ RxJava3TypeNames.COMPLETABLE.rawTypeName.toString(CodeLanguage.JAVA)
)
)
}
@@ -251,7 +252,7 @@
invocation.assertCompilationResult {
hasErrorContaining(
ProcessorErrors.transactionMethodAsync(
- RxJava2TypeNames.SINGLE.rawTypeName.toString()
+ RxJava2TypeNames.SINGLE.rawTypeName.toString(CodeLanguage.JAVA)
)
)
}
@@ -272,7 +273,7 @@
invocation.assertCompilationResult {
hasErrorContaining(
ProcessorErrors.transactionMethodAsync(
- RxJava3TypeNames.SINGLE.rawTypeName.toString()
+ RxJava3TypeNames.SINGLE.rawTypeName.toString(CodeLanguage.JAVA)
)
)
}
@@ -291,7 +292,7 @@
invocation.assertCompilationResult {
hasErrorContaining(
ProcessorErrors.transactionMethodAsync(
- LISTENABLE_FUTURE.rawTypeName.toString()
+ LISTENABLE_FUTURE.rawTypeName.toString(CodeLanguage.JAVA)
)
)
}
@@ -310,7 +311,7 @@
invocation.assertCompilationResult {
hasErrorContaining(
ProcessorErrors.transactionMethodAsync(
- PUBLISHER.rawTypeName.toString()
+ PUBLISHER.rawTypeName.toString(CodeLanguage.JAVA)
)
)
}
@@ -336,7 +337,8 @@
COMMON.RX3_SINGLE, COMMON.LISTENABLE_FUTURE, COMMON.FLOW
)
runProcessorTest(
- sources = inputSource + otherSources
+ sources = inputSource + otherSources,
+ options = mapOf(Context.BooleanProcessorOptions.GENERATE_KOTLIN.argName to "false")
) { invocation ->
val (owner, methods) = invocation.roundEnv
.getElementsAnnotatedWith(Dao::class.qualifiedName!!)
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/writer/DaoKotlinCodeGenTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/writer/DaoKotlinCodeGenTest.kt
index 720724a..786c995 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/writer/DaoKotlinCodeGenTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/writer/DaoKotlinCodeGenTest.kt
@@ -1339,13 +1339,21 @@
@Query("SELECT * FROM MyEntity")
fun queryOfArray(): Array<MyEntity>
+ @Suppress(RoomWarnings.UNNECESSARY_NULLABILITY_IN_DAO_RETURN_TYPE)
+ @Query("SELECT * FROM MyEntity")
+ fun queryOfNullableArray(): Array<MyEntity?>
+
@Query("SELECT pk FROM MyEntity")
fun queryOfArrayWithLong(): Array<Long>
- @Query("SELECT * FROM MyEntity")
+ @Suppress(RoomWarnings.UNNECESSARY_NULLABILITY_IN_DAO_RETURN_TYPE)
+ @Query("SELECT pk FROM MyEntity")
+ fun queryOfArrayWithNullableLong(): Array<Long?>
+
+ @Query("SELECT pk FROM MyEntity")
fun queryOfLongArray(): LongArray
- @Query("SELECT * FROM MyEntity")
+ @Query("SELECT pk FROM MyEntity")
fun queryOfShortArray(): ShortArray
}
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/javac/ComplexDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/javac/ComplexDao.java
index 7c8b733..b29e9ef 100644
--- a/room/room-compiler/src/test/test-data/daoWriter/output/javac/ComplexDao.java
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/javac/ComplexDao.java
@@ -264,34 +264,44 @@
StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
_stringBuilder.append(")");
final String _sql = _stringBuilder.toString();
- final int _argCount = 0 + _inputSize;
- final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, _argCount);
- int _argIndex = 1;
- if (ids == null) {
- _statement.bindNull(_argIndex);
- } else {
- for (int _item : ids) {
- _statement.bindLong(_argIndex, _item);
- _argIndex++;
+ return DBUtil.performBlocking(__db, true, false, new Function1<SQLiteConnection, int[]>() {
+ @Override
+ @NonNull
+ public int[] invoke(@NonNull final SQLiteConnection _connection) {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ int _argIndex = 1;
+ if (ids == null) {
+ _stmt.bindNull(_argIndex);
+ } else {
+ for (int _item : ids) {
+ _stmt.bindLong(_argIndex, _item);
+ _argIndex++;
+ }
+ }
+ final List<Integer> _listResult = new ArrayList<Integer>();
+ while (_stmt.step()) {
+ final Integer _item_1;
+ if (_stmt.isNull(0)) {
+ _item_1 = null;
+ } else {
+ _item_1 = (int) (_stmt.getLong(0));
+ }
+ _listResult.add(_item_1);
+ }
+ final int[] _tmpArrayResult = new int[_listResult.size()];
+ int _index = 0;
+ for (int _listItem : _listResult) {
+ _tmpArrayResult[_index] = _listItem;
+ _index++;
+ }
+ final int[] _result = _tmpArrayResult;
+ return _result;
+ } finally {
+ _stmt.close();
+ }
}
- }
- __db.assertNotSuspendingTransaction();
- final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
- try {
- final int[] _tmpResult = new int[_cursor.getCount()];
- int _index = 0;
- while (_cursor.moveToNext()) {
- final int _item_1;
- _item_1 = _cursor.getInt(0);
- _tmpResult[_index] = _item_1;
- _index++;
- }
- final int[] _result = _tmpResult;
- return _result;
- } finally {
- _cursor.close();
- _statement.release();
- }
+ });
}
@Override
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/ksp/ComplexDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/ksp/ComplexDao.java
index d8c8fde..275e55d 100644
--- a/room/room-compiler/src/test/test-data/daoWriter/output/ksp/ComplexDao.java
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/ksp/ComplexDao.java
@@ -228,34 +228,40 @@
StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
_stringBuilder.append(")");
final String _sql = _stringBuilder.toString();
- final int _argCount = 0 + _inputSize;
- final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, _argCount);
- int _argIndex = 1;
- if (ids == null) {
- _statement.bindNull(_argIndex);
- } else {
- for (int _item : ids) {
- _statement.bindLong(_argIndex, _item);
- _argIndex++;
+ return DBUtil.performBlocking(__db, true, false, new Function1<SQLiteConnection, int[]>() {
+ @Override
+ @NonNull
+ public int[] invoke(@NonNull final SQLiteConnection _connection) {
+ final SQLiteStatement _stmt = _connection.prepare(_sql);
+ try {
+ int _argIndex = 1;
+ if (ids == null) {
+ _stmt.bindNull(_argIndex);
+ } else {
+ for (int _item : ids) {
+ _stmt.bindLong(_argIndex, _item);
+ _argIndex++;
+ }
+ }
+ final List<Integer> _listResult = new ArrayList<Integer>();
+ while (_stmt.step()) {
+ final Integer _item_1;
+ _item_1 = (int) (_stmt.getLong(0));
+ _listResult.add(_item_1);
+ }
+ final int[] _tmpArrayResult = new int[_listResult.size()];
+ int _index = 0;
+ for (int _listItem : _listResult) {
+ _tmpArrayResult[_index] = _listItem;
+ _index++;
+ }
+ final int[] _result = _tmpArrayResult;
+ return _result;
+ } finally {
+ _stmt.close();
+ }
}
- }
- __db.assertNotSuspendingTransaction();
- final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
- try {
- final int[] _tmpResult = new int[_cursor.getCount()];
- int _index = 0;
- while (_cursor.moveToNext()) {
- final int _item_1;
- _item_1 = _cursor.getInt(0);
- _tmpResult[_index] = _item_1;
- _index++;
- }
- final int[] _result = _tmpResult;
- return _result;
- } finally {
- _cursor.close();
- _statement.release();
- }
+ });
}
@Override
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_array.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_array.kt
index 34c3a55..c3cf5e4 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_array.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_array.kt
@@ -1,9 +1,7 @@
-import android.database.Cursor
import androidx.room.RoomDatabase
-import androidx.room.RoomSQLiteQuery
-import androidx.room.RoomSQLiteQuery.Companion.acquire
import androidx.room.util.getColumnIndexOrThrow
-import androidx.room.util.query
+import androidx.room.util.performBlocking
+import androidx.sqlite.SQLiteStatement
import javax.`annotation`.processing.Generated
import kotlin.Array
import kotlin.Int
@@ -13,8 +11,9 @@
import kotlin.ShortArray
import kotlin.String
import kotlin.Suppress
-import kotlin.arrayOfNulls
import kotlin.collections.List
+import kotlin.collections.MutableList
+import kotlin.collections.mutableListOf
import kotlin.reflect.KClass
@Generated(value = ["androidx.room.RoomProcessor"])
@@ -29,98 +28,133 @@
public override fun queryOfArray(): Array<MyEntity> {
val _sql: String = "SELECT * FROM MyEntity"
- val _statement: RoomSQLiteQuery = acquire(_sql, 0)
- __db.assertNotSuspendingTransaction()
- val _cursor: Cursor = query(__db, _statement, false, null)
- try {
- val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_cursor, "pk")
- val _cursorIndexOfOther: Int = getColumnIndexOrThrow(_cursor, "other")
- val _cursorIndexOfOther2: Int = getColumnIndexOrThrow(_cursor, "other2")
- val _tmpResult: Array<MyEntity?> = arrayOfNulls<MyEntity>(_cursor.getCount())
- var _index: Int = 0
- while (_cursor.moveToNext()) {
- val _item: MyEntity
- val _tmpPk: Int
- _tmpPk = _cursor.getInt(_cursorIndexOfPk)
- val _tmpOther: String
- _tmpOther = _cursor.getString(_cursorIndexOfOther)
- val _tmpOther2: Long
- _tmpOther2 = _cursor.getLong(_cursorIndexOfOther2)
- _item = MyEntity(_tmpPk,_tmpOther,_tmpOther2)
- _tmpResult[_index] = _item
- _index++
+ return performBlocking(__db, true, false) { _connection ->
+ val _stmt: SQLiteStatement = _connection.prepare(_sql)
+ try {
+ val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_stmt, "pk")
+ val _cursorIndexOfOther: Int = getColumnIndexOrThrow(_stmt, "other")
+ val _cursorIndexOfOther2: Int = getColumnIndexOrThrow(_stmt, "other2")
+ val _listResult: MutableList<MyEntity> = mutableListOf()
+ while (_stmt.step()) {
+ val _item: MyEntity
+ val _tmpPk: Int
+ _tmpPk = _stmt.getLong(_cursorIndexOfPk).toInt()
+ val _tmpOther: String
+ _tmpOther = _stmt.getText(_cursorIndexOfOther)
+ val _tmpOther2: Long
+ _tmpOther2 = _stmt.getLong(_cursorIndexOfOther2)
+ _item = MyEntity(_tmpPk,_tmpOther,_tmpOther2)
+ _listResult.add(_item)
+ }
+ val _result: Array<MyEntity> = _listResult.toTypedArray()
+ _result
+ } finally {
+ _stmt.close()
}
- val _result: Array<MyEntity> = (_tmpResult) as Array<MyEntity>
- return _result
- } finally {
- _cursor.close()
- _statement.release()
+ }
+ }
+
+ public override fun queryOfNullableArray(): Array<MyEntity?> {
+ val _sql: String = "SELECT * FROM MyEntity"
+ return performBlocking(__db, true, false) { _connection ->
+ val _stmt: SQLiteStatement = _connection.prepare(_sql)
+ try {
+ val _cursorIndexOfPk: Int = getColumnIndexOrThrow(_stmt, "pk")
+ val _cursorIndexOfOther: Int = getColumnIndexOrThrow(_stmt, "other")
+ val _cursorIndexOfOther2: Int = getColumnIndexOrThrow(_stmt, "other2")
+ val _listResult: MutableList<MyEntity> = mutableListOf()
+ while (_stmt.step()) {
+ val _item: MyEntity
+ val _tmpPk: Int
+ _tmpPk = _stmt.getLong(_cursorIndexOfPk).toInt()
+ val _tmpOther: String
+ _tmpOther = _stmt.getText(_cursorIndexOfOther)
+ val _tmpOther2: Long
+ _tmpOther2 = _stmt.getLong(_cursorIndexOfOther2)
+ _item = MyEntity(_tmpPk,_tmpOther,_tmpOther2)
+ _listResult.add(_item)
+ }
+ val _result: Array<MyEntity?> = _listResult.toTypedArray()
+ _result
+ } finally {
+ _stmt.close()
+ }
}
}
public override fun queryOfArrayWithLong(): Array<Long> {
val _sql: String = "SELECT pk FROM MyEntity"
- val _statement: RoomSQLiteQuery = acquire(_sql, 0)
- __db.assertNotSuspendingTransaction()
- val _cursor: Cursor = query(__db, _statement, false, null)
- try {
- val _tmpResult: Array<Long?> = arrayOfNulls<Long>(_cursor.getCount())
- var _index: Int = 0
- while (_cursor.moveToNext()) {
- val _item: Long
- _item = _cursor.getLong(0)
- _tmpResult[_index] = _item
- _index++
+ return performBlocking(__db, true, false) { _connection ->
+ val _stmt: SQLiteStatement = _connection.prepare(_sql)
+ try {
+ val _listResult: MutableList<Long> = mutableListOf()
+ while (_stmt.step()) {
+ val _item: Long
+ _item = _stmt.getLong(0)
+ _listResult.add(_item)
+ }
+ val _result: Array<Long> = _listResult.toTypedArray()
+ _result
+ } finally {
+ _stmt.close()
}
- val _result: Array<Long> = (_tmpResult) as Array<Long>
- return _result
- } finally {
- _cursor.close()
- _statement.release()
+ }
+ }
+
+ public override fun queryOfArrayWithNullableLong(): Array<Long?> {
+ val _sql: String = "SELECT pk FROM MyEntity"
+ return performBlocking(__db, true, false) { _connection ->
+ val _stmt: SQLiteStatement = _connection.prepare(_sql)
+ try {
+ val _listResult: MutableList<Long> = mutableListOf()
+ while (_stmt.step()) {
+ val _item: Long
+ _item = _stmt.getLong(0)
+ _listResult.add(_item)
+ }
+ val _result: Array<Long?> = _listResult.toTypedArray()
+ _result
+ } finally {
+ _stmt.close()
+ }
}
}
public override fun queryOfLongArray(): LongArray {
- val _sql: String = "SELECT * FROM MyEntity"
- val _statement: RoomSQLiteQuery = acquire(_sql, 0)
- __db.assertNotSuspendingTransaction()
- val _cursor: Cursor = query(__db, _statement, false, null)
- try {
- val _tmpResult: Array<Long?> = arrayOfNulls<Long>(_cursor.getCount())
- var _index: Int = 0
- while (_cursor.moveToNext()) {
- val _item: Long
- _item = _cursor.getLong(0)
- _tmpResult[_index] = _item
- _index++
+ val _sql: String = "SELECT pk FROM MyEntity"
+ return performBlocking(__db, true, false) { _connection ->
+ val _stmt: SQLiteStatement = _connection.prepare(_sql)
+ try {
+ val _listResult: MutableList<Long> = mutableListOf()
+ while (_stmt.step()) {
+ val _item: Long
+ _item = _stmt.getLong(0)
+ _listResult.add(_item)
+ }
+ val _result: LongArray = _listResult.toLongArray()
+ _result
+ } finally {
+ _stmt.close()
}
- val _result: LongArray = ((_tmpResult) as Array<Long>).toLongArray()
- return _result
- } finally {
- _cursor.close()
- _statement.release()
}
}
public override fun queryOfShortArray(): ShortArray {
- val _sql: String = "SELECT * FROM MyEntity"
- val _statement: RoomSQLiteQuery = acquire(_sql, 0)
- __db.assertNotSuspendingTransaction()
- val _cursor: Cursor = query(__db, _statement, false, null)
- try {
- val _tmpResult: Array<Short?> = arrayOfNulls<Short>(_cursor.getCount())
- var _index: Int = 0
- while (_cursor.moveToNext()) {
- val _item: Short
- _item = _cursor.getShort(0)
- _tmpResult[_index] = _item
- _index++
+ val _sql: String = "SELECT pk FROM MyEntity"
+ return performBlocking(__db, true, false) { _connection ->
+ val _stmt: SQLiteStatement = _connection.prepare(_sql)
+ try {
+ val _listResult: MutableList<Short> = mutableListOf()
+ while (_stmt.step()) {
+ val _item: Short
+ _item = _stmt.getLong(0).toShort()
+ _listResult.add(_item)
+ }
+ val _result: ShortArray = _listResult.toShortArray()
+ _result
+ } finally {
+ _stmt.close()
}
- val _result: ShortArray = ((_tmpResult) as Array<Short>).toShortArray()
- return _result
- } finally {
- _cursor.close()
- _statement.release()
}
}
diff --git a/room/room-gradle-plugin/src/test/java/androidx/room/gradle/RoomAndroidGradlePluginTest.kt b/room/room-gradle-plugin/src/test/java/androidx/room/gradle/RoomAndroidGradlePluginTest.kt
index 02b6c11..cc0132e 100644
--- a/room/room-gradle-plugin/src/test/java/androidx/room/gradle/RoomAndroidGradlePluginTest.kt
+++ b/room/room-gradle-plugin/src/test/java/androidx/room/gradle/RoomAndroidGradlePluginTest.kt
@@ -133,6 +133,7 @@
|
|room {
|${schemaDslLines.joinToString(separator = "\n")}
+ | generateKotlin = false
|}
|
""".trimMargin()
diff --git a/room/room-gradle-plugin/src/test/java/androidx/room/gradle/RoomKmpGradlePluginTest.kt b/room/room-gradle-plugin/src/test/java/androidx/room/gradle/RoomKmpGradlePluginTest.kt
index bc7b632..1537e8f 100644
--- a/room/room-gradle-plugin/src/test/java/androidx/room/gradle/RoomKmpGradlePluginTest.kt
+++ b/room/room-gradle-plugin/src/test/java/androidx/room/gradle/RoomKmpGradlePluginTest.kt
@@ -178,10 +178,33 @@
}
@Test
- fun `Blocking DAO function in non-Android source set`() {
+ fun `Blocking query DAO function in non-Android source set`() {
setup(generateKotlin = "true")
- // Make a change that changes the schema at version 1
+ searchAndReplace(
+ file = projectSetup.rootDir.resolve("src/nativeMain/kotlin/room/testapp/MyDatabase.kt"),
+ search = "// Insert-change",
+ replace = """
+ @Query("SELECT * FROM NativeEntity")
+ fun blockingQuery(): NativeEntity
+ """.trimIndent()
+ )
+
+ runGradle(
+ NATIVE_COMPILE_TASK,
+ projectDir = projectSetup.rootDir,
+ expectFailure = true
+ ).let { result ->
+ result.assertTaskOutcome(NATIVE_KSP_TASK, TaskOutcome.FAILED)
+ result.output.contains("Only suspend functions are allowed in DAOs" +
+ " declared in non-Android platforms.")
+ }
+ }
+
+ @Test
+ fun `Blocking shortcut DAO function in non-Android source set`() {
+ setup(generateKotlin = "true")
+
searchAndReplace(
file = projectSetup.rootDir.resolve("src/nativeMain/kotlin/room/testapp/MyDatabase.kt"),
search = "// Insert-change",
@@ -202,6 +225,30 @@
}
}
+ @Test
+ fun `Blocking transaction wrapper DAO function in non-Android source set`() {
+ setup(generateKotlin = "true")
+
+ searchAndReplace(
+ file = projectSetup.rootDir.resolve("src/nativeMain/kotlin/room/testapp/MyDatabase.kt"),
+ search = "// Insert-change",
+ replace = """
+ @Transaction
+ fun blockingTransaction() { }
+ """.trimIndent()
+ )
+
+ runGradle(
+ NATIVE_COMPILE_TASK,
+ projectDir = projectSetup.rootDir,
+ expectFailure = true
+ ).let { result ->
+ result.assertTaskOutcome(NATIVE_KSP_TASK, TaskOutcome.FAILED)
+ result.output.contains("Only suspend functions are allowed in DAOs" +
+ " declared in non-Android platforms.")
+ }
+ }
+
companion object {
private const val CLEAN_TASK = ":clean"
private const val COMMON_KSP_TASK = ":kspCommonMainKotlinMetadata"
diff --git a/room/room-runtime/build.gradle b/room/room-runtime/build.gradle
index 9d0c517..9b0a135 100644
--- a/room/room-runtime/build.gradle
+++ b/room/room-runtime/build.gradle
@@ -187,6 +187,7 @@
dependsOn(commonTest)
dependencies {
implementation(project(":sqlite:sqlite-bundled"))
+ implementation(libs.okio)
}
}
targets.all { target ->
diff --git a/room/room-runtime/src/androidMain/kotlin/androidx/room/DatabaseConfiguration.android.kt b/room/room-runtime/src/androidMain/kotlin/androidx/room/DatabaseConfiguration.android.kt
index 1307781..70201df 100644
--- a/room/room-runtime/src/androidMain/kotlin/androidx/room/DatabaseConfiguration.android.kt
+++ b/room/room-runtime/src/androidMain/kotlin/androidx/room/DatabaseConfiguration.android.kt
@@ -37,54 +37,39 @@
@SuppressLint("LambdaLast")
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
constructor(
- /**
- * The context to use while connecting to the database.
- */
+ /* The context to use while connecting to the database. */
@JvmField
val context: Context,
- /**
- * The name of the database file or null if it is an in-memory database.
- */
+ /* The name of the database file or null if it is an in-memory database. */
@JvmField
actual val name: String?,
- /**
- * The factory to use to access the database.
- */
+ /* The factory to use to access the database. */
@JvmField
val sqliteOpenHelperFactory: SupportSQLiteOpenHelper.Factory?,
- /**
- * Collection of available migrations.
- */
+ /* Collection of available migrations. */
@JvmField
actual val migrationContainer: RoomDatabase.MigrationContainer,
+ /* Database callbacks. */
@JvmField
actual val callbacks: List<RoomDatabase.Callback>?,
- /**
- * Whether Room should throw an exception for queries run on the main thread.
- */
+ /* Whether Room should throw an exception for queries run on the main thread. */
@JvmField
val allowMainThreadQueries: Boolean,
- /**
- * The journal mode for this database.
- */
+ /* The journal mode for this database. */
@JvmField
actual val journalMode: RoomDatabase.JournalMode,
- /**
- * The Executor used to execute asynchronous queries.
- */
+ /* The Executor used to execute asynchronous queries. */
@JvmField
val queryExecutor: Executor,
- /**
- * The Executor used to execute asynchronous transactions.
- */
+ /* The Executor used to execute asynchronous transactions. */
@JvmField
val transactionExecutor: Executor,
@@ -97,38 +82,49 @@
@JvmField
val multiInstanceInvalidationServiceIntent: Intent?,
+ /* Whether Room should throw an exception for missing migrations. */
@JvmField
actual val requireMigration: Boolean,
+ /* Whether Room will fallback to destructive migrations on downgrades only .*/
@JvmField
actual val allowDestructiveMigrationOnDowngrade: Boolean,
internal actual val migrationNotRequiredFrom: Set<Int>?,
+ /* Asset path of pre-package database or null if not used. */
@JvmField
val copyFromAssetPath: String?,
+ /* File of pre-package database or null if not used. */
@JvmField
val copyFromFile: File?,
+ /* Input stream of pre-package database or null if not used. */
@JvmField
val copyFromInputStream: Callable<InputStream>?,
+ /* Callback when Room uses a pre-packaged database. */
@JvmField
val prepackagedDatabaseCallback: RoomDatabase.PrepackagedDatabaseCallback?,
+ /* List of provided type converters. */
@JvmField
actual val typeConverters: List<Any>,
+ /* List of provided auto migration specs. */
@JvmField
actual val autoMigrationSpecs: List<AutoMigrationSpec>,
+ /* Whether Room will delete all tables or only known tables during destructive migrations. */
@JvmField
- val allowDestructiveMigrationForAllTables: Boolean,
+ actual val allowDestructiveMigrationForAllTables: Boolean,
+ /* The SQLite Driver for the database. */
@JvmField
actual val sqliteDriver: SQLiteDriver?,
+ /* The Coroutine context for the database. */
@JvmField
actual val queryCoroutineContext: CoroutineContext?,
) {
diff --git a/room/room-runtime/src/androidMain/kotlin/androidx/room/Room.android.kt b/room/room-runtime/src/androidMain/kotlin/androidx/room/Room.android.kt
index d6a942a..e27a6ec 100644
--- a/room/room-runtime/src/androidMain/kotlin/androidx/room/Room.android.kt
+++ b/room/room-runtime/src/androidMain/kotlin/androidx/room/Room.android.kt
@@ -90,6 +90,11 @@
" If you are trying to create an in memory database, use Room" +
".inMemoryDatabaseBuilder"
}
+ require(name != ":memory:") {
+ "Cannot build a database with the special name ':memory:'." +
+ " If you are trying to create an in memory database, use Room" +
+ ".inMemoryDatabaseBuilder"
+ }
return RoomDatabase.Builder(context, klass, name)
}
@@ -113,7 +118,12 @@
require(name.isNotBlank()) {
"Cannot build a database with empty name." +
" If you are trying to create an in memory database, use Room" +
- ".inMemoryDatabaseBuilder"
+ ".inMemoryDatabaseBuilder()."
+ }
+ require(name != ":memory:") {
+ "Cannot build a database with the special name ':memory:'." +
+ " If you are trying to create an in memory database, use Room" +
+ ".inMemoryDatabaseBuilder()."
}
return RoomDatabase.Builder(T::class, name, factory, context)
}
diff --git a/room/room-runtime/src/androidMain/kotlin/androidx/room/RoomConnectionManager.android.kt b/room/room-runtime/src/androidMain/kotlin/androidx/room/RoomConnectionManager.android.kt
index e8f7d67..40bbd48 100644
--- a/room/room-runtime/src/androidMain/kotlin/androidx/room/RoomConnectionManager.android.kt
+++ b/room/room-runtime/src/androidMain/kotlin/androidx/room/RoomConnectionManager.android.kt
@@ -115,16 +115,6 @@
block: suspend (Transactor) -> R
): R = connectionPool.useConnection(isReadOnly, block)
- override fun dropAllTables(connection: SQLiteConnection) {
- if (configuration.allowDestructiveMigrationForAllTables) {
- // Drops all tables (excluding special ones)
- super.dropAllTables(connection)
- } else {
- // Drops known tables (Room entity tables)
- openDelegate.dropAllTables(connection)
- }
- }
-
fun close() {
connectionPool.close()
}
diff --git a/room/room-runtime/src/androidMain/kotlin/androidx/room/RoomDatabase.android.kt b/room/room-runtime/src/androidMain/kotlin/androidx/room/RoomDatabase.android.kt
index 0a6b0ea..7421bac 100644
--- a/room/room-runtime/src/androidMain/kotlin/androidx/room/RoomDatabase.android.kt
+++ b/room/room-runtime/src/androidMain/kotlin/androidx/room/RoomDatabase.android.kt
@@ -924,18 +924,14 @@
private var queryCallback: QueryCallback? = null
private var queryCallbackExecutor: Executor? = null
private val typeConverters: MutableList<Any> = mutableListOf()
- private var autoMigrationSpecs: MutableList<AutoMigrationSpec> = mutableListOf()
-
private var queryExecutor: Executor? = null
-
private var transactionExecutor: Executor? = null
+
private var supportOpenHelperFactory: SupportSQLiteOpenHelper.Factory? = null
private var allowMainThreadQueries = false
private var journalMode: JournalMode = JournalMode.AUTOMATIC
private var multiInstanceInvalidationIntent: Intent? = null
- private var requireMigration: Boolean = true
- private var allowDestructiveMigrationOnDowngrade = false
- private var allowDestructiveMigrationForAllTables = false
+
private var autoCloseTimeout = -1L
private var autoCloseTimeUnit: TimeUnit? = null
@@ -943,6 +939,11 @@
* Migrations, mapped by from-to pairs.
*/
private val migrationContainer: MigrationContainer = MigrationContainer()
+
+ /**
+ * Versions that don't require migrations, configured via
+ * [fallbackToDestructiveMigrationFrom].
+ */
private var migrationsNotRequiredFrom: MutableSet<Int> = mutableSetOf()
/**
@@ -950,7 +951,14 @@
* [addMigrations] for later validation that makes those versions don't
* match any versions passed to [fallbackToDestructiveMigrationFrom].
*/
- private var migrationStartAndEndVersions: MutableSet<Int>? = null
+ private val migrationStartAndEndVersions = mutableSetOf<Int>()
+
+ private val autoMigrationSpecs: MutableList<AutoMigrationSpec> = mutableListOf()
+
+ private var requireMigration: Boolean = true
+ private var allowDestructiveMigrationOnDowngrade = false
+ private var allowDestructiveMigrationForAllTables = false
+
private var copyFromAssetPath: String? = null
private var copyFromFile: File? = null
private var copyFromInputStream: Callable<InputStream>? = null
@@ -1149,42 +1157,35 @@
/**
* Adds a migration to the builder.
*
- * Each Migration has a start and end versions and Room runs these migrations to bring the
+ * Each [Migration] has a start and end versions and Room runs these migrations to bring the
* database to the latest version.
*
- * If a migration item is missing between current version and the latest version, Room
- * will clear the database and recreate so even if you have no changes between 2 versions,
- * you should still provide a Migration object to the builder.
- *
* A migration can handle more than 1 version (e.g. if you have a faster path to choose when
- * going version 3 to 5 without going to version 4). If Room opens a database at version
- * 3 and latest version is >= 5, Room will use the migration object that can migrate from
- * 3 to 5 instead of 3 to 4 and 4 to 5.
+ * going from version 3 to 5 without going to version 4). If Room opens a database at
+ * version 3 and latest version is >= 5, Room will use the migration object that can migrate
+ * from 3 to 5 instead of 3 to 4 and 4 to 5.
*
- * @param migrations The migration object that can modify the database and to the necessary
- * changes.
+ * @param migrations The migration objects that modify the database schema with the
+ * necessary changes for a version change.
* @return This builder instance.
*/
- open fun addMigrations(vararg migrations: Migration) = apply {
- if (migrationStartAndEndVersions == null) {
- migrationStartAndEndVersions = HashSet()
- }
+ actual open fun addMigrations(vararg migrations: Migration) = apply {
for (migration in migrations) {
- migrationStartAndEndVersions!!.add(migration.startVersion)
- migrationStartAndEndVersions!!.add(migration.endVersion)
+ migrationStartAndEndVersions.add(migration.startVersion)
+ migrationStartAndEndVersions.add(migration.endVersion)
}
migrationContainer.addMigrations(*migrations)
}
/**
- * Adds an auto migration spec to the builder.
+ * Adds an auto migration spec instance to the builder.
*
* @param autoMigrationSpec The auto migration object that is annotated with
- * [AutoMigrationSpec] and is declared in an [AutoMigration] annotation.
+ * [ProvidedAutoMigrationSpec] and is declared in an [AutoMigration] annotation.
* @return This builder instance.
*/
@Suppress("MissingGetterMatchingBuilder")
- open fun addAutoMigrationSpec(autoMigrationSpec: AutoMigrationSpec) = apply {
+ actual open fun addAutoMigrationSpec(autoMigrationSpec: AutoMigrationSpec) = apply {
this.autoMigrationSpecs.add(autoMigrationSpec)
}
@@ -1207,18 +1208,16 @@
/**
* Sets the journal mode for this database.
*
- * This value is ignored if the builder is initialized with
- * [Room.inMemoryDatabaseBuilder].
- *
- * The journal mode should be consistent across multiple instances of
- * [RoomDatabase] for a single SQLite database file.
+ * The value is ignored if the builder is for an 'in-memory database'. The journal mode
+ * should be consistent across multiple instances of [RoomDatabase] for a single SQLite
+ * database file.
*
* The default value is [JournalMode.AUTOMATIC].
*
* @param journalMode The journal mode.
* @return This builder instance.
*/
- open fun setJournalMode(journalMode: JournalMode) = apply {
+ actual open fun setJournalMode(journalMode: JournalMode) = apply {
this.journalMode = journalMode
}
@@ -1357,7 +1356,7 @@
* @return This builder instance.
*/
@Deprecated(
- message = "Replace by overloaded version with parameter to indicate if all tables" +
+ message = "Replace by overloaded version with parameter to indicate if all tables " +
"should be dropped or not.",
replaceWith = ReplaceWith("fallbackToDestructiveMigration(false)")
)
@@ -1372,13 +1371,10 @@
* migrate old database schemas to the latest schema version are not found.
*
* When the database version on the device does not match the latest schema version, Room
- * runs necessary [Migration]s on the database.
- *
- * If it cannot find the set of [Migration]s that will bring the database to the
- * current version, it will throw an [IllegalStateException].
- *
- * You can call this method to change this behavior to re-create the database tables instead
- * of crashing.
+ * runs necessary [Migration]s on the database. If it cannot find the set of [Migration]s
+ * that will bring the database to the current version, it will throw an
+ * [IllegalStateException]. You can call this method to change this behavior to re-create
+ * the database tables instead of crashing.
*
* If the database was create from an asset or a file then Room will try to use the same
* file to re-create the database, otherwise this will delete all of the data in the
@@ -1388,11 +1384,12 @@
* [fallbackToDestructiveMigrationOnDowngrade].
*
* @param dropAllTables Set to `true` if all tables should be dropped during destructive
- * migration including those not managed by Room.
+ * migration including those not managed by Room. Recommended value is `true` as otherwise
+ * Room could leave obsolete data when table names or existence changes between versions.
* @return This builder instance.
*/
@Suppress("BuilderSetStyle") // Overload of existing API
- fun fallbackToDestructiveMigration(dropAllTables: Boolean) = apply {
+ actual fun fallbackToDestructiveMigration(dropAllTables: Boolean) = apply {
this.requireMigration = false
this.allowDestructiveMigrationOnDowngrade = true
this.allowDestructiveMigrationForAllTables = dropAllTables
@@ -1407,7 +1404,7 @@
* @return This builder instance.
*/
@Deprecated(
- message = "Replace by overloaded version with parameter to indicate if all tables" +
+ message = "Replace by overloaded version with parameter to indicate if all tables " +
"should be dropped or not.",
replaceWith = ReplaceWith("fallbackToDestructiveMigrationOnDowngrade(false)")
)
@@ -1428,7 +1425,7 @@
* @return This builder instance.
*/
@Suppress("BuilderSetStyle") // Overload of existing API
- fun fallbackToDestructiveMigrationOnDowngrade(dropAllTables: Boolean) = apply {
+ actual fun fallbackToDestructiveMigrationOnDowngrade(dropAllTables: Boolean) = apply {
this.requireMigration = true
this.allowDestructiveMigrationOnDowngrade = true
this.allowDestructiveMigrationForAllTables = dropAllTables
@@ -1456,7 +1453,7 @@
* @return This builder instance.
*/
@Deprecated(
- message = "Replace by overloaded version with parameter to indicate if all tables" +
+ message = "Replace by overloaded version with parameter to indicate if all tables " +
"should be dropped or not.",
replaceWith = ReplaceWith("fallbackToDestructiveMigrationFrom(false, startVersions)")
)
@@ -1470,16 +1467,16 @@
* Informs Room that it is allowed to destructively recreate database tables from specific
* starting schema versions.
*
- * This functionality is the same as that provided by
- * [fallbackToDestructiveMigration], except that this method allows the
- * specification of a set of schema versions for which destructive recreation is allowed.
+ * This functionality is the same [fallbackToDestructiveMigration], except that this method
+ * allows the specification of a set of schema versions for which destructive recreation is
+ * allowed.
*
* Using this method is preferable to [fallbackToDestructiveMigration] if you want
* to allow destructive migrations from some schema versions while still taking advantage
* of exceptions being thrown due to unintentionally missing migrations.
*
* Note: No versions passed to this method may also exist as either starting or ending
- * versions in the [Migration]s provided to [addMigrations]. If a
+ * versions in the [Migration]s provided via [addMigrations]. If a
* version passed to this method is found as a starting or ending version in a Migration, an
* exception will be thrown.
*
@@ -1489,8 +1486,12 @@
* migration.
* @return This builder instance.
*/
- @Suppress("BuilderSetStyle") // Overload of existing API
- open fun fallbackToDestructiveMigrationFrom(
+ @Suppress(
+ "BuilderSetStyle", // Overload of existing API
+ "MissingJvmstatic", // No need for @JvmOverloads due to an overload already existing
+ )
+ actual open fun fallbackToDestructiveMigrationFrom(
+ @Suppress("KotlinDefaultParameterOrder") // There is a vararg that must be last
dropAllTables: Boolean,
vararg startVersions: Int
) = apply {
@@ -1533,13 +1534,13 @@
}
/**
- * Adds a type converter instance to this database.
+ * Adds a type converter instance to the builder.
*
- * @param typeConverter The converter. It must be an instance of a class annotated with
- * [ProvidedTypeConverter] otherwise Room will throw an exception.
+ * @param typeConverter The converter instance that is annotated with
+ * [ProvidedTypeConverter].
* @return This builder instance.
*/
- open fun addTypeConverter(typeConverter: Any) = apply {
+ actual open fun addTypeConverter(typeConverter: Any) = apply {
this.typeConverters.add(typeConverter)
}
@@ -1647,17 +1648,8 @@
} else if (queryExecutor == null) {
queryExecutor = transactionExecutor
}
- if (migrationStartAndEndVersions != null) {
- for (version in migrationStartAndEndVersions!!) {
- require(!migrationsNotRequiredFrom.contains(version)) {
- "Inconsistency detected. A Migration was supplied to " +
- "addMigration(Migration... migrations) that has a start " +
- "or end version equal to a start version supplied to " +
- "fallbackToDestructiveMigrationFrom(int... " +
- "startVersions). Start version: $version"
- }
- }
- }
+
+ validateMigrationsNotRequired(migrationStartAndEndVersions, migrationsNotRequiredFrom)
val initialFactory: SupportSQLiteOpenHelper.Factory? =
if (driver == null && supportOpenHelperFactory == null) {
@@ -1733,28 +1725,28 @@
}
}
val configuration = DatabaseConfiguration(
- context,
- name,
- supportOpenHelperFactory,
- migrationContainer,
- callbacks,
- allowMainThreadQueries,
- journalMode.resolve(context),
- requireNotNull(queryExecutor),
- requireNotNull(transactionExecutor),
- multiInstanceInvalidationIntent,
- requireMigration,
- allowDestructiveMigrationOnDowngrade,
- migrationsNotRequiredFrom,
- copyFromAssetPath,
- copyFromFile,
- copyFromInputStream,
- prepackagedDatabaseCallback,
- typeConverters,
- autoMigrationSpecs,
- allowDestructiveMigrationForAllTables,
- driver,
- queryCoroutineContext,
+ context = context,
+ name = name,
+ sqliteOpenHelperFactory = supportOpenHelperFactory,
+ migrationContainer = migrationContainer,
+ callbacks = callbacks,
+ allowMainThreadQueries = allowMainThreadQueries,
+ journalMode = journalMode.resolve(context),
+ queryExecutor = requireNotNull(queryExecutor),
+ transactionExecutor = requireNotNull(transactionExecutor),
+ multiInstanceInvalidationServiceIntent = multiInstanceInvalidationIntent,
+ requireMigration = requireMigration,
+ allowDestructiveMigrationOnDowngrade = allowDestructiveMigrationOnDowngrade,
+ migrationNotRequiredFrom = migrationsNotRequiredFrom,
+ copyFromAssetPath = copyFromAssetPath,
+ copyFromFile = copyFromFile,
+ copyFromInputStream = copyFromInputStream,
+ prepackagedDatabaseCallback = prepackagedDatabaseCallback,
+ typeConverters = typeConverters,
+ autoMigrationSpecs = autoMigrationSpecs,
+ allowDestructiveMigrationForAllTables = allowDestructiveMigrationForAllTables,
+ sqliteDriver = driver,
+ queryCoroutineContext = queryCoroutineContext,
)
val db = factory?.invoke() ?: findAndInstantiateDatabaseImpl(klass.java)
db.init(configuration)
diff --git a/room/room-runtime/src/androidMain/kotlin/androidx/room/migration/Migration.android.kt b/room/room-runtime/src/androidMain/kotlin/androidx/room/migration/Migration.android.kt
index 1adc4fe..353ad4b 100644
--- a/room/room-runtime/src/androidMain/kotlin/androidx/room/migration/Migration.android.kt
+++ b/room/room-runtime/src/androidMain/kotlin/androidx/room/migration/Migration.android.kt
@@ -37,7 +37,7 @@
*
* @constructor Creates a new migration between [startVersion] and [endVersion] inclusive.
*/
-actual abstract class Migration(
+actual abstract class Migration actual constructor(
@JvmField
actual val startVersion: Int,
@JvmField
diff --git a/room/room-runtime/src/androidUnitTest/kotlin/androidx/room/BuilderTest.kt b/room/room-runtime/src/androidUnitTest/kotlin/androidx/room/BuilderTest.kt
index 7927ee5..d24b481 100644
--- a/room/room-runtime/src/androidUnitTest/kotlin/androidx/room/BuilderTest.kt
+++ b/room/room-runtime/src/androidUnitTest/kotlin/androidx/room/BuilderTest.kt
@@ -80,6 +80,20 @@
}
@Test
+ fun specialMemoryName() {
+ try {
+ databaseBuilder(
+ mock(), RoomDatabase::class.java, ":memory:"
+ ).build()
+ } catch (e: IllegalArgumentException) {
+ assertThat(e.message).isEqualTo(
+ "Cannot build a database with the special name ':memory:'. If you are trying " +
+ "to create an in memory database, use Room.inMemoryDatabaseBuilder"
+ )
+ }
+ }
+
+ @Test
fun executors_setQueryExecutor() {
val executor: Executor = mock()
val db = databaseBuilder(
diff --git a/room/room-runtime/src/commonMain/kotlin/androidx/room/DatabaseConfiguration.kt b/room/room-runtime/src/commonMain/kotlin/androidx/room/DatabaseConfiguration.kt
index f5a89e9..cfa6ff2 100644
--- a/room/room-runtime/src/commonMain/kotlin/androidx/room/DatabaseConfiguration.kt
+++ b/room/room-runtime/src/commonMain/kotlin/androidx/room/DatabaseConfiguration.kt
@@ -28,13 +28,23 @@
val name: String?
/* Collection of available migrations. */
val migrationContainer: RoomDatabase.MigrationContainer
+ /* Database callbacks. */
val callbacks: List<RoomDatabase.Callback>?
+ /* The journal mode for this database. */
val journalMode: RoomDatabase.JournalMode
+ /* Whether Room should throw an exception for missing migrations. */
val requireMigration: Boolean
+ /* Whether Room will fallback to destructive migrations on downgrades only .*/
val allowDestructiveMigrationOnDowngrade: Boolean
internal val migrationNotRequiredFrom: Set<Int>?
+ /* List of provided type converters. */
val typeConverters: List<Any>
+ /* List of provided auto migration specs. */
val autoMigrationSpecs: List<AutoMigrationSpec>
+ /* Whether Room will delete all tables or only known tables during destructive migrations. */
+ val allowDestructiveMigrationForAllTables: Boolean
+ /* The SQLite Driver for the database. */
val sqliteDriver: SQLiteDriver?
+ /* The Coroutine context for the database. */
val queryCoroutineContext: CoroutineContext?
}
diff --git a/room/room-runtime/src/commonMain/kotlin/androidx/room/RoomConnectionManager.kt b/room/room-runtime/src/commonMain/kotlin/androidx/room/RoomConnectionManager.kt
index c8fef52..d1f2a0d 100644
--- a/room/room-runtime/src/commonMain/kotlin/androidx/room/RoomConnectionManager.kt
+++ b/room/room-runtime/src/commonMain/kotlin/androidx/room/RoomConnectionManager.kt
@@ -19,6 +19,7 @@
import androidx.annotation.RestrictTo
import androidx.room.RoomDatabase.JournalMode.TRUNCATE
import androidx.room.RoomDatabase.JournalMode.WRITE_AHEAD_LOGGING
+import androidx.room.concurrent.ExclusiveLock
import androidx.room.util.findMigrationPath
import androidx.room.util.isMigrationRequired
import androidx.sqlite.SQLiteConnection
@@ -42,6 +43,12 @@
protected abstract val openDelegate: RoomOpenDelegate
protected abstract val callbacks: List<RoomDatabase.Callback>
+ // Flag indicating that the database was configured, i.e. at least one connection has been
+ // opened, configured and schema validated.
+ private var isConfigured = false
+ // Flag set during initialization to prevent recursive initialization.
+ private var isInitializing = false
+
abstract suspend fun <R> useConnection(
isReadOnly: Boolean,
block: suspend (Transactor) -> R
@@ -51,18 +58,34 @@
protected inner class DriverWrapper(
private val actual: SQLiteDriver
) : SQLiteDriver {
- override fun open(fileName: String): SQLiteConnection {
- return configureConnection(actual.open(fileName))
- }
+ override fun open(fileName: String): SQLiteConnection =
+ ExclusiveLock(
+ filename = fileName,
+ useFileLock = !isConfigured && !isInitializing && fileName != ":memory:"
+ ).withLock {
+ check(!isInitializing) {
+ "Recursive database initialization detected. Did you try to use the database " +
+ "instance during initialization? Maybe in one of the callbacks?"
+ }
+ val connection = actual.open(fileName)
+ if (!isConfigured) {
+ try {
+ isInitializing = true
+ configureConnection(connection)
+ } finally {
+ isInitializing = false
+ }
+ }
+ return@withLock connection
+ }
}
/**
* Common database connection configuration and opening procedure, performs migrations if
* necessary, validates schema and invokes configured callbacks if any.
*/
- // TODO(b/316945717): Thread safe and process safe opening and migration
// TODO(b/316944352): Retry mechanism
- private fun configureConnection(connection: SQLiteConnection): SQLiteConnection {
+ private fun configureConnection(connection: SQLiteConnection) {
configureJournalMode(connection)
val version = connection.prepare("PRAGMA user_version").use { statement ->
statement.step()
@@ -85,7 +108,6 @@
}
}
onOpen(connection)
- return connection
}
private fun configureJournalMode(connection: SQLiteConnection) {
@@ -161,21 +183,27 @@
}
}
- protected open fun dropAllTables(connection: SQLiteConnection) {
- connection.prepare(
- "SELECT name FROM sqlite_master WHERE type = 'table'"
- ).use { statement ->
- buildList {
- while (statement.step()) {
- val name = statement.getText(0)
- if (name.startsWith("sqlite_") || name == "android_metadata") {
- continue
+ private fun dropAllTables(connection: SQLiteConnection) {
+ if (configuration.allowDestructiveMigrationForAllTables) {
+ // Drops all tables (excluding special ones)
+ connection.prepare(
+ "SELECT name FROM sqlite_master WHERE type = 'table'"
+ ).use { statement ->
+ buildList {
+ while (statement.step()) {
+ val name = statement.getText(0)
+ if (name.startsWith("sqlite_") || name == "android_metadata") {
+ continue
+ }
+ add(name)
}
- add(name)
}
+ }.forEach { table ->
+ connection.execSQL("DROP TABLE IF EXISTS $table")
}
- }.forEach { table ->
- connection.execSQL("DROP TABLE IF EXISTS $table")
+ } else {
+ // Drops known tables (Room entity tables)
+ openDelegate.dropAllTables(connection)
}
}
@@ -183,6 +211,7 @@
checkIdentity(connection)
openDelegate.onOpen(connection)
invokeOpenCallback(connection)
+ isConfigured = true
}
private fun checkIdentity(connection: SQLiteConnection) {
diff --git a/room/room-runtime/src/commonMain/kotlin/androidx/room/RoomDatabase.kt b/room/room-runtime/src/commonMain/kotlin/androidx/room/RoomDatabase.kt
index 919cf1a..8e59497 100644
--- a/room/room-runtime/src/commonMain/kotlin/androidx/room/RoomDatabase.kt
+++ b/room/room-runtime/src/commonMain/kotlin/androidx/room/RoomDatabase.kt
@@ -226,6 +226,117 @@
fun setDriver(driver: SQLiteDriver): Builder<T>
/**
+ * Adds a migration to the builder.
+ *
+ * Each [Migration] has a start and end versions and Room runs these migrations to bring the
+ * database to the latest version.
+ *
+ * A migration can handle more than 1 version (e.g. if you have a faster path to choose when
+ * going from version 3 to 5 without going to version 4). If Room opens a database at
+ * version 3 and latest version is >= 5, Room will use the migration object that can migrate
+ * from 3 to 5 instead of 3 to 4 and 4 to 5.
+ *
+ * @param migrations The migration objects that modify the database schema with the
+ * necessary changes for a version change.
+ * @return This builder instance.
+ */
+ fun addMigrations(vararg migrations: Migration): Builder<T>
+
+ /**
+ * Adds an auto migration spec instance to the builder.
+ *
+ * @param autoMigrationSpec The auto migration object that is annotated with
+ * [ProvidedAutoMigrationSpec] and is declared in an [AutoMigration] annotation.
+ * @return This builder instance.
+ */
+ fun addAutoMigrationSpec(autoMigrationSpec: AutoMigrationSpec): Builder<T>
+
+ /**
+ * Allows Room to destructively recreate database tables if [Migration]s that would
+ * migrate old database schemas to the latest schema version are not found.
+ *
+ * When the database version on the device does not match the latest schema version, Room
+ * runs necessary [Migration]s on the database. If it cannot find the set of [Migration]s
+ * that will bring the database to the current version, it will throw an
+ * [IllegalStateException]. You can call this method to change this behavior to re-create
+ * the database tables instead of crashing.
+ *
+ * To let Room fallback to destructive migration only during a schema downgrade then use
+ * [fallbackToDestructiveMigrationOnDowngrade].
+ *
+ * @param dropAllTables Set to `true` if all tables should be dropped during destructive
+ * migration including those not managed by Room. Recommended value is `true` as otherwise
+ * Room could leave obsolete data when table names or existence changes between versions.
+ * @return This builder instance.
+ */
+ fun fallbackToDestructiveMigration(dropAllTables: Boolean): Builder<T>
+
+ /**
+ * Allows Room to destructively recreate database tables if [Migration]s are not
+ * available when downgrading to old schema versions.
+ *
+ * For details, see [Builder.fallbackToDestructiveMigration].
+ *
+ * @param dropAllTables Set to `true` if all tables should be dropped during destructive
+ * migration including those not managed by Room. Recommended value is `true` as otherwise
+ * Room could leave obsolete data when table names or existence changes between versions.
+ * @return This builder instance.
+ */
+ fun fallbackToDestructiveMigrationOnDowngrade(dropAllTables: Boolean): Builder<T>
+
+ /**
+ * Informs Room that it is allowed to destructively recreate database tables from specific
+ * starting schema versions.
+ *
+ * This functionality is the same [fallbackToDestructiveMigration], except that this method
+ * allows the specification of a set of schema versions for which destructive recreation is
+ * allowed.
+ *
+ * Using this method is preferable to [fallbackToDestructiveMigration] if you want
+ * to allow destructive migrations from some schema versions while still taking advantage
+ * of exceptions being thrown due to unintentionally missing migrations.
+ *
+ * Note: No versions passed to this method may also exist as either starting or ending
+ * versions in the [Migration]s provided via [addMigrations]. If a
+ * version passed to this method is found as a starting or ending version in a Migration, an
+ * exception will be thrown.
+ *
+ * @param dropAllTables Set to `true` if all tables should be dropped during destructive
+ * migration including those not managed by Room. Recommended value is `true` as otherwise
+ * Room could leave obsolete data when table names or existence changes between versions.
+ * @param startVersions The set of schema versions from which Room should use a destructive
+ * migration.
+ * @return This builder instance.
+ */
+ fun fallbackToDestructiveMigrationFrom(
+ dropAllTables: Boolean,
+ vararg startVersions: Int
+ ): Builder<T>
+
+ /**
+ * Adds a type converter instance to the builder.
+ *
+ * @param typeConverter The converter instance that is annotated with
+ * [ProvidedTypeConverter].
+ * @return This builder instance.
+ */
+ fun addTypeConverter(typeConverter: Any): Builder<T>
+
+ /**
+ * Sets the journal mode for this database.
+ *
+ * The value is ignored if the builder is for an 'in-memory database'. The journal mode
+ * should be consistent across multiple instances of [RoomDatabase] for a single SQLite
+ * database file.
+ *
+ * The default value is [JournalMode.WRITE_AHEAD_LOGGING].
+ *
+ * @param journalMode The journal mode.
+ * @return This builder instance.
+ */
+ fun setJournalMode(journalMode: JournalMode): Builder<T>
+
+ /**
* Sets the [CoroutineContext] that will be used to execute all asynchronous queries and
* tasks, such as `Flow` emissions and [InvalidationTracker] notifications.
*
@@ -404,6 +515,25 @@
useConnection(isReadOnly = false, block)
}
+/**
+ * Validates that no added migration start or end are also marked as fallback to destructive
+ * migration from.
+ */
+internal fun validateMigrationsNotRequired(
+ migrationStartAndEndVersions: Set<Int>,
+ migrationsNotRequiredFrom: Set<Int>
+) {
+ if (migrationStartAndEndVersions.isNotEmpty()) {
+ for (version in migrationStartAndEndVersions) {
+ require(!migrationsNotRequiredFrom.contains(version)) {
+ "Inconsistency detected. A Migration was supplied to addMigration() that has a " +
+ "start or end version equal to a start version supplied to " +
+ "fallbackToDestructiveMigrationFrom(). Start version is: $version"
+ }
+ }
+ }
+}
+
internal fun RoomDatabase.validateAutoMigrations(configuration: DatabaseConfiguration) {
val autoMigrationSpecs = mutableMapOf<KClass<out AutoMigrationSpec>, AutoMigrationSpec>()
val requiredAutoMigrationSpecs = getRequiredAutoMigrationSpecClasses()
@@ -426,7 +556,7 @@
autoMigrationSpecs[spec] = configuration.autoMigrationSpecs[foundIndex]
}
for (providedIndex in configuration.autoMigrationSpecs.indices.reversed()) {
- require(usedSpecs[providedIndex]) {
+ require(providedIndex < usedSpecs.size && usedSpecs[providedIndex]) {
"Unexpected auto migration specs found. " +
"Annotate AutoMigrationSpec implementation with " +
"@ProvidedAutoMigrationSpec annotation or remove this spec from the " +
@@ -464,7 +594,7 @@
}
}
require(foundIndex >= 0) {
- "A required type converter ($converter) for" +
+ "A required type converter (${converter.qualifiedName}) for" +
" ${daoName.qualifiedName} is missing in the database configuration."
}
addTypeConverter(converter, configuration.typeConverters[foundIndex])
diff --git a/room/room-runtime/src/commonMain/kotlin/androidx/room/concurrent/ExclusiveLock.kt b/room/room-runtime/src/commonMain/kotlin/androidx/room/concurrent/ExclusiveLock.kt
new file mode 100644
index 0000000..abd360b
--- /dev/null
+++ b/room/room-runtime/src/commonMain/kotlin/androidx/room/concurrent/ExclusiveLock.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.room.concurrent
+
+import kotlinx.atomicfu.locks.ReentrantLock
+import kotlinx.atomicfu.locks.SynchronizedObject
+import kotlinx.atomicfu.locks.reentrantLock
+import kotlinx.atomicfu.locks.synchronized
+
+/**
+ * An exclusive lock for in-process and multi-process synchronization.
+ *
+ * The lock is cooperative and only protects the critical region from other [ExclusiveLock] users
+ * with the same `filename`. The lock is reentrant from within the same thread in the same process.
+ *
+ * Locking is done via two levels:
+ * 1. Thread locking within the same process is done via a [ReentrantLock] keyed by the given
+ * `filename`.
+ * 2. Multi-process locking is done via a [FileLock] whose lock file is based on the given
+ * `filename`.
+ *
+ * @param filename The path to the file to protect.
+ * @param useFileLock Whether multi-process lock will be done or not.
+ */
+internal class ExclusiveLock(filename: String, useFileLock: Boolean) {
+ private val threadLock: ReentrantLock = getThreadLock(filename)
+ private val fileLock: FileLock? = if (useFileLock) getFileLock(filename) else null
+
+ fun <T> withLock(block: () -> T): T {
+ threadLock.lock()
+ try {
+ fileLock?.lock()
+ try {
+ return block()
+ } finally {
+ fileLock?.unlock()
+ }
+ } finally {
+ threadLock.unlock()
+ }
+ }
+
+ companion object : SynchronizedObject() {
+ private val threadLocksMap = mutableMapOf<String, ReentrantLock>()
+ private fun getThreadLock(key: String): ReentrantLock = synchronized(this) {
+ return threadLocksMap.getOrPut(key) { reentrantLock() }
+ }
+ private fun getFileLock(key: String) = FileLock(key)
+ }
+}
diff --git a/room/room-runtime/src/commonMain/kotlin/androidx/room/concurrent/FileLock.kt b/room/room-runtime/src/commonMain/kotlin/androidx/room/concurrent/FileLock.kt
new file mode 100644
index 0000000..7594dc042
--- /dev/null
+++ b/room/room-runtime/src/commonMain/kotlin/androidx/room/concurrent/FileLock.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.concurrent
+
+/**
+ * A mutually exclusive advisory file lock implementation.
+ *
+ * The lock is cooperative and only protects the critical region from other [FileLock] users in
+ * other process with the same `filename`. Do not use this class on its own, instead use
+ * [ExclusiveLock] which guarantees in-process locking too.
+ *
+ * @param filename The path to the file to protect. Note that an actual lock is not grab on the file
+ * itself but on a temporary file create with the same path but ending with `.lck`.
+ */
+internal expect class FileLock(filename: String) {
+ fun lock()
+ fun unlock()
+}
diff --git a/room/room-runtime/src/commonMain/kotlin/androidx/room/migration/Migration.kt b/room/room-runtime/src/commonMain/kotlin/androidx/room/migration/Migration.kt
index bfad951..865e6b8 100644
--- a/room/room-runtime/src/commonMain/kotlin/androidx/room/migration/Migration.kt
+++ b/room/room-runtime/src/commonMain/kotlin/androidx/room/migration/Migration.kt
@@ -34,7 +34,7 @@
*
* @constructor Creates a new migration between [startVersion] and [endVersion] inclusive.
*/
-expect abstract class Migration {
+expect abstract class Migration(startVersion: Int, endVersion: Int) {
val startVersion: Int
val endVersion: Int
diff --git a/room/room-runtime/src/jvmAndroidMain/kotlin/androidx/room/concurrent/FileLock.jvmAndroid.kt b/room/room-runtime/src/jvmAndroidMain/kotlin/androidx/room/concurrent/FileLock.jvmAndroid.kt
new file mode 100644
index 0000000..0073b80
--- /dev/null
+++ b/room/room-runtime/src/jvmAndroidMain/kotlin/androidx/room/concurrent/FileLock.jvmAndroid.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.concurrent
+
+import java.io.File
+import java.io.FileOutputStream
+import java.nio.channels.FileChannel
+
+/**
+ * A mutually exclusive advisory file lock implementation.
+ *
+ * The lock is cooperative and only protects the critical region from other [FileLock] users in
+ * other process with the same `filename`. Do not use this class on its own, instead use
+ * [ExclusiveLock] which guarantees in-process locking too.
+ *
+ * @param filename The path to the file to protect. Note that an actual lock is not grab on the file
+ * itself but on a temporary file create with the same path but ending with `.lck`.
+ */
+internal actual class FileLock actual constructor(filename: String) {
+ private val lockFilename = "$filename.lck"
+ private var lockChannel: FileChannel? = null
+
+ actual fun lock() {
+ if (lockChannel != null) {
+ return
+ }
+ try {
+ val lockFile = File(lockFilename)
+ lockFile.parentFile?.mkdirs()
+ lockChannel = FileOutputStream(lockFile).channel
+ lockChannel?.lock()
+ } catch (ex: Throwable) {
+ lockChannel?.close()
+ lockChannel = null
+ throw IllegalStateException("Unable to lock file: '$lockFilename'.", ex)
+ }
+ }
+
+ actual fun unlock() {
+ val channel = lockChannel ?: return
+ try {
+ channel.close()
+ } finally {
+ lockChannel = null
+ }
+ }
+}
diff --git a/room/room-runtime/src/jvmMain/kotlin/androidx/room/Room.jvm.kt b/room/room-runtime/src/jvmMain/kotlin/androidx/room/Room.jvm.kt
index 4626508..d10a21c 100644
--- a/room/room-runtime/src/jvmMain/kotlin/androidx/room/Room.jvm.kt
+++ b/room/room-runtime/src/jvmMain/kotlin/androidx/room/Room.jvm.kt
@@ -60,6 +60,16 @@
name: String,
noinline factory: () -> T = { findAndInstantiateDatabaseImpl(T::class.java) }
): RoomDatabase.Builder<T> {
+ require(name.isNotBlank()) {
+ "Cannot build a database with empty name." +
+ " If you are trying to create an in memory database, use Room" +
+ ".inMemoryDatabaseBuilder()."
+ }
+ require(name != ":memory:") {
+ "Cannot build a database with the special name ':memory:'." +
+ " If you are trying to create an in memory database, use Room" +
+ ".inMemoryDatabaseBuilder()."
+ }
return RoomDatabase.Builder(T::class, name, factory)
}
}
diff --git a/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/DatabaseConfiguration.jvmNative.kt b/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/DatabaseConfiguration.jvmNative.kt
index 0d6c83d..a282cee 100644
--- a/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/DatabaseConfiguration.jvmNative.kt
+++ b/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/DatabaseConfiguration.jvmNative.kt
@@ -28,13 +28,23 @@
actual val name: String?,
/* Collection of available migrations. */
actual val migrationContainer: RoomDatabase.MigrationContainer,
+ /* Database callbacks. */
actual val callbacks: List<RoomDatabase.Callback>?,
+ /* The journal mode for this database. */
actual val journalMode: RoomDatabase.JournalMode,
+ /* Whether Room should throw an exception for missing migrations. */
actual val requireMigration: Boolean,
+ /* Whether Room will fallback to destructive migrations on downgrades only .*/
actual val allowDestructiveMigrationOnDowngrade: Boolean,
internal actual val migrationNotRequiredFrom: Set<Int>?,
+ /* List of provided type converters. */
actual val typeConverters: List<Any>,
+ /* List of provided auto migration specs. */
actual val autoMigrationSpecs: List<AutoMigrationSpec>,
+ /* Whether Room will delete all tables or only known tables during destructive migrations. */
+ actual val allowDestructiveMigrationForAllTables: Boolean,
+ /* The SQLite Driver for the database. */
actual val sqliteDriver: SQLiteDriver?,
+ /* The Coroutine context for the database. */
actual val queryCoroutineContext: CoroutineContext?,
)
diff --git a/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/RoomDatabase.jvmNative.kt b/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/RoomDatabase.jvmNative.kt
index 2d4277c..89b1adf 100644
--- a/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/RoomDatabase.jvmNative.kt
+++ b/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/RoomDatabase.jvmNative.kt
@@ -288,8 +288,34 @@
) {
private var driver: SQLiteDriver? = null
- private var queryCoroutineContext: CoroutineContext? = null
private val callbacks = mutableListOf<Callback>()
+ private val typeConverters: MutableList<Any> = mutableListOf()
+ private var journalMode: JournalMode = JournalMode.WRITE_AHEAD_LOGGING
+ private var queryCoroutineContext: CoroutineContext? = null
+
+ /**
+ * Migrations, mapped by from-to pairs.
+ */
+ private val migrationContainer: MigrationContainer = MigrationContainer()
+
+ /**
+ * Versions that don't require migrations, configured via
+ * [fallbackToDestructiveMigrationFrom].
+ */
+ private var migrationsNotRequiredFrom: MutableSet<Int> = mutableSetOf()
+
+ /**
+ * Keeps track of [Migration.startVersion]s and [Migration.endVersion]s added in
+ * [addMigrations] for later validation that makes those versions don't
+ * match any versions passed to [fallbackToDestructiveMigrationFrom].
+ */
+ private val migrationStartAndEndVersions = mutableSetOf<Int>()
+
+ private val autoMigrationSpecs: MutableList<AutoMigrationSpec> = mutableListOf()
+
+ private var requireMigration: Boolean = true
+ private var allowDestructiveMigrationOnDowngrade = false
+ private var allowDestructiveMigrationForAllTables = false
/**
* Sets the [SQLiteDriver] implementation to be used by Room to open database connections.
@@ -304,6 +330,142 @@
}
/**
+ * Adds a migration to the builder.
+ *
+ * Each [Migration] has a start and end versions and Room runs these migrations to bring the
+ * database to the latest version.
+ *
+ * A migration can handle more than 1 version (e.g. if you have a faster path to choose when
+ * going from version 3 to 5 without going to version 4). If Room opens a database at
+ * version 3 and latest version is >= 5, Room will use the migration object that can migrate
+ * from 3 to 5 instead of 3 to 4 and 4 to 5.
+ *
+ * @param migrations The migration objects that modify the database schema with the
+ * necessary changes for a version change.
+ * @return This builder instance.
+ */
+ actual fun addMigrations(vararg migrations: Migration) = apply {
+ for (migration in migrations) {
+ migrationStartAndEndVersions.add(migration.startVersion)
+ migrationStartAndEndVersions.add(migration.endVersion)
+ }
+ migrationContainer.addMigrations(migrations.toList())
+ }
+
+ /**
+ * Adds an auto migration spec instance to the builder.
+ *
+ * @param autoMigrationSpec The auto migration object that is annotated with
+ * [ProvidedAutoMigrationSpec] and is declared in an [AutoMigration] annotation.
+ * @return This builder instance.
+ */
+ actual fun addAutoMigrationSpec(autoMigrationSpec: AutoMigrationSpec) = apply {
+ this.autoMigrationSpecs.add(autoMigrationSpec)
+ }
+
+ /**
+ * Allows Room to destructively recreate database tables if [Migration]s that would
+ * migrate old database schemas to the latest schema version are not found.
+ *
+ * When the database version on the device does not match the latest schema version, Room
+ * runs necessary [Migration]s on the database. If it cannot find the set of [Migration]s
+ * that will bring the database to the current version, it will throw an
+ * [IllegalStateException]. You can call this method to change this behavior to re-create
+ * the database tables instead of crashing.
+ *
+ * To let Room fallback to destructive migration only during a schema downgrade then use
+ * [fallbackToDestructiveMigrationOnDowngrade].
+ *
+ * @param dropAllTables Set to `true` if all tables should be dropped during destructive
+ * migration including those not managed by Room. Recommended value is `true` as otherwise
+ * Room could leave obsolete data when table names or existence changes between versions.
+ * @return This builder instance.
+ */
+ actual fun fallbackToDestructiveMigration(dropAllTables: Boolean) = apply {
+ this.requireMigration = false
+ this.allowDestructiveMigrationOnDowngrade = true
+ this.allowDestructiveMigrationForAllTables = dropAllTables
+ }
+
+ /**
+ * Allows Room to destructively recreate database tables if [Migration]s are not
+ * available when downgrading to old schema versions.
+ *
+ * For details, see [Builder.fallbackToDestructiveMigration].
+ *
+ * @param dropAllTables Set to `true` if all tables should be dropped during destructive
+ * migration including those not managed by Room. Recommended value is `true` as otherwise
+ * Room could leave obsolete data when table names or existence changes between versions.
+ * @return This builder instance.
+ */
+ actual fun fallbackToDestructiveMigrationOnDowngrade(dropAllTables: Boolean) = apply {
+ this.requireMigration = true
+ this.allowDestructiveMigrationOnDowngrade = true
+ this.allowDestructiveMigrationForAllTables = dropAllTables
+ }
+
+ /**
+ * Informs Room that it is allowed to destructively recreate database tables from specific
+ * starting schema versions.
+ *
+ * This functionality is the same [fallbackToDestructiveMigration], except that this method
+ * allows the specification of a set of schema versions for which destructive recreation is
+ * allowed.
+ *
+ * Using this method is preferable to [fallbackToDestructiveMigration] if you want
+ * to allow destructive migrations from some schema versions while still taking advantage
+ * of exceptions being thrown due to unintentionally missing migrations.
+ *
+ * Note: No versions passed to this method may also exist as either starting or ending
+ * versions in the [Migration]s provided via [addMigrations]. If a
+ * version passed to this method is found as a starting or ending version in a Migration, an
+ * exception will be thrown.
+ *
+ * @param dropAllTables Set to `true` if all tables should be dropped during destructive
+ * migration including those not managed by Room. Recommended value is `true` as otherwise
+ * Room could leave obsolete data when table names or existence changes between versions.
+ * @param startVersions The set of schema versions from which Room should use a destructive
+ * migration.
+ * @return This builder instance.
+ */
+ actual fun fallbackToDestructiveMigrationFrom(
+ dropAllTables: Boolean,
+ vararg startVersions: Int
+ ) = apply {
+ for (startVersion in startVersions) {
+ this.migrationsNotRequiredFrom.add(startVersion)
+ }
+ this.allowDestructiveMigrationForAllTables = dropAllTables
+ }
+
+ /**
+ * Adds a type converter instance to the builder.
+ *
+ * @param typeConverter The converter instance that is annotated with
+ * [ProvidedTypeConverter].
+ * @return This builder instance.
+ */
+ actual fun addTypeConverter(typeConverter: Any) = apply {
+ this.typeConverters.add(typeConverter)
+ }
+
+ /**
+ * Sets the journal mode for this database.
+ *
+ * The value is ignored if the builder is for an 'in-memory database'. The journal mode
+ * should be consistent across multiple instances of [RoomDatabase] for a single SQLite
+ * database file.
+ *
+ * The default value is [JournalMode.WRITE_AHEAD_LOGGING].
+ *
+ * @param journalMode The journal mode.
+ * @return This builder instance.
+ */
+ actual fun setJournalMode(journalMode: JournalMode) = apply {
+ this.journalMode = journalMode
+ }
+
+ /**
* Sets the [CoroutineContext] that will be used to execute all asynchronous queries and
* tasks, such as `Flow` emissions and [InvalidationTracker] notifications.
*
@@ -343,16 +505,20 @@
requireNotNull(driver) {
"Cannot create a RoomDatabase without providing a SQLiteDriver via setDriver()."
}
+
+ validateMigrationsNotRequired(migrationStartAndEndVersions, migrationsNotRequiredFrom)
+
val configuration = DatabaseConfiguration(
name = name,
- migrationContainer = MigrationContainer(),
+ migrationContainer = migrationContainer,
callbacks = callbacks,
- journalMode = JournalMode.WRITE_AHEAD_LOGGING,
- requireMigration = false,
- allowDestructiveMigrationOnDowngrade = false,
- migrationNotRequiredFrom = null,
- typeConverters = emptyList(),
- autoMigrationSpecs = emptyList(),
+ journalMode = journalMode,
+ requireMigration = requireMigration,
+ allowDestructiveMigrationOnDowngrade = allowDestructiveMigrationOnDowngrade,
+ migrationNotRequiredFrom = migrationsNotRequiredFrom,
+ typeConverters = typeConverters,
+ autoMigrationSpecs = autoMigrationSpecs,
+ allowDestructiveMigrationForAllTables = allowDestructiveMigrationForAllTables,
sqliteDriver = driver,
queryCoroutineContext = queryCoroutineContext ?: Dispatchers.IO,
)
diff --git a/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/migration/Migration.jvmNative.kt b/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/migration/Migration.jvmNative.kt
index cd3bb8c..a6535fe0 100644
--- a/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/migration/Migration.jvmNative.kt
+++ b/room/room-runtime/src/jvmNativeMain/kotlin/androidx/room/migration/Migration.jvmNative.kt
@@ -34,7 +34,7 @@
*
* @constructor Creates a new migration between [startVersion] and [endVersion] inclusive.
*/
-actual abstract class Migration(
+actual abstract class Migration actual constructor(
actual val startVersion: Int,
actual val endVersion: Int
) {
diff --git a/room/room-runtime/src/nativeMain/kotlin/androidx/room/Room.native.kt b/room/room-runtime/src/nativeMain/kotlin/androidx/room/Room.native.kt
index 319dbea..e794c3a 100644
--- a/room/room-runtime/src/nativeMain/kotlin/androidx/room/Room.native.kt
+++ b/room/room-runtime/src/nativeMain/kotlin/androidx/room/Room.native.kt
@@ -56,6 +56,16 @@
name: String,
noinline factory: () -> T
): RoomDatabase.Builder<T> {
+ require(name.isNotBlank()) {
+ "Cannot build a database with empty name." +
+ " If you are trying to create an in memory database, use Room" +
+ ".inMemoryDatabaseBuilder()."
+ }
+ require(name != ":memory:") {
+ "Cannot build a database with the special name ':memory:'." +
+ " If you are trying to create an in memory database, use Room" +
+ ".inMemoryDatabaseBuilder()."
+ }
return RoomDatabase.Builder(T::class, name, factory)
}
}
diff --git a/room/room-runtime/src/nativeMain/kotlin/androidx/room/concurrent/FileLock.native.kt b/room/room-runtime/src/nativeMain/kotlin/androidx/room/concurrent/FileLock.native.kt
new file mode 100644
index 0000000..3fb8e79
--- /dev/null
+++ b/room/room-runtime/src/nativeMain/kotlin/androidx/room/concurrent/FileLock.native.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.concurrent
+
+import androidx.room.util.stringError
+import kotlinx.cinterop.cValue
+import kotlinx.cinterop.memScoped
+import platform.posix.F_SETLK
+import platform.posix.F_SETLKW
+import platform.posix.F_UNLCK
+import platform.posix.F_WRLCK
+import platform.posix.O_CREAT
+import platform.posix.O_RDWR
+import platform.posix.SEEK_SET
+import platform.posix.S_IRGRP
+import platform.posix.S_IROTH
+import platform.posix.S_IRUSR
+import platform.posix.S_IWUSR
+import platform.posix.close
+import platform.posix.fcntl
+import platform.posix.flock
+import platform.posix.open
+
+/**
+ * A mutually exclusive advisory file lock implementation.
+ *
+ * The lock is cooperative and only protects the critical region from other [FileLock] users in
+ * other process with the same `filename`. Do not use this class on its own, instead use
+ * [ExclusiveLock] which guarantees in-process locking too.
+ *
+ * @param filename The path to the file to protect. Note that an actual lock is not grab on the file
+ * itself but on a temporary file create with the same path but ending with `.lck`.
+ */
+@OptIn(kotlinx.cinterop.ExperimentalForeignApi::class)
+internal actual class FileLock actual constructor(filename: String) {
+ private val lockFilename = "$filename.lck"
+ private var lockFd: Int? = null
+
+ actual fun lock() {
+ if (lockFd != null) {
+ return
+ }
+ // Open flags: open in read-write mode and create if doesn't exist
+ // Open mode: user has read / write permissions, group and others only read (0644)
+ val fd = open(lockFilename, O_RDWR or O_CREAT, S_IWUSR or S_IRUSR or S_IRGRP or S_IROTH)
+ check(fd != -1) { "Unable to open lock file (${stringError()}): '$lockFilename'." }
+ try {
+ val cFlock = cValue<flock> {
+ l_type = F_WRLCK.toShort() // acquire write (exclusive) lock
+ l_whence = SEEK_SET.toShort() // lock from start of file
+ l_start = 0 // lock start offset
+ l_len = 0 // lock all bytes (special meaning)
+ }
+ // Command: 'Set lock waiting' will block until file lock is acquired by process
+ if (memScoped { fcntl(fd, F_SETLKW, cFlock.ptr) } == -1) {
+ error("Unable to lock file (${stringError()}): '$lockFilename'.")
+ }
+ lockFd = fd
+ } catch (ex: Throwable) {
+ close(fd)
+ throw ex
+ }
+ }
+
+ actual fun unlock() {
+ val fd = lockFd ?: return
+ try {
+ val cFlock = cValue<flock> {
+ l_type = F_UNLCK.toShort() // release lock
+ l_whence = SEEK_SET.toShort() // lock from start of file
+ l_start = 0 // lock start offset
+ l_len = 0 // lock all bytes (special meaning)
+ }
+ // Command: 'Set lock' (without waiting because we are unlocking)
+ if (memScoped { fcntl(fd, F_SETLK, cFlock.ptr) } == -1) {
+ error("Unable to unlock file (${stringError()}): '$lockFilename'.")
+ }
+ } finally {
+ close(fd)
+ }
+ lockFd = null
+ }
+}
diff --git a/room/room-runtime/src/nativeMain/kotlin/androidx/room/util/PosixUtil.kt b/room/room-runtime/src/nativeMain/kotlin/androidx/room/util/PosixUtil.kt
new file mode 100644
index 0000000..8a4ff37
--- /dev/null
+++ b/room/room-runtime/src/nativeMain/kotlin/androidx/room/util/PosixUtil.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.util
+
+import kotlinx.cinterop.ExperimentalForeignApi
+import kotlinx.cinterop.toKString
+import platform.posix.errno
+import platform.posix.strerror
+
+/**
+ * Convenience function to get a String description of the last error number.
+ */
+@OptIn(ExperimentalForeignApi::class)
+fun stringError(): String = strerror(errno)?.toKString() ?: "Unknown error"
diff --git a/room/room-runtime/src/nativeTest/kotlin/androidx.room/concurrent/FileLockTest.kt b/room/room-runtime/src/nativeTest/kotlin/androidx.room/concurrent/FileLockTest.kt
new file mode 100644
index 0000000..2b25cb9
--- /dev/null
+++ b/room/room-runtime/src/nativeTest/kotlin/androidx.room/concurrent/FileLockTest.kt
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.concurrent
+
+import androidx.kruth.assertThat
+import androidx.kruth.assertWithMessage
+import androidx.room.util.stringError
+import kotlin.random.Random
+import kotlin.test.AfterTest
+import kotlin.test.BeforeTest
+import kotlin.test.Test
+import kotlinx.cinterop.ExperimentalForeignApi
+import kotlinx.cinterop.alloc
+import kotlinx.cinterop.memScoped
+import kotlinx.cinterop.ptr
+import okio.FileSystem
+import okio.Path.Companion.toPath
+import platform.posix.exit
+import platform.posix.fork
+import platform.posix.gettimeofday
+import platform.posix.remove
+import platform.posix.timeval
+import platform.posix.usleep
+import platform.posix.waitpid
+
+@OptIn(ExperimentalForeignApi::class)
+class FileLockTest {
+
+ private val testFile = "/tmp/test-${Random.nextInt()}.db"
+ private val parentLogFile = "/tmp/test-${Random.nextInt()}-parent"
+ private val childLogFile = "/tmp/test-${Random.nextInt()}-child"
+
+ @BeforeTest
+ fun before() {
+ remove(testFile)
+ remove(parentLogFile)
+ remove(childLogFile)
+ }
+
+ @AfterTest
+ fun after() {
+ remove(testFile)
+ remove(parentLogFile)
+ remove(childLogFile)
+ }
+
+ @Test
+ fun processLock() {
+ val pid = fork()
+ if (pid == -1) {
+ error("fork() failed: ${stringError()}")
+ }
+
+ // Forked code is next, both process concurrently attempt to acquire the file lock,
+ // recording the time they did so to later validate exclusive access to the critical
+ // region.
+ val timeStamps = TimeStamps()
+ val lock = FileLock(testFile)
+ timeStamps.before = getUnixMicroseconds()
+ lock.lock()
+ // sleep for 200ms total to give a chance for contention
+ usleep(100u * 1000u)
+ timeStamps.during = getUnixMicroseconds()
+ usleep(100u * 1000u)
+ lock.unlock()
+ timeStamps.after = getUnixMicroseconds()
+ writeTimestamps(
+ logFile = if (pid == 0) childLogFile else parentLogFile,
+ timeStamps = timeStamps,
+ )
+
+ when (pid) {
+ // Child process, terminate
+ 0 -> {
+ exit(0)
+ }
+ // Parent process (this test), wait for child
+ else -> {
+ val result = waitpid(pid, null, 0)
+ if (result == -1) {
+ error("wait() failed: ${stringError()}")
+ }
+ }
+ }
+
+ val parentTimeStamps = readTimestamps(parentLogFile)
+ val childTimeStamps = readTimestamps(childLogFile)
+
+ // Initial check, attempt was before acquire, and release was after acquired
+ assertThat(parentTimeStamps.before).isLessThan(parentTimeStamps.during)
+ assertThat(parentTimeStamps.during).isLessThan(parentTimeStamps.after)
+ assertThat(childTimeStamps.before).isLessThan(childTimeStamps.during)
+ assertThat(childTimeStamps.during).isLessThan(childTimeStamps.after)
+
+ // Find out who got the lock first
+ val (first, second) = if (parentTimeStamps.during < childTimeStamps.during) {
+ parentTimeStamps to childTimeStamps
+ } else {
+ childTimeStamps to parentTimeStamps
+ }
+ // Now really validate second acquired the lock *after* first released it
+ assertWithMessage("Comparing first unlock time with second acquire time")
+ .that(first.after).isLessThan(second.during)
+ }
+
+ private fun writeTimestamps(logFile: String, timeStamps: TimeStamps) {
+ FileSystem.SYSTEM.write(logFile.toPath(), mustCreate = true) {
+ writeUtf8("${timeStamps.before},${timeStamps.during},${timeStamps.after}")
+ }
+ }
+
+ private fun readTimestamps(logFile: String): TimeStamps {
+ return FileSystem.SYSTEM.read(logFile.toPath()) {
+ val stamps = readUtf8().split(",")
+ TimeStamps(
+ before = stamps[0].toLong(),
+ during = stamps[1].toLong(),
+ after = stamps[2].toLong()
+ )
+ }
+ }
+
+ // All times are in microseconds
+ data class TimeStamps(
+ var before: Long = -1,
+ var during: Long = -1,
+ var after: Long = -1
+ )
+
+ private fun getUnixMicroseconds(): Long = memScoped {
+ val tv = alloc<timeval>()
+ gettimeofday(tv.ptr, null)
+ return (tv.tv_sec * 1000000) + tv.tv_usec
+ }
+}
diff --git a/room/room-testing/src/androidMain/kotlin/androidx/room/testing/MigrationTestHelper.android.kt b/room/room-testing/src/androidMain/kotlin/androidx/room/testing/MigrationTestHelper.android.kt
index 9e4a324..f62ae11 100644
--- a/room/room-testing/src/androidMain/kotlin/androidx/room/testing/MigrationTestHelper.android.kt
+++ b/room/room-testing/src/androidMain/kotlin/androidx/room/testing/MigrationTestHelper.android.kt
@@ -543,7 +543,15 @@
this.driverWrapper = DriverWrapper(supportDriver)
}
- override fun openConnection() = driverWrapper.open(configuration.name ?: ":memory:")
+ override fun openConnection(): SQLiteConnection {
+ val name = configuration.name
+ val filename = if (configuration.name != null) {
+ configuration.context.getDatabasePath(name).absolutePath
+ } else {
+ ":memory:"
+ }
+ return driverWrapper.open(filename)
+ }
inner class SupportOpenHelperCallback(
version: Int
diff --git a/room/room-testing/src/jvmMain/kotlin/androidx/room/testing/MigrationTestHelper.jvm.kt b/room/room-testing/src/jvmMain/kotlin/androidx/room/testing/MigrationTestHelper.jvm.kt
index fa527a3..bab0e16 100644
--- a/room/room-testing/src/jvmMain/kotlin/androidx/room/testing/MigrationTestHelper.jvm.kt
+++ b/room/room-testing/src/jvmMain/kotlin/androidx/room/testing/MigrationTestHelper.jvm.kt
@@ -175,6 +175,7 @@
migrationNotRequiredFrom = null,
typeConverters = emptyList(),
autoMigrationSpecs = emptyList(),
+ allowDestructiveMigrationForAllTables = false,
sqliteDriver = driver,
queryCoroutineContext = null
)
diff --git a/room/room-testing/src/nativeMain/kotlin/androidx/room/testing/MigrationTestHelper.native.kt b/room/room-testing/src/nativeMain/kotlin/androidx/room/testing/MigrationTestHelper.native.kt
index c0e69bc..9ab308c 100644
--- a/room/room-testing/src/nativeMain/kotlin/androidx/room/testing/MigrationTestHelper.native.kt
+++ b/room/room-testing/src/nativeMain/kotlin/androidx/room/testing/MigrationTestHelper.native.kt
@@ -173,6 +173,7 @@
migrationNotRequiredFrom = null,
typeConverters = emptyList(),
autoMigrationSpecs = emptyList(),
+ allowDestructiveMigrationForAllTables = false,
sqliteDriver = driver,
queryCoroutineContext = null
)
diff --git a/settings.gradle b/settings.gradle
index d2d31c0..b2fcfbd 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -33,6 +33,7 @@
classpath("androidx.build.gradle.gcpbuildcache:gcpbuildcache:1.0.0-beta07")
}
}
+def supportRootFolder = buildscript.sourceFile.getParentFile()
skikoSetup.defineSkikoInVersionCatalog(settings)
// Abort immediately if we're running in Studio, but not a managed instance of Studio.
@@ -45,7 +46,7 @@
// Makes strong assumptions about the project structure.
def prebuiltsRoot = new File(
- buildscript.sourceFile.parentFile.parentFile.parentFile,
+ supportRootFolder.parentFile.parentFile,
"prebuilts"
).absolutePath
def rootProjectRepositories
@@ -64,6 +65,7 @@
// on each project
project.repositories.addAll(rootProjectRepositories)
}
+ project.ext.supportRootFolder = supportRootFolder
project.ext.prebuiltsRoot = prebuiltsRoot
init.chooseBuildDirectory(
new File("${buildscript.sourceFile.parent}/../.."), rootProject.name, project
@@ -277,7 +279,10 @@
includeProject(name, null, filter)
}
// createProjectDependencyGraph is provided by project-dependency-graph.groovy
-ext.projectDependencyGraph = createProjectDependencyGraph(settings)
+ext.projectDependencyGraph = createProjectDependencyGraph(
+ settings,
+ providers.gradleProperty("androidx.constraints").getOrElse("false").toBoolean()
+)
// A set of projects that the user asked to filter to.
@Field Set<String> filteredProjects = new HashSet<String>()
filteredProjects.add(":lint-checks")
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/Surface.kt b/tv/tv-material/src/main/java/androidx/tv/material3/Surface.kt
index b5b738d..92792c2 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/Surface.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/Surface.kt
@@ -210,7 +210,7 @@
*
* To manually retrieve the content color inside a surface, use [LocalContentColor].
*
- * @param selected whether or not this Surface is toggled on or off
+ * @param selected whether or not this Surface is selected
* @param onClick callback to be invoked when the selectable Surface is clicked.
* @param modifier [Modifier] to be applied to the layout corresponding to the surface
* @param onLongClick callback to be called when the selectable surface is long clicked
diff --git a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/PositionIndicator.kt b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/PositionIndicator.kt
index 6ac0c34..da9a1a1 100644
--- a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/PositionIndicator.kt
+++ b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/PositionIndicator.kt
@@ -200,7 +200,7 @@
ScrollStateAdapter(scrollState),
indicatorHeight = 50.dp,
indicatorWidth = 4.dp,
- paddingHorizontal = 5.dp,
+ paddingHorizontal = PositionIndicatorDefaults.horizontalPadding,
modifier = modifier,
reverseDirection = reverseDirection,
fadeInAnimationSpec = fadeInAnimationSpec,
@@ -280,7 +280,7 @@
),
indicatorHeight = 50.dp,
indicatorWidth = 4.dp,
- paddingHorizontal = 5.dp,
+ paddingHorizontal = PositionIndicatorDefaults.horizontalPadding,
modifier = modifier,
reverseDirection = reverseDirection,
fadeInAnimationSpec = fadeInAnimationSpec,
@@ -349,7 +349,7 @@
),
indicatorHeight = 50.dp,
indicatorWidth = 4.dp,
- paddingHorizontal = 5.dp,
+ paddingHorizontal = PositionIndicatorDefaults.horizontalPadding,
modifier = modifier,
reverseDirection = reverseDirection
)
@@ -396,7 +396,7 @@
),
indicatorHeight = 50.dp,
indicatorWidth = 4.dp,
- paddingHorizontal = 5.dp,
+ paddingHorizontal = PositionIndicatorDefaults.horizontalPadding,
modifier = modifier,
reverseDirection = reverseDirection,
fadeInAnimationSpec = fadeInAnimationSpec,
@@ -1013,6 +1013,11 @@
*/
val visibilityAnimationSpec: AnimationSpec<Float> =
spring(stiffness = Spring.StiffnessMediumLow)
+
+ /**
+ * Horizontal padding from the PositionIndicator to the screen edge.
+ */
+ internal val horizontalPadding = 2.dp
}
internal fun handleFadeOut(
diff --git a/wear/compose/compose-material3/build.gradle b/wear/compose/compose-material3/build.gradle
index 955646f..d18ada1 100644
--- a/wear/compose/compose-material3/build.gradle
+++ b/wear/compose/compose-material3/build.gradle
@@ -85,7 +85,7 @@
"developers to write Jetpack Compose applications for Wearable devices that " +
"implement Wear Material 3 Design UX guidelines and specifications. It builds upon " +
"the Jetpack Compose libraries."
- samples(project(":wear:compose:compose-material-samples"))
+ samples(project(":wear:compose:compose-material3-samples"))
}
tasks.withType(KotlinCompile).configureEach {
diff --git a/wear/compose/integration-tests/demos/build.gradle b/wear/compose/integration-tests/demos/build.gradle
index 324e62f..9774ec0 100644
--- a/wear/compose/integration-tests/demos/build.gradle
+++ b/wear/compose/integration-tests/demos/build.gradle
@@ -25,8 +25,8 @@
defaultConfig {
applicationId "androidx.wear.compose.integration.demos"
minSdk 25
- versionCode 21
- versionName "1.21"
+ versionCode 22
+ versionName "1.22"
}
buildTypes {
diff --git a/wear/protolayout/protolayout-expression/src/main/java/androidx/wear/protolayout/expression/Fingerprint.java b/wear/protolayout/protolayout-expression/src/main/java/androidx/wear/protolayout/expression/Fingerprint.java
index 004bea8..d92eaa5 100644
--- a/wear/protolayout/protolayout-expression/src/main/java/androidx/wear/protolayout/expression/Fingerprint.java
+++ b/wear/protolayout/protolayout-expression/src/main/java/androidx/wear/protolayout/expression/Fingerprint.java
@@ -41,7 +41,6 @@
@RestrictTo(Scope.LIBRARY_GROUP)
public final class Fingerprint {
private static final int DEFAULT_VALUE = 0;
- private static final int DISCARDED_VALUE = -1;
private final int selfTypeValue;
private int selfPropsValue;
private int childNodesValue;
@@ -65,12 +64,9 @@
/**
* Get the aggregate numeric fingerprint, representing the message itself as well as all its
- * child nodes. Returns -1 if the fingerprint is discarded.
+ * child nodes.
*/
public int aggregateValueAsInt() {
- if (selfPropsValue == DISCARDED_VALUE) {
- return DISCARDED_VALUE;
- }
int aggregateValue = selfTypeValue;
aggregateValue = (31 * aggregateValue) + selfPropsValue;
aggregateValue = (31 * aggregateValue) + childNodesValue;
@@ -84,72 +80,29 @@
/**
* Get the numeric fingerprint for the message's properties only, excluding its type and child
- * nodes. Returns -1 if the fingerprint is discarded.
+ * nodes.
*/
public int selfPropsValue() {
return selfPropsValue;
}
- /**
- * Get the numeric fingerprint for the child nodes. Returns -1 if the fingerprint for children
- * is discarded.
- *
- * <p>Note: If {@link #childNodes()} is empty, the children should be considered fully discarded
- * at this level. Otherwise, at least one of the children is discarded (self discard) and the
- * fingerprint of each children should be checked individually.
- */
+ /** Get the numeric fingerprint for the child nodes. */
public int childNodesValue() {
return childNodesValue;
}
- /**
- * Get the child nodes. Returns empty list if the node has no children, or if the child
- * fingerprints are discarded.
- */
+ /** Get the child nodes. Returns empty list if the node has no children. */
public @NonNull List<Fingerprint> childNodes() {
return childNodes == null ? Collections.emptyList() : childNodes;
}
/** Add a child node to this fingerprint. */
public void addChildNode(@NonNull Fingerprint childNode) {
- // Even if the children are not discarded directly through discardValued(true), if one of
- // them is individually discarded, we need to propagate that so that the differ knows it
- // has to go down one more level. That's why childNodesValue == DISCARDED_VALUE doesn't
- // necessarily mean all of the children are discarded. childNodes is used to
- // differentiate these two cases.
- if (selfPropsValue == DISCARDED_VALUE
- && childNodesValue == DISCARDED_VALUE
- && childNodes == null) {
- return;
- }
if (childNodes == null) {
childNodes = new ArrayList<>();
}
childNodes.add(childNode);
- if (childNode.selfPropsValue == DISCARDED_VALUE) {
- childNodesValue = DISCARDED_VALUE;
- } else if (childNodesValue != DISCARDED_VALUE) {
- childNodesValue = (31 * childNodesValue) + childNode.aggregateValueAsInt();
- }
- }
-
- /**
- * Discard values of this fingerprint.
- *
- * @param includeChildren if True, discards children values of this fingerprints too.
- */
- public void discardValues(boolean includeChildren) {
- if (selfPropsValue == DISCARDED_VALUE
- && childNodesValue == DISCARDED_VALUE
- && !includeChildren) {
- throw new IllegalStateException(
- "Container is in discarded state. Children can't be reinstated.");
- }
- selfPropsValue = DISCARDED_VALUE;
- if (includeChildren) {
- childNodesValue = DISCARDED_VALUE;
- childNodes = null;
- }
+ childNodesValue = (31 * childNodesValue) + childNode.aggregateValueAsInt();
}
/** Record a property value being updated. */
diff --git a/wear/protolayout/protolayout-expression/src/test/java/androidx/wear/protolayout/expression/FingerprintTest.java b/wear/protolayout/protolayout-expression/src/test/java/androidx/wear/protolayout/expression/FingerprintTest.java
index ad075ef..2f99ad8 100644
--- a/wear/protolayout/protolayout-expression/src/test/java/androidx/wear/protolayout/expression/FingerprintTest.java
+++ b/wear/protolayout/protolayout-expression/src/test/java/androidx/wear/protolayout/expression/FingerprintTest.java
@@ -44,34 +44,6 @@
}
@Test
- public void discard_clearsSelfFingerprint() {
- Fingerprint parentFingerPrint = new Fingerprint(SELF_TYPE_VALUE);
- Fingerprint childFingerPrint = new Fingerprint(SELF_TYPE_VALUE);
- childFingerPrint.recordPropertyUpdate(FIELD_1, VALUE_HASH1);
- parentFingerPrint.addChildNode(childFingerPrint);
-
- parentFingerPrint.discardValues(/* includeChildren= */ false);
-
- assertThat(parentFingerPrint.selfPropsValue()).isEqualTo(DISCARDED_VALUE);
- assertThat(parentFingerPrint.childNodes()).containsExactly(childFingerPrint);
- assertThat(parentFingerPrint.childNodesValue()).isNotEqualTo(DISCARDED_VALUE);
- }
-
- @Test
- public void discard_includeChildren_clearsSelfAndChildrenFingerprint() {
- Fingerprint parentFingerPrint = new Fingerprint(SELF_TYPE_VALUE);
- Fingerprint childFingerPrint = new Fingerprint(SELF_TYPE_VALUE);
- childFingerPrint.recordPropertyUpdate(FIELD_1, VALUE_HASH1);
- parentFingerPrint.addChildNode(childFingerPrint);
-
- parentFingerPrint.discardValues(/* includeChildren= */ true);
-
- assertThat(parentFingerPrint.selfPropsValue()).isEqualTo(DISCARDED_VALUE);
- assertThat(parentFingerPrint.childNodes()).isEmpty();
- assertThat(parentFingerPrint.childNodesValue()).isEqualTo(DISCARDED_VALUE);
- }
-
- @Test
public void toProto_fromProto() {
Fingerprint parentFingerPrint = new Fingerprint(SELF_TYPE_VALUE);
Fingerprint childFingerPrint = new Fingerprint(SELF_TYPE_VALUE);
diff --git a/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/TestCasesGenerator.java b/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/TestCasesGenerator.java
index 032a9e0..6d3cbd4 100644
--- a/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/TestCasesGenerator.java
+++ b/wear/protolayout/protolayout-material/src/androidTest/java/androidx/wear/protolayout/material/TestCasesGenerator.java
@@ -22,6 +22,9 @@
import static androidx.wear.protolayout.LayoutElementBuilders.HORIZONTAL_ALIGN_END;
import static androidx.wear.protolayout.LayoutElementBuilders.HORIZONTAL_ALIGN_START;
+import static androidx.wear.protolayout.material.ProgressIndicatorDefaults.GAP_END_ANGLE;
+import static androidx.wear.protolayout.material.ProgressIndicatorDefaults.GAP_START_ANGLE;
+
import static com.google.common.collect.ImmutableMap.toImmutableMap;
import android.content.Context;
@@ -75,6 +78,7 @@
String labelText = "Secondary label";
String largeChipText = "Action";
HashMap<String, LayoutElement> testCases = new HashMap<>();
+
testCases.put(
"default_icon_button_golden" + NORMAL_SCALE_SUFFIX,
new Button.Builder(context, clickable).setIconContent(ICON_ID).build());
@@ -110,6 +114,7 @@
testCases.put(
"default_image_button_golden" + NORMAL_SCALE_SUFFIX,
new Button.Builder(context, clickable).setImageContent(AVATAR).build());
+
testCases.put(
"default_chip_maintext_golden" + goldenSuffix,
new Chip.Builder(context, clickable, deviceParameters)
@@ -300,8 +305,8 @@
testCases.put(
"default_gap_circularprogressindicator",
new CircularProgressIndicator.Builder()
- .setStartAngle(ProgressIndicatorDefaults.GAP_START_ANGLE)
- .setEndAngle(ProgressIndicatorDefaults.GAP_END_ANGLE)
+ .setStartAngle(GAP_START_ANGLE)
+ .setEndAngle(GAP_END_ANGLE)
.build());
testCases.put(
"default_full_90_circularprogressindicator",
@@ -310,8 +315,8 @@
"default_gap_90_circularprogressindicator",
new CircularProgressIndicator.Builder()
.setProgress(0.25f)
- .setStartAngle(ProgressIndicatorDefaults.GAP_START_ANGLE)
- .setEndAngle(ProgressIndicatorDefaults.GAP_END_ANGLE)
+ .setStartAngle(GAP_START_ANGLE)
+ .setEndAngle(GAP_END_ANGLE)
.build());
testCases.put(
"custom_gap_45_circularprogressindicator",
@@ -410,7 +415,7 @@
* as it should point on the same size independent image.
*/
@NonNull
- @SuppressWarnings("deprecation")
+ @SuppressWarnings("deprecation") // TEXT_OVERFLOW_ELLIPSIZE_END
private static ImmutableMap<String, Layout> generateTextTestCasesForLanguage(
@NonNull Context context,
@NonNull DeviceParameters deviceParameters,
@@ -418,12 +423,15 @@
@NonNull String primaryLabel,
@NonNull String shortText,
@NonNull String longText) {
+
HashMap<String, LayoutElement> testCases = new HashMap<>();
+
Clickable clickable =
new Clickable.Builder()
.setOnClick(new LaunchAction.Builder().build())
.setId("action_id")
.build();
+
testCases.put(
"custom_text_golden" + goldenSuffix,
new Text.Builder(context, shortText)
@@ -482,6 +490,7 @@
.setChipColors(
new ChipColors(Color.YELLOW, Color.GREEN, Color.BLACK, Color.GRAY))
.build());
+
return collectTestCases(testCases);
}
diff --git a/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/Chip.java b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/Chip.java
index a30c4c7..e5504f0 100644
--- a/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/Chip.java
+++ b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/Chip.java
@@ -368,7 +368,7 @@
}
}
- @SuppressWarnings("deprecation") // ELLIPSIZE_END as existing API
+ @SuppressWarnings("deprecation") // TEXT_OVERFLOW_ELLIPSIZE_END as existing API
private void setCorrectContent() {
if (mImageResourceId != null) {
Image icon =
diff --git a/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/Typography.java b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/Typography.java
index a297558..9e1613e 100644
--- a/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/Typography.java
+++ b/wear/protolayout/protolayout-material/src/main/java/androidx/wear/protolayout/material/Typography.java
@@ -185,7 +185,7 @@
@NonNull
@SuppressLint("ResourceType")
- @SuppressWarnings("deprecation")
+ @SuppressWarnings("deprecation") // scaledDensity, b/335215227
// This is a helper function to make the font not scalable. It should interpret in value as DP
// and convert it to SP which is needed to be passed in as a font size. However, we will pass an
// SP object to it, because the default style is defined in it, but for the case when the font
diff --git a/wear/protolayout/protolayout/api/current.txt b/wear/protolayout/protolayout/api/current.txt
index 24afe14..e950512 100644
--- a/wear/protolayout/protolayout/api/current.txt
+++ b/wear/protolayout/protolayout/api/current.txt
@@ -1153,8 +1153,8 @@
}
@androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=400) public static final class ModifiersBuilders.Transformation {
- method public androidx.wear.protolayout.DimensionBuilders.PivotDimension? getPivotX();
- method public androidx.wear.protolayout.DimensionBuilders.PivotDimension? getPivotY();
+ method public androidx.wear.protolayout.DimensionBuilders.PivotDimension getPivotX();
+ method public androidx.wear.protolayout.DimensionBuilders.PivotDimension getPivotY();
method public androidx.wear.protolayout.DimensionBuilders.DegreesProp getRotation();
method public androidx.wear.protolayout.TypeBuilders.FloatProp getScaleX();
method public androidx.wear.protolayout.TypeBuilders.FloatProp getScaleY();
diff --git a/wear/protolayout/protolayout/api/restricted_current.txt b/wear/protolayout/protolayout/api/restricted_current.txt
index 24afe14..e950512 100644
--- a/wear/protolayout/protolayout/api/restricted_current.txt
+++ b/wear/protolayout/protolayout/api/restricted_current.txt
@@ -1153,8 +1153,8 @@
}
@androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=400) public static final class ModifiersBuilders.Transformation {
- method public androidx.wear.protolayout.DimensionBuilders.PivotDimension? getPivotX();
- method public androidx.wear.protolayout.DimensionBuilders.PivotDimension? getPivotY();
+ method public androidx.wear.protolayout.DimensionBuilders.PivotDimension getPivotX();
+ method public androidx.wear.protolayout.DimensionBuilders.PivotDimension getPivotY();
method public androidx.wear.protolayout.DimensionBuilders.DegreesProp getRotation();
method public androidx.wear.protolayout.TypeBuilders.FloatProp getScaleX();
method public androidx.wear.protolayout.TypeBuilders.FloatProp getScaleY();
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ModifiersBuilders.java b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ModifiersBuilders.java
index a6bb9fe..c3f280c 100644
--- a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ModifiersBuilders.java
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ModifiersBuilders.java
@@ -1399,28 +1399,30 @@
}
/**
- * Sets the x offset of the point around which the element is rotated and scaled.
- * Dynamic value is supported.
+ * Gets the horizontal location of the point around which the element is rotated and scaled.
+ * With type {@link DpProp}, it is the offset from the element center; otherwise with type
+ * {@link BoundingBoxRatio}, it is the location proportional to the bounding box width.
*/
- @Nullable
+ @NonNull
public PivotDimension getPivotX() {
if (mImpl.hasPivotX()) {
return DimensionBuilders.pivotDimensionFromProto(mImpl.getPivotX());
} else {
- return null;
+ return new DpProp.Builder(0f).build();
}
}
/**
- * Gets the y offset of the point around which the element is rotated and scaled.
- * Dynamic value is supported.
+ * Gets the vertical location of the point around which the element is rotated and scaled.
+ * With type {@link DpProp}, it is the offset from the element center; otherwise with type
+ * {@link BoundingBoxRatio}, it is the location proportional to the bounding box height.
*/
- @Nullable
+ @NonNull
public PivotDimension getPivotY() {
if (mImpl.hasPivotY()) {
return DimensionBuilders.pivotDimensionFromProto(mImpl.getPivotY());
} else {
- return null;
+ return new DpProp.Builder(0f).build();
}
}
@@ -1548,8 +1550,10 @@
}
/**
- * Sets the x offset of the point around which the element is rotated and scaled.
- * Dynamic value is supported. If not set, defaults to the element center.
+ * Sets the horizontal location of the point around which the element is rotated and
+ * scaled. With type {@link DpProp}, it is the offset from the element center; otherwise
+ * with type {@link BoundingBoxRatio}, it is the location proportional to the bounding
+ * box width. Dynamic value is supported. If not set, defaults to the element center.
*/
@RequiresSchemaVersion(major = 1, minor = 400)
@NonNull
@@ -1561,8 +1565,10 @@
}
/**
- * Sets the y offset of the point around which the element is rotated and scaled.
- * Dynamic value is supported. If not set, defaults to the element center.
+ * Sets the vertical location of the point around which the element is rotated and
+ * scaled. With type {@link DpProp}, it is the offset from the element center; otherwise
+ * with type {@link BoundingBoxRatio}, it is the location proportional to the bounding
+ * box height. Dynamic value is supported. If not set, defaults to the element center.
*/
@RequiresSchemaVersion(major = 1, minor = 400)
@NonNull
diff --git a/wear/tiles/tiles-proto/src/main/proto/events.proto b/wear/tiles/tiles-proto/src/main/proto/events.proto
index 2859a2e..a9e1400 100644
--- a/wear/tiles/tiles-proto/src/main/proto/events.proto
+++ b/wear/tiles/tiles-proto/src/main/proto/events.proto
@@ -8,26 +8,59 @@
// Event fired when a tile has been added to the carousel.
message TileAddEvent {
- // The ID of the tile added to the carousel.
+ // Instance ID of the added tile, allocated when the tile instance was added
+ // to the carousel. This ID will remain the same for this tile instance as
+ // long it is not removed from the carousel.
uint32 tile_id = 1;
}
// Event fired when a tile has been removed from the carousel.
message TileRemoveEvent {
- // The ID of the tile removed from the carousel.
+ // Instance ID of the removed tile, allocated when the tile instance
+ // was added to the carousel.
uint32 tile_id = 1;
}
// Event fired when a tile is swiped to by the user (i.e. it's visible on
// screen).
message TileEnterEvent {
- // The ID of the entered tile.
+ // Instance ID of the tile, allocated when the tile instance is added to the
+ // carousel. This ID will remain the same for this tile instance as long it is
+ // not removed from the carousel.
uint32 tile_id = 1;
}
// Event fired when a tile is swiped away from by the user (i.e. it's no longer
// visible on screen).
message TileLeaveEvent {
- // The ID of the tile.
+ // Instance ID of the tile, allocated when the tile instance is added to the
+ // carousel. This ID will remain the same for this tile instance as long it is
+ // not removed from the carousel.
uint32 tile_id = 1;
}
+
+// Event reported when a user interacts with a tile (e.g. entering or leaving a tile).
+message TileInteractionEvent {
+ // Instance ID of the tile, allocated when the tile instance is added to the
+ // carousel. This ID will remain the same for this tile instance as long it is
+ // not removed from the carousel.
+ uint32 tile_id = 1;
+ // The timestamp of when the interaction was reported.
+ uint64 timestamp_epoch_millis = 2;
+ oneof inner {
+ // Enter event
+ TileEnter enter = 3;
+ // Leave event
+ TileLeave leave = 4;
+ }
+}
+
+// Event reported when a tile is swiped to by the user (i.e. it's visible on
+// screen).
+message TileEnter {
+}
+
+// Event reported when a tile is swiped away from by the user (i.e. it's no longer
+// visible on screen).
+message TileLeave {
+}
diff --git a/wear/tiles/tiles/api/current.txt b/wear/tiles/tiles/api/current.txt
index 5a0fee4..8ca5721 100644
--- a/wear/tiles/tiles/api/current.txt
+++ b/wear/tiles/tiles/api/current.txt
@@ -275,6 +275,22 @@
method public androidx.wear.tiles.EventBuilders.TileEnterEvent.Builder setTileId(int);
}
+ @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=400) public static final class EventBuilders.TileInteractionEvent {
+ method public int getEventType();
+ method public int getTileId();
+ method public java.time.Instant getTimestamp();
+ field public static final int ENTER = 1; // 0x1
+ field public static final int LEAVE = 2; // 0x2
+ field public static final int UNKNOWN = 0; // 0x0
+ }
+
+ public static final class EventBuilders.TileInteractionEvent.Builder {
+ ctor public EventBuilders.TileInteractionEvent.Builder(int);
+ method public androidx.wear.tiles.EventBuilders.TileInteractionEvent build();
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=400) public androidx.wear.tiles.EventBuilders.TileInteractionEvent.Builder setTileId(int);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=400) public androidx.wear.tiles.EventBuilders.TileInteractionEvent.Builder setTimestamp(java.time.Instant);
+ }
+
public static final class EventBuilders.TileLeaveEvent {
method public int getTileId();
}
diff --git a/wear/tiles/tiles/api/restricted_current.txt b/wear/tiles/tiles/api/restricted_current.txt
index 5a0fee4..8ca5721 100644
--- a/wear/tiles/tiles/api/restricted_current.txt
+++ b/wear/tiles/tiles/api/restricted_current.txt
@@ -275,6 +275,22 @@
method public androidx.wear.tiles.EventBuilders.TileEnterEvent.Builder setTileId(int);
}
+ @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=400) public static final class EventBuilders.TileInteractionEvent {
+ method public int getEventType();
+ method public int getTileId();
+ method public java.time.Instant getTimestamp();
+ field public static final int ENTER = 1; // 0x1
+ field public static final int LEAVE = 2; // 0x2
+ field public static final int UNKNOWN = 0; // 0x0
+ }
+
+ public static final class EventBuilders.TileInteractionEvent.Builder {
+ ctor public EventBuilders.TileInteractionEvent.Builder(int);
+ method public androidx.wear.tiles.EventBuilders.TileInteractionEvent build();
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=400) public androidx.wear.tiles.EventBuilders.TileInteractionEvent.Builder setTileId(int);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=400) public androidx.wear.tiles.EventBuilders.TileInteractionEvent.Builder setTimestamp(java.time.Instant);
+ }
+
public static final class EventBuilders.TileLeaveEvent {
method public int getTileId();
}
diff --git a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/EventBuilders.java b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/EventBuilders.java
index 156405f..c2ac8b3 100644
--- a/wear/tiles/tiles/src/main/java/androidx/wear/tiles/EventBuilders.java
+++ b/wear/tiles/tiles/src/main/java/androidx/wear/tiles/EventBuilders.java
@@ -16,18 +16,22 @@
package androidx.wear.tiles;
+import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
+import androidx.wear.protolayout.expression.RequiresSchemaVersion;
import androidx.wear.tiles.proto.EventProto;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.time.Instant;
+
/** Builders for messages used when events happen in the Tiles system. */
public final class EventBuilders {
private EventBuilders() {}
- /**
- * Event fired when a tile has been added to the carousel.
- */
+ /** Event fired when a tile has been added to the carousel. */
public static final class TileAddEvent {
private final EventProto.TileAddEvent mImpl;
@@ -72,7 +76,9 @@
public Builder() {}
/**
- * Sets the ID of the tile added to the carousel.
+ * Sets instance ID of the added tile, allocated when the tile instance was added to the
+ * carousel. This ID will remain the same for this tile instance as long it is not
+ * removed from the carousel.
*/
@NonNull
public Builder setTileId(int tileId) {
@@ -88,9 +94,7 @@
}
}
- /**
- * Event fired when a tile has been removed from the carousel.
- */
+ /** Event fired when a tile has been removed from the carousel. */
public static final class TileRemoveEvent {
private final EventProto.TileRemoveEvent mImpl;
@@ -135,7 +139,8 @@
public Builder() {}
/**
- * Sets the ID of the tile removed from the carousel.
+ * Sets instance ID of the removed tile, allocated when the tile instance was added to
+ * the carousel.
*/
@NonNull
public Builder setTileId(int tileId) {
@@ -151,9 +156,7 @@
}
}
- /**
- * Event fired when a tile is swiped to by the user (i.e. it's visible on screen).
- */
+ /** Event fired when a tile is swiped to by the user (i.e. it's visible on screen). */
public static final class TileEnterEvent {
private final EventProto.TileEnterEvent mImpl;
@@ -198,7 +201,9 @@
public Builder() {}
/**
- * Sets the ID of the entered tile.
+ * Sets instance ID of the tile, allocated when the tile instance is added to the
+ * carousel. This ID will remain the same for this tile instance as long it is not
+ * removed from the carousel.
*/
@NonNull
public Builder setTileId(int tileId) {
@@ -262,7 +267,9 @@
public Builder() {}
/**
- * Sets the ID of the tile.
+ * Sets instance ID of the tile, allocated when the tile instance is added to the
+ * carousel. This ID will remain the same for this tile instance as long it is not
+ * removed from the carousel.
*/
@NonNull
public Builder setTileId(int tileId) {
@@ -277,4 +284,142 @@
}
}
}
+
+ /** Event reported when a user interacts with a tile (e.g. entering or leaving a tile). */
+ @RequiresSchemaVersion(major = 1, minor = 400)
+ public static final class TileInteractionEvent {
+ private final EventProto.TileInteractionEvent mImpl;
+
+ TileInteractionEvent(EventProto.TileInteractionEvent impl) {
+ this.mImpl = impl;
+ }
+
+ /**
+ * Gets instance ID of the tile, allocated when the tile instance is added to the carousel.
+ * This ID will remain the same for this tile instance as long it is not removed from the
+ * carousel.
+ */
+ public int getTileId() {
+ return mImpl.getTileId();
+ }
+
+ /** {@link TileInteractionEvent} type. */
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ @IntDef({ENTER, LEAVE, UNKNOWN})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface EventType {}
+
+ /** Unknown type */
+ @EventType public static final int UNKNOWN = 0;
+
+ /** User entered the tile. */
+ @EventType public static final int ENTER = 1;
+
+ /** User left the tile. */
+ @EventType public static final int LEAVE = 2;
+
+ /** Gets the type of the {@link TileInteractionEvent}. */
+ @EventType
+ public int getEventType() {
+ if (mImpl.hasEnter()) {
+ return ENTER;
+ } else if (mImpl.hasLeave()) {
+ return LEAVE;
+ }
+ return UNKNOWN;
+ }
+
+ /** Gets the timestamp of when the interaction was reported. */
+ @NonNull
+ public Instant getTimestamp() {
+ return Instant.ofEpochMilli(mImpl.getTimestampEpochMillis());
+ }
+
+ /** Creates a new wrapper instance from the proto. */
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @NonNull
+ public static TileInteractionEvent fromProto(
+ @NonNull EventProto.TileInteractionEvent proto) {
+ return new TileInteractionEvent(proto);
+ }
+
+ /** Returns the internal proto instance. */
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ @NonNull
+ public EventProto.TileInteractionEvent toProto() {
+ return mImpl;
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ return "TileInteractionEvent{"
+ + "tileId="
+ + getTileId()
+ + ", timestamp="
+ + getTimestamp()
+ + ", eventType="
+ + getEventType()
+ + "}";
+ }
+
+ /** Builder for {@link TileInteractionEvent} */
+ public static final class Builder {
+ private final EventProto.TileInteractionEvent.Builder mImpl =
+ EventProto.TileInteractionEvent.newBuilder();
+
+ /**
+ * Sets instance ID of the tile, allocated when the tile instance is added to the
+ * carousel. This ID will remain the same for this tile instance as long it is not
+ * removed from the carousel.
+ */
+ @RequiresSchemaVersion(major = 1, minor = 400)
+ @NonNull
+ public Builder setTileId(int tileId) {
+ mImpl.setTileId(tileId);
+ return this;
+ }
+
+ private Builder() {}
+
+ /**
+ * A builder for {@link TileInteractionEvent}.
+ *
+ * @throws IllegalArgumentException when the provided {@code eventType} is equal to
+ * {@link EventType#UNKNOWN} or not defined by {@link EventType}.
+ */
+ public Builder(@EventType int eventType) {
+ mImpl.setTimestampEpochMillis(Instant.now().toEpochMilli());
+ switch (eventType) {
+ case ENTER:
+ mImpl.setEnter(EventProto.TileEnter.newBuilder().build());
+ break;
+ case LEAVE:
+ mImpl.setLeave(EventProto.TileLeave.newBuilder().build());
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Event type not supported: " + eventType);
+ }
+ }
+
+ /**
+ * Sets the timestamp of when the interaction was reported. Defaults to {@link
+ * Instant#now()} (Created at the time of {@link Builder#Builder(int)} constructor call)
+ * if not provided.
+ */
+ @RequiresSchemaVersion(major = 1, minor = 400)
+ @NonNull
+ public Builder setTimestamp(@NonNull Instant instant) {
+ mImpl.setTimestampEpochMillis(instant.toEpochMilli());
+ return this;
+ }
+
+ /** Builds an instance from accumulated values. */
+ @NonNull
+ public TileInteractionEvent build() {
+ return TileInteractionEvent.fromProto(mImpl.build());
+ }
+ }
+ }
}
diff --git a/wear/tiles/tiles/src/test/java/androidx/wear/tiles/EventBuildersTest.java b/wear/tiles/tiles/src/test/java/androidx/wear/tiles/EventBuildersTest.java
new file mode 100644
index 0000000..5cbb415
--- /dev/null
+++ b/wear/tiles/tiles/src/test/java/androidx/wear/tiles/EventBuildersTest.java
@@ -0,0 +1,82 @@
+/*
+ * 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.tiles;
+
+import static androidx.wear.tiles.EventBuilders.TileInteractionEvent.ENTER;
+import static androidx.wear.tiles.EventBuilders.TileInteractionEvent.LEAVE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.wear.tiles.proto.EventProto;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.internal.DoNotInstrument;
+
+import java.time.Instant;
+
+@RunWith(AndroidJUnit4.class)
+@DoNotInstrument
+public class EventBuildersTest {
+ @Test
+ public void canBuildBasicTileEnterEvent() {
+ long epochMillis = 12345;
+ int tileId = 1;
+ EventBuilders.TileInteractionEvent interactionEvent =
+ new EventBuilders.TileInteractionEvent.Builder(
+ EventBuilders.TileInteractionEvent.ENTER)
+ .setTimestamp(Instant.ofEpochMilli(epochMillis))
+ .setTileId(tileId)
+ .build();
+
+ EventProto.TileInteractionEvent eventProto =
+ EventProto.TileInteractionEvent.newBuilder()
+ .setEnter(EventProto.TileEnter.newBuilder().build())
+ .setTileId(tileId)
+ .setTimestampEpochMillis(epochMillis)
+ .build();
+
+ assertThat(interactionEvent.toProto()).isEqualTo(eventProto);
+ assertThat(interactionEvent.getEventType()).isEqualTo(ENTER);
+ assertThat(interactionEvent.getTimestamp().toEpochMilli()).isEqualTo(epochMillis);
+ assertThat(interactionEvent.getTileId()).isEqualTo(tileId);
+ }
+
+ @Test
+ public void canBuildBasicTileLeaveEvent() {
+ long epochMillis = 12345;
+ int tileId = 1;
+ EventBuilders.TileInteractionEvent interactionEvent =
+ new EventBuilders.TileInteractionEvent.Builder(LEAVE)
+ .setTimestamp(Instant.ofEpochMilli(epochMillis))
+ .setTileId(tileId)
+ .build();
+
+ EventProto.TileInteractionEvent eventProto =
+ EventProto.TileInteractionEvent.newBuilder()
+ .setLeave(EventProto.TileLeave.newBuilder().build())
+ .setTileId(tileId)
+ .setTimestampEpochMillis(epochMillis)
+ .build();
+
+ assertThat(interactionEvent.toProto()).isEqualTo(eventProto);
+ assertThat(interactionEvent.getEventType()).isEqualTo(LEAVE);
+ assertThat(interactionEvent.getTimestamp().toEpochMilli()).isEqualTo(epochMillis);
+ assertThat(interactionEvent.getTileId()).isEqualTo(tileId);
+ }
+}
diff --git a/window/window-core/api/1.3.0-beta02.txt b/window/window-core/api/1.3.0-beta02.txt
index 5f75b34..aa7ee82 100644
--- a/window/window-core/api/1.3.0-beta02.txt
+++ b/window/window-core/api/1.3.0-beta02.txt
@@ -19,15 +19,10 @@
}
public final class WindowSizeClass {
- ctor public WindowSizeClass(int widthDp, int heightDp);
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 int getHeightDp();
- method public int getWidthDp();
method public androidx.window.core.layout.WindowHeightSizeClass getWindowHeightSizeClass();
method public androidx.window.core.layout.WindowWidthSizeClass getWindowWidthSizeClass();
- property public final int heightDp;
- property public final int widthDp;
property public final androidx.window.core.layout.WindowHeightSizeClass windowHeightSizeClass;
property public final androidx.window.core.layout.WindowWidthSizeClass windowWidthSizeClass;
field public static final androidx.window.core.layout.WindowSizeClass.Companion Companion;
@@ -38,16 +33,6 @@
method @SuppressCompatibility @androidx.window.core.ExperimentalWindowCoreApi public androidx.window.core.layout.WindowSizeClass compute(int widthPx, int heightPx, float density);
}
- public final class WindowSizeClassScoreCalculator {
- method @SuppressCompatibility @androidx.window.core.ExperimentalWindowCoreApi public static int scoreWithinAreaBounds(androidx.window.core.layout.WindowSizeClass, int windowWidthDp, int windowHeightDp);
- method @SuppressCompatibility @androidx.window.core.ExperimentalWindowCoreApi public static int scoreWithinHeightDp(androidx.window.core.layout.WindowSizeClass, int heightDp);
- method @SuppressCompatibility @androidx.window.core.ExperimentalWindowCoreApi public static int scoreWithinWidthDp(androidx.window.core.layout.WindowSizeClass, int widthDp);
- }
-
- public final class WindowSizeClassSelectors {
- method public static androidx.window.core.layout.WindowSizeClass? widestOrEqualWidthDp(java.util.Set<androidx.window.core.layout.WindowSizeClass>, int windowWidthDp, int windowHeightDp);
- }
-
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;
diff --git a/window/window-core/api/current.ignore b/window/window-core/api/current.ignore
new file mode 100644
index 0000000..6f2176b
--- /dev/null
+++ b/window/window-core/api/current.ignore
@@ -0,0 +1,13 @@
+// Baseline format: 1.0
+RemovedClass: androidx.window.core.layout.WindowSizeClassScoreCalculator:
+ Removed class androidx.window.core.layout.WindowSizeClassScoreCalculator
+RemovedClass: androidx.window.core.layout.WindowSizeClassSelectors:
+ Removed class androidx.window.core.layout.WindowSizeClassSelectors
+
+
+RemovedMethod: androidx.window.core.layout.WindowSizeClass#WindowSizeClass(int, int):
+ Removed constructor androidx.window.core.layout.WindowSizeClass(int,int)
+RemovedMethod: androidx.window.core.layout.WindowSizeClass#getHeightDp():
+ Removed method androidx.window.core.layout.WindowSizeClass.getHeightDp()
+RemovedMethod: androidx.window.core.layout.WindowSizeClass#getWidthDp():
+ Removed method androidx.window.core.layout.WindowSizeClass.getWidthDp()
diff --git a/window/window-core/api/current.txt b/window/window-core/api/current.txt
index 5f75b34..aa7ee82 100644
--- a/window/window-core/api/current.txt
+++ b/window/window-core/api/current.txt
@@ -19,15 +19,10 @@
}
public final class WindowSizeClass {
- ctor public WindowSizeClass(int widthDp, int heightDp);
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 int getHeightDp();
- method public int getWidthDp();
method public androidx.window.core.layout.WindowHeightSizeClass getWindowHeightSizeClass();
method public androidx.window.core.layout.WindowWidthSizeClass getWindowWidthSizeClass();
- property public final int heightDp;
- property public final int widthDp;
property public final androidx.window.core.layout.WindowHeightSizeClass windowHeightSizeClass;
property public final androidx.window.core.layout.WindowWidthSizeClass windowWidthSizeClass;
field public static final androidx.window.core.layout.WindowSizeClass.Companion Companion;
@@ -38,16 +33,6 @@
method @SuppressCompatibility @androidx.window.core.ExperimentalWindowCoreApi public androidx.window.core.layout.WindowSizeClass compute(int widthPx, int heightPx, float density);
}
- public final class WindowSizeClassScoreCalculator {
- method @SuppressCompatibility @androidx.window.core.ExperimentalWindowCoreApi public static int scoreWithinAreaBounds(androidx.window.core.layout.WindowSizeClass, int windowWidthDp, int windowHeightDp);
- method @SuppressCompatibility @androidx.window.core.ExperimentalWindowCoreApi public static int scoreWithinHeightDp(androidx.window.core.layout.WindowSizeClass, int heightDp);
- method @SuppressCompatibility @androidx.window.core.ExperimentalWindowCoreApi public static int scoreWithinWidthDp(androidx.window.core.layout.WindowSizeClass, int widthDp);
- }
-
- public final class WindowSizeClassSelectors {
- method public static androidx.window.core.layout.WindowSizeClass? widestOrEqualWidthDp(java.util.Set<androidx.window.core.layout.WindowSizeClass>, int windowWidthDp, int windowHeightDp);
- }
-
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;
diff --git a/window/window-core/api/restricted_1.3.0-beta02.txt b/window/window-core/api/restricted_1.3.0-beta02.txt
index 5f75b34..aa7ee82 100644
--- a/window/window-core/api/restricted_1.3.0-beta02.txt
+++ b/window/window-core/api/restricted_1.3.0-beta02.txt
@@ -19,15 +19,10 @@
}
public final class WindowSizeClass {
- ctor public WindowSizeClass(int widthDp, int heightDp);
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 int getHeightDp();
- method public int getWidthDp();
method public androidx.window.core.layout.WindowHeightSizeClass getWindowHeightSizeClass();
method public androidx.window.core.layout.WindowWidthSizeClass getWindowWidthSizeClass();
- property public final int heightDp;
- property public final int widthDp;
property public final androidx.window.core.layout.WindowHeightSizeClass windowHeightSizeClass;
property public final androidx.window.core.layout.WindowWidthSizeClass windowWidthSizeClass;
field public static final androidx.window.core.layout.WindowSizeClass.Companion Companion;
@@ -38,16 +33,6 @@
method @SuppressCompatibility @androidx.window.core.ExperimentalWindowCoreApi public androidx.window.core.layout.WindowSizeClass compute(int widthPx, int heightPx, float density);
}
- public final class WindowSizeClassScoreCalculator {
- method @SuppressCompatibility @androidx.window.core.ExperimentalWindowCoreApi public static int scoreWithinAreaBounds(androidx.window.core.layout.WindowSizeClass, int windowWidthDp, int windowHeightDp);
- method @SuppressCompatibility @androidx.window.core.ExperimentalWindowCoreApi public static int scoreWithinHeightDp(androidx.window.core.layout.WindowSizeClass, int heightDp);
- method @SuppressCompatibility @androidx.window.core.ExperimentalWindowCoreApi public static int scoreWithinWidthDp(androidx.window.core.layout.WindowSizeClass, int widthDp);
- }
-
- public final class WindowSizeClassSelectors {
- method public static androidx.window.core.layout.WindowSizeClass? widestOrEqualWidthDp(java.util.Set<androidx.window.core.layout.WindowSizeClass>, int windowWidthDp, int windowHeightDp);
- }
-
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;
diff --git a/window/window-core/api/restricted_current.ignore b/window/window-core/api/restricted_current.ignore
new file mode 100644
index 0000000..6f2176b
--- /dev/null
+++ b/window/window-core/api/restricted_current.ignore
@@ -0,0 +1,13 @@
+// Baseline format: 1.0
+RemovedClass: androidx.window.core.layout.WindowSizeClassScoreCalculator:
+ Removed class androidx.window.core.layout.WindowSizeClassScoreCalculator
+RemovedClass: androidx.window.core.layout.WindowSizeClassSelectors:
+ Removed class androidx.window.core.layout.WindowSizeClassSelectors
+
+
+RemovedMethod: androidx.window.core.layout.WindowSizeClass#WindowSizeClass(int, int):
+ Removed constructor androidx.window.core.layout.WindowSizeClass(int,int)
+RemovedMethod: androidx.window.core.layout.WindowSizeClass#getHeightDp():
+ Removed method androidx.window.core.layout.WindowSizeClass.getHeightDp()
+RemovedMethod: androidx.window.core.layout.WindowSizeClass#getWidthDp():
+ Removed method androidx.window.core.layout.WindowSizeClass.getWidthDp()
diff --git a/window/window-core/api/restricted_current.txt b/window/window-core/api/restricted_current.txt
index 5f75b34..aa7ee82 100644
--- a/window/window-core/api/restricted_current.txt
+++ b/window/window-core/api/restricted_current.txt
@@ -19,15 +19,10 @@
}
public final class WindowSizeClass {
- ctor public WindowSizeClass(int widthDp, int heightDp);
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 int getHeightDp();
- method public int getWidthDp();
method public androidx.window.core.layout.WindowHeightSizeClass getWindowHeightSizeClass();
method public androidx.window.core.layout.WindowWidthSizeClass getWindowWidthSizeClass();
- property public final int heightDp;
- property public final int widthDp;
property public final androidx.window.core.layout.WindowHeightSizeClass windowHeightSizeClass;
property public final androidx.window.core.layout.WindowWidthSizeClass windowWidthSizeClass;
field public static final androidx.window.core.layout.WindowSizeClass.Companion Companion;
@@ -38,16 +33,6 @@
method @SuppressCompatibility @androidx.window.core.ExperimentalWindowCoreApi public androidx.window.core.layout.WindowSizeClass compute(int widthPx, int heightPx, float density);
}
- public final class WindowSizeClassScoreCalculator {
- method @SuppressCompatibility @androidx.window.core.ExperimentalWindowCoreApi public static int scoreWithinAreaBounds(androidx.window.core.layout.WindowSizeClass, int windowWidthDp, int windowHeightDp);
- method @SuppressCompatibility @androidx.window.core.ExperimentalWindowCoreApi public static int scoreWithinHeightDp(androidx.window.core.layout.WindowSizeClass, int heightDp);
- method @SuppressCompatibility @androidx.window.core.ExperimentalWindowCoreApi public static int scoreWithinWidthDp(androidx.window.core.layout.WindowSizeClass, int widthDp);
- }
-
- public final class WindowSizeClassSelectors {
- method public static androidx.window.core.layout.WindowSizeClass? widestOrEqualWidthDp(java.util.Set<androidx.window.core.layout.WindowSizeClass>, int windowWidthDp, int windowHeightDp);
- }
-
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;
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 aa43d8a..8d61d8a1 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
@@ -48,36 +48,19 @@
* In these cases developers may wish to specify their own custom break points and match using
* a `when` statement.
*
- * @constructor the primary constructor taking the bounds of the size class.
- * @property widthDp the width in DP for the size class.
- * @property heightDp the height in DP for the size class.
- *
- * @throws IllegalArgumentException if [widthDp] or [heightDp] is negative.
- *
* @see WindowWidthSizeClass
* @see WindowHeightSizeClass
*/
-class WindowSizeClass(
- val widthDp: Int,
- val heightDp: Int
-) {
-
- init {
- require(widthDp >= 0) { "Must have non-negative widthDp: $widthDp." }
- require(heightDp >= 0) { "Must have non-negative heightDp: $heightDp." }
- }
-
+class WindowSizeClass private constructor(
/**
- * Returns the [WindowWidthSizeClass] that corresponds to the [widthDp].
+ * Returns the [WindowWidthSizeClass] that corresponds to the widthDp of the window.
*/
- val windowWidthSizeClass: WindowWidthSizeClass
- get() = WindowWidthSizeClass.compute(widthDp.toFloat())
-
+ val windowWidthSizeClass: WindowWidthSizeClass,
/**
- * Returns the [WindowHeightSizeClass] that corresponds to the [heightDp].
+ * Returns the [WindowHeightSizeClass] that corresponds to the heightDp of the window.
*/
val windowHeightSizeClass: WindowHeightSizeClass
- get() = WindowHeightSizeClass.compute(heightDp.toFloat())
+) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
@@ -85,21 +68,22 @@
other as WindowSizeClass
- if (widthDp != other.widthDp) return false
- if (heightDp != other.heightDp) return false
+ if (windowWidthSizeClass != other.windowWidthSizeClass) return false
+ if (windowHeightSizeClass != other.windowHeightSizeClass) return false
return true
}
override fun hashCode(): Int {
- var result = widthDp.hashCode()
- result = 31 * result + heightDp.hashCode()
+ var result = windowWidthSizeClass.hashCode()
+ result = 31 * result + windowHeightSizeClass.hashCode()
return result
}
override fun toString(): String {
- return "SizeClass { widthDp: $widthDp," +
- " heightDp: $heightDp }"
+ return "WindowSizeClass {" +
+ "windowWidthSizeClass=$windowWidthSizeClass, " +
+ "windowHeightSizeClass=$windowHeightSizeClass }"
}
companion object {
@@ -109,23 +93,15 @@
* @param dpWidth width of a window in DP.
* @param dpHeight height of a window in DP.
* @return [WindowSizeClass] that is recommended for the given dimensions.
- * @see [widestOrEqualWidthDp] for selecting from a custom set of [WindowSizeClass].
* @throws IllegalArgumentException if [dpWidth] or [dpHeight] is
* negative.
*/
@JvmStatic
fun compute(dpWidth: Float, dpHeight: Float): WindowSizeClass {
- val widthDp = when {
- dpWidth < 600 -> 0
- dpWidth < 840 -> 600
- else -> 840
- }
- val heightDp = when {
- dpHeight < 480 -> 0
- dpHeight < 900 -> 480
- else -> 900
- }
- return WindowSizeClass(widthDp, heightDp)
+ return WindowSizeClass(
+ WindowWidthSizeClass.compute(dpWidth),
+ WindowHeightSizeClass.compute(dpHeight)
+ )
}
/**
diff --git a/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowSizeClassScoreCalculator.kt b/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowSizeClassScoreCalculator.kt
deleted file mode 100644
index e105f90..0000000
--- a/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowSizeClassScoreCalculator.kt
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-@file:JvmName("WindowSizeClassScoreCalculator")
-package androidx.window.core.layout
-
-import androidx.window.core.ExperimentalWindowCoreApi
-
-/**
- * A scoring function to calculate how close the width of a [WindowSizeClass] is to [widthDp]
- * without exceeding it.
- *
- * @param widthDp the width bound to try to match.
- * @return an integer from -1 to [Integer.MAX_VALUE] where a larger value indicates a better match.
- */
-@ExperimentalWindowCoreApi
-fun WindowSizeClass.scoreWithinWidthDp(widthDp: Int): Int {
- return if (this.widthDp <= widthDp) {
- Integer.MAX_VALUE / (1 + widthDp - this.widthDp)
- } else {
- -1
- }
-}
-
-/**
- * A scoring function to calculate how close the height of a [WindowSizeClass] is to [heightDp]
- * without exceeding it.
- *
- * @param heightDp the height bound to try to match.
- * @return an integer from -1 to [Integer.MAX_VALUE] where a larger value indicates a better match.
- */
-@ExperimentalWindowCoreApi
-fun WindowSizeClass.scoreWithinHeightDp(heightDp: Int): Int {
- return if (this.heightDp <= heightDp) {
- Integer.MAX_VALUE / (1 + heightDp - this.heightDp)
- } else {
- -1
- }
-}
-
-/**
- * A scoring function to calculate how close the area of a [WindowSizeClass] is to the area of a
- * window without exceeding it.
- *
- * @param windowWidthDp the width of a window constraint.
- * @param windowHeightDp the height of a window constraint.
- *
- * @return an integer from -1 to [Integer.MAX_VALUE] where a larger value indicates a better match.
- */
-@ExperimentalWindowCoreApi
-fun WindowSizeClass.scoreWithinAreaBounds(
- windowWidthDp: Int,
- windowHeightDp: Int
-): Int {
- if (windowWidthDp < this.widthDp || windowHeightDp < this.heightDp) {
- return -1
- }
- val areaDifference = windowWidthDp * windowHeightDp - this.widthDp * this.heightDp
- return Integer.MAX_VALUE / (1 + areaDifference)
-}
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
deleted file mode 100644
index 529b376..0000000
--- a/window/window-core/src/commonMain/kotlin/androidx/window/core/layout/WindowSizeClassSelectors.kt
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-@file:JvmName("WindowSizeClassSelectors")
-package androidx.window.core.layout
-
-/**
- * Calculates which [WindowSizeClass] has the closest matching [windowWidthDp] within the given
- * value. If there are multiple matches then the tallest [WindowSizeClass] is selected within the
- * given value.
- *
- * @param windowWidthDp the width of the current window in DP to choose a [WindowSizeClass].
- * @param windowHeightDp the height of the current window in DP to chose a [WindowSizeClass].
- * @return a [WindowSizeClass] that has [WindowSizeClass.widthDp] less than or equal to the
- * [windowWidthDp] and is the closest to [windowWidthDp] if possible `null` otherwise.
- */
-fun Set<WindowSizeClass>.widestOrEqualWidthDp(
- windowWidthDp: Int,
- windowHeightDp: Int
-): WindowSizeClass? {
- require(0 <= windowHeightDp) {
- "Window height must be non-negative but got windowHeightDp: $windowHeightDp"
- }
- require(0 <= windowWidthDp) {
- "Window width must be non-negative but got windowHeightDp: $windowWidthDp"
- }
- var maxValue: WindowSizeClass? = null
- forEach { sizeClass ->
- if (sizeClass.widthDp > windowWidthDp) {
- return@forEach
- }
- if (sizeClass.heightDp > windowHeightDp) {
- return@forEach
- }
-
- val localMax = maxValue
- if (localMax == null) {
- maxValue = sizeClass
- return@forEach
- }
- if (localMax.widthDp > sizeClass.widthDp) {
- return@forEach
- }
- if (localMax.widthDp == sizeClass.widthDp && sizeClass.heightDp < localMax.heightDp) {
- return@forEach
- }
- maxValue = sizeClass
- }
- return maxValue
-}
diff --git a/window/window-core/src/commonTest/kotlin/androidx/window/core/layout/WindowSizeClassScoreCalculatorTest.kt b/window/window-core/src/commonTest/kotlin/androidx/window/core/layout/WindowSizeClassScoreCalculatorTest.kt
deleted file mode 100644
index 1587ff6..0000000
--- a/window/window-core/src/commonTest/kotlin/androidx/window/core/layout/WindowSizeClassScoreCalculatorTest.kt
+++ /dev/null
@@ -1,117 +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.window.core.layout
-
-import androidx.window.core.ExperimentalWindowCoreApi
-import kotlin.test.Test
-import kotlin.test.assertEquals
-import kotlin.test.assertTrue
-
-@OptIn(ExperimentalWindowCoreApi::class)
-class WindowSizeClassScoreCalculatorTest {
-
- private val widthDp = 100
- private val heightDp = 200
-
- @Test
- fun scoreWindowSizeClassWithinWidthDp_exact_match_has_max_value() {
- val sizeClass = WindowSizeClass(widthDp, heightDp)
-
- val actual = sizeClass.scoreWithinWidthDp(widthDp)
-
- assertEquals(Integer.MAX_VALUE, actual)
- }
-
- @Test
- fun scoreWindowSizeClassWithinWidthDp_too_wide_has_negative_value() {
- val sizeClass = WindowSizeClass(widthDp, heightDp)
-
- val actual = sizeClass.scoreWithinWidthDp(widthDp / 2)
-
- assertEquals(-1, actual)
- }
-
- @Test
- fun scoreWindowSizeClassWithinWidthDp_closer_match_has_larger_value() {
- val narrowSizeClass = WindowSizeClass(widthDp - 10, heightDp)
- val wideSizeClass = WindowSizeClass(widthDp - 5, heightDp)
-
- val narrowScore = narrowSizeClass.scoreWithinWidthDp(widthDp)
- val widerScore = wideSizeClass.scoreWithinWidthDp(widthDp)
-
- assertTrue(narrowScore < widerScore)
- }
-
- @Test
- fun scoreWindowSizeClassWithinHeightDp_exact_match_has_max_value() {
- val sizeClass = WindowSizeClass(widthDp, heightDp)
-
- val actual = sizeClass.scoreWithinHeightDp(heightDp)
-
- assertEquals(Integer.MAX_VALUE, actual)
- }
-
- @Test
- fun scoreWindowSizeClassWithinHeightDp_too_tall_has_negative_value() {
- val sizeClass = WindowSizeClass(widthDp, heightDp)
-
- val actual = sizeClass.scoreWithinHeightDp(heightDp / 2)
-
- assertEquals(-1, actual)
- }
-
- @Test
- fun scoreWindowSizeClassWithinHeightDp_closer_match_has_larger_value() {
- val shortSizeClass = WindowSizeClass(widthDp, heightDp - 10)
- val tallSizeClass = WindowSizeClass(widthDp, heightDp - 5)
-
- val narrowScore = shortSizeClass.scoreWithinHeightDp(heightDp)
- val widerScore = tallSizeClass.scoreWithinHeightDp(heightDp)
-
- assertTrue(narrowScore < widerScore)
- }
-
- @Test
- fun scoreWindowSizeClassAreaWithinBounds_exact_match_has_max_value() {
- val sizeClass = WindowSizeClass(widthDp, heightDp)
-
- val actual = sizeClass.scoreWithinAreaBounds(widthDp, heightDp)
-
- assertEquals(Integer.MAX_VALUE, actual)
- }
-
- @Test
- fun scoreWindowSizeClassAreaWithinBounds_too_large_has_negative_value() {
- val sizeClass = WindowSizeClass(widthDp, heightDp)
-
- val actual = sizeClass.scoreWithinAreaBounds(widthDp - 1, heightDp)
-
- assertEquals(-1, actual)
- }
-
- @Test
- fun scoreWindowSizeClassAreaWithinBounds_closer_match_has_larger_value() {
- val smallSizeClass = WindowSizeClass(widthDp - 10, heightDp - 10)
- val largeSizeClass = WindowSizeClass(widthDp - 5, heightDp - 5)
-
- val smallScore = smallSizeClass.scoreWithinAreaBounds(widthDp, heightDp)
- val largeScore = largeSizeClass.scoreWithinAreaBounds(widthDp, heightDp)
-
- assertTrue(smallScore < largeScore, "Expected smallScore < large score," +
- " but was false. smallScore: $smallScore, largeScore: $largeScore")
- }
-}
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
deleted file mode 100644
index 2640117..0000000
--- a/window/window-core/src/commonTest/kotlin/androidx/window/core/layout/WindowSizeClassSelectorsTest.kt
+++ /dev/null
@@ -1,108 +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.window.core.layout
-
-import kotlin.test.Test
-import kotlin.test.assertEquals
-import kotlin.test.assertFailsWith
-import kotlin.test.assertNull
-
-class WindowSizeClassSelectorsTest {
-
- private val widthDp = 100
- private val heightDp = 200
-
- @Test
- fun widestOrEqualWidthDp_return_null_if_no_width_match() {
- val sizeClass = WindowSizeClass(widthDp, heightDp)
-
- val actual = setOf(sizeClass).widestOrEqualWidthDp(0, heightDp)
-
- assertNull(actual)
- }
-
- @Test
- fun widestOrEqualWidthDp_return_widest_match() {
- val smallSizeClass = WindowSizeClass(widthDp / 2, heightDp)
- val mediumSizeClass = WindowSizeClass(widthDp, heightDp)
- val largeSizeClass = WindowSizeClass(widthDp * 2, heightDp)
-
- val actual = setOf(smallSizeClass, mediumSizeClass, largeSizeClass)
- .widestOrEqualWidthDp(widthDp + 1, heightDp)
-
- assertEquals(mediumSizeClass, actual)
- }
-
- @Test
- fun widestOrEqualWidthDp_return_exact_match() {
- val smallSizeClass = WindowSizeClass(widthDp / 2, heightDp)
- val mediumSizeClass = WindowSizeClass(widthDp, heightDp)
- val largeSizeClass = WindowSizeClass(widthDp * 2, heightDp)
-
- val actual = setOf(smallSizeClass, mediumSizeClass, largeSizeClass)
- .widestOrEqualWidthDp(widthDp, heightDp)
-
- assertEquals(mediumSizeClass, actual)
- }
-
- @Test
- fun widestOrEqualWidthDp_multiple_matches_return_width() {
- val smallSizeClass = WindowSizeClass(widthDp, heightDp / 2)
- val mediumSizeClass = WindowSizeClass(widthDp, heightDp)
- val largeSizeClass = WindowSizeClass(widthDp, heightDp * 2)
-
- val actual = setOf(smallSizeClass, mediumSizeClass, largeSizeClass)
- .widestOrEqualWidthDp(widthDp, heightDp * 3)
-
- assertEquals(largeSizeClass, actual)
- }
-
- @Test
- fun widestOrEqualWidth_throws_on_negative_height() {
- assertFailsWith(IllegalArgumentException::class) {
- val sizeClass = WindowSizeClass(widthDp, heightDp)
-
- setOf(sizeClass).widestOrEqualWidthDp(0, -1)
- }
- }
-
- @Test
- fun widestOrEqualWidth_throws_on_negative_width() {
- assertFailsWith(IllegalArgumentException::class) {
- val sizeClass = WindowSizeClass(widthDp, heightDp)
-
- setOf(sizeClass).widestOrEqualWidthDp(-1, 0)
- }
- }
-
- @Test
- fun widestOrEqualWidthDp_return_null_if_no_height_match() {
- val sizeClass = WindowSizeClass(widthDp, heightDp)
-
- val actual = setOf(sizeClass).widestOrEqualWidthDp(widthDp, heightDp - 1)
-
- assertNull(actual)
- }
-
- @Test
- fun widestOrEqualWidthDp_return_value_if_has_exact_height_match() {
- val sizeClass = WindowSizeClass(widthDp, heightDp)
-
- val actual = setOf(sizeClass).widestOrEqualWidthDp(widthDp, heightDp)
-
- assertEquals(sizeClass, 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 d026bfd..bc89fda 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
@@ -34,8 +34,8 @@
WindowWidthSizeClass.EXPANDED
)
- val actual = listOf(100, 700, 900).map { width ->
- WindowSizeClass(widthDp = width, heightDp = 100)
+ val actual = listOf(100f, 700f, 900f).map { width ->
+ WindowSizeClass.compute(width, 100f)
}.map { sizeClass ->
sizeClass.windowWidthSizeClass
}
@@ -45,7 +45,7 @@
@Test
fun testWindowSizeClass_computeRounds() {
- val expected = WindowSizeClass(0, 0)
+ val expected = WindowSizeClass.compute(0f, 0f)
val actual = WindowSizeClass.compute(300f, 300f)
@@ -70,8 +70,8 @@
WindowHeightSizeClass.EXPANDED
)
- val actual = listOf(100, 500, 900).map { height ->
- WindowSizeClass(widthDp = 100, heightDp = height)
+ val actual = listOf(100f, 500f, 900f).map { height ->
+ WindowSizeClass.compute(100f, height)
}.map { sizeClass ->
sizeClass.windowHeightSizeClass
}
@@ -81,15 +81,14 @@
@Test
fun testEqualsImpliesHashCode() {
- val first = WindowSizeClass(widthDp = 100, heightDp = 500)
- val second = WindowSizeClass(widthDp = 100, heightDp = 500)
+ val first = WindowSizeClass.compute(100f, 500f)
+ val second = WindowSizeClass.compute(100f, 500f)
assertEquals(first, second)
assertEquals(first.hashCode(), second.hashCode())
}
@Test
- @Suppress("DEPRECATION")
fun truncated_float_does_not_throw() {
val sizeClass = WindowSizeClass.compute(0.5f, 0.5f)
@@ -102,7 +101,7 @@
@Test
fun zero_size_class_does_not_throw() {
- val sizeClass = WindowSizeClass(0, 0)
+ val sizeClass = WindowSizeClass.compute(0f, 0f)
val widthSizeClass = sizeClass.windowWidthSizeClass
val heightSizeClass = sizeClass.windowHeightSizeClass
@@ -114,14 +113,14 @@
@Test
fun negative_width_throws() {
assertFailsWith(IllegalArgumentException::class) {
- WindowSizeClass(-1, 0)
+ WindowSizeClass.compute(-1f, 0f)
}
}
@Test
fun negative_height_throws() {
assertFailsWith(IllegalArgumentException::class) {
- WindowSizeClass(0, -1)
+ WindowSizeClass.compute(0f, -1f)
}
}
}
diff --git a/window/window/build.gradle b/window/window/build.gradle
index 4df8a41..0f9e2d0 100644
--- a/window/window/build.gradle
+++ b/window/window/build.gradle
@@ -101,4 +101,5 @@
// Suppressing deprecation warnings, since there is a need to maintain compatibility with old
// Sidecar interface.
failOnDeprecationWarnings = false
+ samples(project(":window:window-samples"))
}