Merge "Mark CameraGraphSimulatorTest$simulatorCanIssueMultipleFrames flaky" into androidx-main
diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml
new file mode 100644
index 0000000..4eebecbb
--- /dev/null
+++ b/.github/workflows/integration_tests.yml
@@ -0,0 +1,32 @@
+name: Run Integration Tests
+on:
+ workflow_run:
+ workflows: ["AndroidX Presubmits"]
+ types: [completed]
+
+jobs:
+ run_integration_tests:
+ runs-on: ubuntu-latest
+ if: ${{ github.event.workflow_run.conclusion == 'success' }}
+ name: Run integration tests on FTL
+ steps:
+ - name: Set up JDK 11
+ uses: actions/setup-java@v2
+ with:
+ java-version: '11'
+ distribution: 'adopt'
+ - name: "set output directory"
+ run: echo "::set-output name=output-dir::$(readlink -f .)/outputs"
+ id: dirs
+ - id: run_tests
+ uses: androidX/androidx-ci-action@latest
+ with:
+ run-id: ${{ github.event.workflow_run.id }}
+ gcp-token: ${{ secrets.GCP_SA_KEY }}
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ output-folder: ${{ steps.dirs.outputs.output-dir }}
+ - uses: actions/upload-artifact@v2
+ if: always()
+ with:
+ name: outputs
+ path: ${{ steps.dirs.outputs.output-dir }}
diff --git a/OWNERS b/OWNERS
index 7c95cab..478bf24 100644
--- a/OWNERS
+++ b/OWNERS
@@ -11,12 +11,15 @@
lpf@google.com
mount@google.com
nickanthony@google.com
+owengray@google.com
pavlis@google.com
romainguy@android.com
+saff@google.com
sergeyv@google.com
siyamed@google.com
sjgilbert@google.com
sumir@google.com
+tiem@google.com
yboyar@google.com
per-file *settings.gradle = set noparent
diff --git a/activity/activity-lint/build.gradle b/activity/activity-lint/build.gradle
index 91f9d3c..7e37c76 100644
--- a/activity/activity-lint/build.gradle
+++ b/activity/activity-lint/build.gradle
@@ -25,13 +25,7 @@
}
dependencies {
- // compileOnly because we use lintChecks and it doesn't allow other types of deps
- // this ugly hack exists because of b/63873667
- if (rootProject.hasProperty("android.injected.invoked.from.ide")) {
- compileOnly(LINT_API_LATEST)
- } else {
- compileOnly(LINT_API_MIN)
- }
+ compileOnly(LINT_API_MIN)
compileOnly(KOTLIN_STDLIB)
testImplementation(KOTLIN_STDLIB)
diff --git a/annotation/annotation-experimental-lint/build.gradle b/annotation/annotation-experimental-lint/build.gradle
index 15ad4d6..17d9220 100644
--- a/annotation/annotation-experimental-lint/build.gradle
+++ b/annotation/annotation-experimental-lint/build.gradle
@@ -34,13 +34,7 @@
}
dependencies {
- // compileOnly because we use lintChecks and it doesn't allow other types of deps
- // this ugly hack exists because of b/63873667
- if (rootProject.hasProperty("android.injected.invoked.from.ide")) {
- compileOnly(LINT_API_LATEST)
- } else {
- compileOnly(LINT_API_MIN)
- }
+ compileOnly(LINT_API_MIN)
compileOnly(KOTLIN_STDLIB)
testImplementation(KOTLIN_STDLIB)
diff --git a/buildSrc/src/main/kotlin/androidx/build/AndroidXUiPlugin.kt b/buildSrc/src/main/kotlin/androidx/build/AndroidXUiPlugin.kt
index bb847e0..2246471 100644
--- a/buildSrc/src/main/kotlin/androidx/build/AndroidXUiPlugin.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/AndroidXUiPlugin.kt
@@ -25,6 +25,7 @@
import org.gradle.api.Project
import org.gradle.api.artifacts.type.ArtifactTypeDefinition
import org.gradle.api.attributes.Attribute
+import org.gradle.api.file.DuplicatesStrategy
import org.gradle.api.tasks.ClasspathNormalizer
import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.findByType
@@ -282,6 +283,14 @@
"multiplatformExtension is null (multiplatform plugin not enabled?)"
}
+ /**
+ * Temporary workaround for https://youtrack.jetbrains.com/issue/KT-46096
+ * Should be removed once the build switches to Kotlin 1.5
+ */
+ tasks.withType(org.gradle.jvm.tasks.Jar::class.java).configureEach { jar ->
+ jar.duplicatesStrategy = DuplicatesStrategy.INCLUDE
+ }
+
/*
The following configures source sets - note:
@@ -313,6 +322,23 @@
tasks.named("desktopTestClasses").also(::addToBuildOnServer)
}
}
+
+ // workaround after migration to AGP 7.0.0-alpha15
+ // https://youtrack.jetbrains.com/issue/KT-43944#focus=Comments-27-4612683.0-0
+ // TODO(demin): remove after migration to Kotlin 1.5.0:
+ // https://android-review.googlesource.com/c/platform/frameworks/support/+/1651538
+ fun createNonExistentConfiguration(name: String) {
+ if (project.configurations.findByName(name) == null) {
+ project.configurations.create(name)
+ }
+ }
+
+ createNonExistentConfiguration("androidTestApi")
+ createNonExistentConfiguration("androidTestDebugApi")
+ createNonExistentConfiguration("androidTestReleaseApi")
+ createNonExistentConfiguration("testApi")
+ createNonExistentConfiguration("testDebugApi")
+ createNonExistentConfiguration("testReleaseApi")
}
}
}
diff --git a/buildSrc/src/main/kotlin/androidx/build/ComposeJvmTarget.kt b/buildSrc/src/main/kotlin/androidx/build/ComposeJvmTarget.kt
new file mode 100644
index 0000000..d3f42f8
--- /dev/null
+++ b/buildSrc/src/main/kotlin/androidx/build/ComposeJvmTarget.kt
@@ -0,0 +1,229 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.build
+
+import org.gradle.api.Project
+import org.gradle.api.file.ConfigurableFileCollection
+import org.gradle.api.plugins.JavaPlugin
+import org.gradle.api.plugins.JavaPluginConvention
+import org.gradle.api.tasks.SourceSet
+import org.gradle.api.tasks.compile.AbstractCompile
+import org.gradle.api.tasks.testing.Test
+import org.gradle.jvm.tasks.Jar
+import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
+import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmCompilation
+import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
+import org.jetbrains.kotlin.gradle.utils.addExtendsFromRelation
+import java.lang.reflect.Modifier
+import java.util.concurrent.Callable
+
+object ComposeJvmTarget {
+ /**
+ * Temporary workaround for fixing Compose Desktop build with Kotlin 1.4 and Gradle 7
+ * Intended to be used only in :compose:desktop:desktop and only until Compose project
+ * switches to Kotlin 1.5.0
+ *
+ * This is basically a copy
+ * org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget.withJava from Kotlin 1.5.0,
+ * which fixes java.lang.NoSuchMethodError, thrown by Gradle 7 with Kotlin 1.4
+ * (https://youtrack.jetbrains.com/issue/KTIJ-10018).
+ *
+ * There are a few changes from the original:
+ * some internal methods are copied or called using Java Reflection
+ */
+ @JvmStatic
+ fun withJava(target: KotlinJvmTarget) {
+ val project = target.project
+
+ project.plugins.apply(JavaPlugin::class.java)
+ val javaPluginConvention = project.convention.getPlugin(JavaPluginConvention::class.java)
+ setUpJavaSourceSets(target)
+
+ javaPluginConvention.sourceSets.all { javaSourceSet ->
+ val compilation = target.compilations.getByName(javaSourceSet.name)
+ val compileJavaTask = project.tasks.withType(AbstractCompile::class.java)
+ .named(javaSourceSet.compileJavaTaskName)
+
+ setupJavaSourceSetSourcesAndResources(project, javaSourceSet, compilation)
+
+ val javaClasses = project.files(compileJavaTask.map { it.destinationDir })
+
+ compilation.output.classesDirs.from(javaClasses)
+
+ (javaSourceSet.output.classesDirs as? ConfigurableFileCollection)?.from(
+ compilation.output.classesDirs.minus(javaClasses)
+ )
+
+ javaSourceSet.output.setResourcesDir(
+ Callable { compilation.output.resourcesDirProvider }
+ )
+
+ setupDependenciesCrossInclusionForJava(project, compilation, javaSourceSet)
+ }
+
+ // Eliminate the Java output configurations from dependency resolution
+ // to avoid ambiguity between them and the equivalent configurations
+ // created for the target:
+ listOf(
+ JavaPlugin.API_ELEMENTS_CONFIGURATION_NAME,
+ JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME
+ ).forEach { outputConfigurationName ->
+ project.configurations.findByName(outputConfigurationName)?.isCanBeConsumed = false
+ }
+
+ disableJavaPluginTasks(project, javaPluginConvention, target)
+ }
+
+ private fun setupDependenciesCrossInclusionForJava(
+ project: Project,
+ compilation: KotlinJvmCompilation,
+ javaSourceSet: SourceSet
+ ) {
+ // Make sure Kotlin compilation dependencies appear in the Java source set classpaths:
+
+ listOfNotNull(
+ compilation.apiConfigurationName,
+ compilation.implementationConfigurationName,
+ compilation.compileOnlyConfigurationName
+ ).forEach { configurationName ->
+ project.addExtendsFromRelation(
+ javaSourceSet.compileClasspathConfigurationName,
+ configurationName
+ )
+ }
+
+ listOfNotNull(
+ compilation.apiConfigurationName,
+ compilation.implementationConfigurationName,
+ compilation.runtimeOnlyConfigurationName
+ ).forEach { configurationName ->
+ project.addExtendsFromRelation(
+ javaSourceSet.runtimeClasspathConfigurationName,
+ configurationName
+ )
+ }
+
+ // Add the Java source set dependencies to the Kotlin compilation
+ // compile & runtime configurations:
+ listOfNotNull(
+ javaSourceSet.compileOnlyConfigurationName,
+ javaSourceSet.apiConfigurationName
+ .takeIf { project.configurations.findByName(it) != null },
+ javaSourceSet.implementationConfigurationName
+ ).forEach { configurationName ->
+ project.addExtendsFromRelation(
+ compilation.compileDependencyConfigurationName,
+ configurationName
+ )
+ }
+
+ listOfNotNull(
+ javaSourceSet.runtimeOnlyConfigurationName,
+ javaSourceSet.apiConfigurationName
+ .takeIf { project.configurations.findByName(it) != null },
+ javaSourceSet.implementationConfigurationName
+ ).forEach { configurationName ->
+ project.addExtendsFromRelation(
+ compilation.runtimeDependencyConfigurationName,
+ configurationName
+ )
+ }
+ }
+
+ /**
+ * Calls AbstractKotlinPlugin.setUpJavaSourceSets(target, false) using reflection
+ */
+ private fun setUpJavaSourceSets(target: KotlinJvmTarget) {
+ val abstractKotlinPluginClass = Class.forName(ABSTRACT_KOTLIN_PLUGIN)
+ ?: error("Could not find '$ABSTRACT_KOTLIN_PLUGIN' class")
+ val companionField = abstractKotlinPluginClass.fields
+ .find { it.name == COMPANION && Modifier.isStatic(it.modifiers) }
+ ?: error("Could not find '$COMPANION' field")
+ val companionInstance = companionField.get(abstractKotlinPluginClass)!!
+ val companionClass = companionInstance.javaClass
+ val setUpJavaSourceSetsMethod = companionClass.methods.find {
+ it.name == SET_UP_JAVA_SOURCE_SETS && it.parameterCount == 2
+ } ?: error("Could not find '$SET_UP_JAVA_SOURCE_SETS' method")
+ setUpJavaSourceSetsMethod.invoke(companionInstance, target, false)
+ }
+
+ private fun disableJavaPluginTasks(
+ project: Project,
+ javaPluginConvention: JavaPluginConvention,
+ target: KotlinJvmTarget
+ ) {
+ // A 'normal' build should not do redundant job like running the tests twice or building two JARs,
+ // so disable some tasks and just make them depend on the others:
+ val targetJar = project.tasks.withType(Jar::class.java).named(target.artifactsTaskName)
+
+ val mainJarTaskName = javaPluginConvention.sourceSets.getByName("main").jarTaskName
+ project.tasks.withType(Jar::class.java).named(mainJarTaskName) { javaJar ->
+ (javaJar.source as? ConfigurableFileCollection)?.setFrom(targetJar.map { it.source })
+ javaJar.conventionMapping("archiveName") { targetJar.get().archiveFileName.get() }
+ javaJar.dependsOn(targetJar)
+ javaJar.enabled = false
+ }
+
+ project.tasks.withType(Test::class.java).named(JavaPlugin.TEST_TASK_NAME) { javaTestTask ->
+ javaTestTask.dependsOn(project.tasks.named(target.testTaskName))
+ javaTestTask.enabled = false
+ }
+ }
+
+ private fun setupJavaSourceSetSourcesAndResources(
+ project: Project,
+ javaSourceSet: SourceSet,
+ compilation: KotlinJvmCompilation
+ ) {
+ javaSourceSet.java.setSrcDirs(listOf("src/${compilation.defaultSourceSet.name}/java"))
+ compilation.defaultSourceSet.kotlin.srcDirs(javaSourceSet.java.sourceDirectories)
+
+ // To avoid confusion in the sources layout, remove the default Java source directories
+ // (like src/main/java, src/test/java) and instead add sibling directories to those where the Kotlin
+ // sources are placed (i.e. src/jvmMain/java, src/jvmTest/java):
+ javaSourceSet.resources.setSrcDirs(
+ compilation.defaultSourceSet.resources.sourceDirectories
+ )
+ compilation.defaultSourceSet.resources.srcDirs(javaSourceSet.resources.sourceDirectories)
+
+ // Resources processing is done with the Kotlin resource processing task:
+ val processJavaResourcesTask =
+ project.tasks.getByName(javaSourceSet.processResourcesTaskName)
+ processJavaResourcesTask.dependsOn(
+ project.tasks.getByName(compilation.processResourcesTaskName)
+ )
+ processJavaResourcesTask.enabled = false
+ }
+
+ private const val ABSTRACT_KOTLIN_PLUGIN =
+ "org.jetbrains.kotlin.gradle.plugin.AbstractKotlinPlugin"
+ private const val COMPANION = "Companion"
+ private const val SET_UP_JAVA_SOURCE_SETS = "setUpJavaSourceSets${'$'}kotlin_gradle_plugin"
+ private const val ARTIFACT_TASK_NAME = "jar"
+
+ private val KotlinTarget.testTaskName: String
+ get() = lowerCamelCaseName(name, "test")
+
+ private fun lowerCamelCaseName(vararg nameParts: String?): String {
+ val nonEmptyParts = nameParts.mapNotNull { it?.takeIf(String::isNotEmpty) }
+ return nonEmptyParts.drop(1).joinToString(
+ separator = "",
+ prefix = nonEmptyParts.firstOrNull().orEmpty(),
+ transform = String::capitalize
+ )
+ }
+}
\ No newline at end of file
diff --git a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
index 0e420a88..194f12e 100644
--- a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
@@ -151,5 +151,5 @@
val WINDOW = Version("1.0.0-alpha07")
val WINDOW_EXTENSIONS = Version("1.0.0-alpha01")
val WINDOW_SIDECAR = Version("0.1.0-alpha01")
- val WORK = Version("2.6.0-alpha02")
+ val WORK = Version("2.6.0-beta01")
}
diff --git a/buildSrc/src/main/kotlin/androidx/build/LintConfiguration.kt b/buildSrc/src/main/kotlin/androidx/build/LintConfiguration.kt
index fa4179a..22a7375 100644
--- a/buildSrc/src/main/kotlin/androidx/build/LintConfiguration.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/LintConfiguration.kt
@@ -133,9 +133,6 @@
}
isIgnoreWarnings = true
- // Workaround for b/177359055 where 27.2.0-beta04 incorrectly computes severity.
- isCheckAllWarnings = true
-
// Write output directly to the console (and nowhere else).
textReport = true
htmlReport = false
@@ -166,9 +163,6 @@
// Disable the TODO check until we have a policy that requires it.
disable("StopShip")
- // Disable a check that conflicts with our workaround for b/177359055
- disable("LintBaseline")
-
// Broken in 7.0.0-alpha15 due to b/180408990
disable("RestrictedApi")
disable("VisibleForTests")
diff --git a/buildSrc/src/main/kotlin/androidx/build/checkapi/ApiTasks.kt b/buildSrc/src/main/kotlin/androidx/build/checkapi/ApiTasks.kt
index 1c9ed42..4f521ef 100644
--- a/buildSrc/src/main/kotlin/androidx/build/checkapi/ApiTasks.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/checkapi/ApiTasks.kt
@@ -20,6 +20,7 @@
import androidx.build.Release
import androidx.build.RunApiTasks
import androidx.build.Version
+import androidx.build.doclava.androidJarFile
import androidx.build.isWriteVersionedApiFilesEnabled
import androidx.build.java.JavaCompileInputs
import androidx.build.metalava.MetalavaTasks
@@ -161,7 +162,6 @@
} ?: return@afterEvaluate
javaInputs = JavaCompileInputs.fromLibraryVariant(
- config.library,
variant,
project
)
@@ -220,9 +220,9 @@
}
)
- return JavaCompileInputs.fromSourcesAndDeps(
+ return JavaCompileInputs(
sourcePaths = mainSourcePaths + kotlinSourcePaths,
dependencyClasspath = dependencyClasspath,
- project = this
+ androidJarFile(project)
)
}
diff --git a/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt b/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
index 279ab0b..2ef858a 100644
--- a/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
@@ -100,7 +100,7 @@
const val REACTIVE_STREAMS = "org.reactivestreams:reactive-streams:1.0.0"
const val RX_JAVA = "io.reactivex.rxjava2:rxjava:2.2.9"
const val RX_JAVA3 = "io.reactivex.rxjava3:rxjava:3.0.0"
-val SKIKO_VERSION = System.getenv("SKIKO_VERSION") ?: "0.2.30"
+val SKIKO_VERSION = System.getenv("SKIKO_VERSION") ?: "0.2.33"
val SKIKO = "org.jetbrains.skiko:skiko-jvm:$SKIKO_VERSION"
val SKIKO_LINUX_X64 = "org.jetbrains.skiko:skiko-jvm-runtime-linux-x64:$SKIKO_VERSION"
val SKIKO_MACOS_X64 = "org.jetbrains.skiko:skiko-jvm-runtime-macos-x64:$SKIKO_VERSION"
diff --git a/buildSrc/src/main/kotlin/androidx/build/java/JavaCompileInputs.kt b/buildSrc/src/main/kotlin/androidx/build/java/JavaCompileInputs.kt
index eab0fbc..98740fa 100644
--- a/buildSrc/src/main/kotlin/androidx/build/java/JavaCompileInputs.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/java/JavaCompileInputs.kt
@@ -18,13 +18,11 @@
import androidx.build.doclava.androidJarFile
import androidx.build.multiplatformExtension
-import com.android.build.gradle.LibraryExtension
import com.android.build.gradle.api.BaseVariant
import com.android.build.gradle.api.SourceKind
import org.gradle.api.Project
import org.gradle.api.file.FileCollection
import org.gradle.api.tasks.SourceSet
-import java.io.File
// JavaCompileInputs contains the information required to compile Java/Kotlin code
// This can be helpful for creating Metalava and Dokka tasks with the same settings
@@ -36,12 +34,11 @@
val dependencyClasspath: FileCollection,
// Android's boot classpath.
- val bootClasspath: Collection<File>
+ val bootClasspath: FileCollection
) {
companion object {
// Constructs a JavaCompileInputs from a library and its variant
fun fromLibraryVariant(
- library: LibraryExtension,
variant: BaseVariant,
project: Project
): JavaCompileInputs {
@@ -54,7 +51,7 @@
return JavaCompileInputs(
sourceCollection,
dependencyClasspath,
- library.bootClasspath
+ androidJarFile(project)
)
}
@@ -66,16 +63,7 @@
})
)
val dependencyClasspath = sourceSet.compileClasspath
- return fromSourcesAndDeps(sourcePaths, dependencyClasspath, project)
- }
-
- fun fromSourcesAndDeps(
- sourcePaths: FileCollection,
- dependencyClasspath: FileCollection,
- project: Project
- ): JavaCompileInputs {
- val bootClasspath: Collection<File> = androidJarFile(project).files
- return JavaCompileInputs(sourcePaths, dependencyClasspath, bootClasspath)
+ return JavaCompileInputs(sourcePaths, dependencyClasspath, androidJarFile(project))
}
private fun getSourceCollection(variant: BaseVariant, project: Project): FileCollection {
diff --git a/buildSrc/src/main/kotlin/androidx/build/metalava/CheckApiCompatibilityTask.kt b/buildSrc/src/main/kotlin/androidx/build/metalava/CheckApiCompatibilityTask.kt
index 1224dea..c376ead 100644
--- a/buildSrc/src/main/kotlin/androidx/build/metalava/CheckApiCompatibilityTask.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/metalava/CheckApiCompatibilityTask.kt
@@ -71,7 +71,7 @@
@TaskAction
fun exec() {
- check(bootClasspath.isNotEmpty()) { "Android boot classpath not set." }
+ check(bootClasspath.files.isNotEmpty()) { "Android boot classpath not set." }
val apiLocation = api.get()
val referenceApiLocation = referenceApi.get()
diff --git a/buildSrc/src/main/kotlin/androidx/build/metalava/GenerateApiStubClassesTask.kt b/buildSrc/src/main/kotlin/androidx/build/metalava/GenerateApiStubClassesTask.kt
index c73f597..7980ce6 100644
--- a/buildSrc/src/main/kotlin/androidx/build/metalava/GenerateApiStubClassesTask.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/metalava/GenerateApiStubClassesTask.kt
@@ -37,7 +37,7 @@
runWithArgs(
listOf(
"--classpath",
- (bootClasspath + dependencyClasspath.files).joinToString(File.pathSeparator),
+ (bootClasspath.files + dependencyClasspath.files).joinToString(File.pathSeparator),
"--source-path",
sourcePaths.filter { it.exists() }.joinToString(File.pathSeparator),
diff --git a/buildSrc/src/main/kotlin/androidx/build/metalava/GenerateApiTask.kt b/buildSrc/src/main/kotlin/androidx/build/metalava/GenerateApiTask.kt
index 3694442..5291379 100644
--- a/buildSrc/src/main/kotlin/androidx/build/metalava/GenerateApiTask.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/metalava/GenerateApiTask.kt
@@ -60,7 +60,7 @@
abstract val apiLocation: Property<ApiLocation>
@OutputFiles
- fun getTaskOutputs(): List<File>? {
+ fun getTaskOutputs(): List<File> {
val prop = apiLocation.get()
return listOfNotNull(
prop.publicApiFile,
@@ -72,13 +72,13 @@
@TaskAction
fun exec() {
- check(bootClasspath.isNotEmpty()) { "Android boot classpath not set." }
+ check(bootClasspath.files.isNotEmpty()) { "Android boot classpath not set." }
check(sourcePaths.files.isNotEmpty()) { "Source paths not set." }
- val inputs = JavaCompileInputs.fromSourcesAndDeps(
+ val inputs = JavaCompileInputs(
sourcePaths,
dependencyClasspath,
- project
+ bootClasspath
)
generateApi(
metalavaClasspath,
diff --git a/buildSrc/src/main/kotlin/androidx/build/metalava/MetalavaRunner.kt b/buildSrc/src/main/kotlin/androidx/build/metalava/MetalavaRunner.kt
index 078160f..5e2508a 100644
--- a/buildSrc/src/main/kotlin/androidx/build/metalava/MetalavaRunner.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/metalava/MetalavaRunner.kt
@@ -214,7 +214,7 @@
// Gets arguments for generating the specified api file
private fun generateApi(
metalavaClasspath: FileCollection,
- bootClasspath: Collection<File>,
+ bootClasspath: FileCollection,
dependencyClasspath: FileCollection,
sourcePaths: Collection<File>,
outputLocation: ApiLocation,
@@ -232,7 +232,7 @@
// Generates the specified api file
fun getGenerateApiArgs(
- bootClasspath: Collection<File>,
+ bootClasspath: FileCollection,
dependencyClasspath: FileCollection,
sourcePaths: Collection<File>,
outputLocation: ApiLocation?,
@@ -243,7 +243,7 @@
// generate public API txt
val args = mutableListOf(
"--classpath",
- (bootClasspath + dependencyClasspath.files).joinToString(File.pathSeparator),
+ (bootClasspath.files + dependencyClasspath.files).joinToString(File.pathSeparator),
"--source-path",
sourcePaths.filter { it.exists() }.joinToString(File.pathSeparator),
diff --git a/buildSrc/src/main/kotlin/androidx/build/metalava/MetalavaTask.kt b/buildSrc/src/main/kotlin/androidx/build/metalava/MetalavaTask.kt
index f4d797b..1e867ee 100644
--- a/buildSrc/src/main/kotlin/androidx/build/metalava/MetalavaTask.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/metalava/MetalavaTask.kt
@@ -28,7 +28,6 @@
import org.gradle.api.tasks.PathSensitive
import org.gradle.api.tasks.PathSensitivity
import org.gradle.workers.WorkerExecutor
-import java.io.File
import javax.inject.Inject
/** Base class for invoking Metalava. */
@@ -40,9 +39,9 @@
@get:Classpath
abstract val metalavaClasspath: ConfigurableFileCollection
- /** Android's boot classpath. Obtained from [BaseExtension.getBootClasspath]. */
+ /** Android's boot classpath */
@get:Classpath
- lateinit var bootClasspath: Collection<File>
+ lateinit var bootClasspath: FileCollection
/** Dependencies of [sourcePaths]. */
@get:Classpath
diff --git a/buildSrc/src/main/kotlin/androidx/build/metalava/RegenerateOldApisTask.kt b/buildSrc/src/main/kotlin/androidx/build/metalava/RegenerateOldApisTask.kt
index 57d735e..63bd573 100644
--- a/buildSrc/src/main/kotlin/androidx/build/metalava/RegenerateOldApisTask.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/metalava/RegenerateOldApisTask.kt
@@ -20,6 +20,7 @@
import androidx.build.checkapi.getApiFileVersion
import androidx.build.checkapi.getVersionedApiLocation
import androidx.build.checkapi.isValidArtifactVersion
+import androidx.build.doclava.androidJarFile
import androidx.build.getCheckoutRoot
import androidx.build.java.JavaCompileInputs
import org.gradle.api.DefaultTask
@@ -102,7 +103,7 @@
val jars = getJars(runnerProject, mavenId)
val sources = getSources(runnerProject, mavenId + ":sources")
- return JavaCompileInputs.fromSourcesAndDeps(sources, jars, runnerProject)
+ return JavaCompileInputs(sources, jars, androidJarFile(project))
}
fun getJars(runnerProject: Project, mavenId: String): FileCollection {
diff --git a/buildSrc/src/main/kotlin/androidx/build/metalava/UpdateBaselineTasks.kt b/buildSrc/src/main/kotlin/androidx/build/metalava/UpdateBaselineTasks.kt
index 127a325..9fa99e3 100644
--- a/buildSrc/src/main/kotlin/androidx/build/metalava/UpdateBaselineTasks.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/metalava/UpdateBaselineTasks.kt
@@ -52,7 +52,7 @@
@TaskAction
fun updateBaseline() {
- check(bootClasspath.isNotEmpty()) { "Android boot classpath not set." }
+ check(bootClasspath.files.isNotEmpty()) { "Android boot classpath not set." }
val baselineFile = baselines.get().apiLintFile
val checkArgs = getGenerateApiArgs(
bootClasspath, dependencyClasspath,
@@ -106,7 +106,7 @@
@TaskAction
fun exec() {
- check(bootClasspath.isNotEmpty()) { "Android boot classpath not set." }
+ check(bootClasspath.files.isNotEmpty()) { "Android boot classpath not set." }
updateBaseline(
api.get().publicApiFile,
@@ -161,13 +161,13 @@
}
private fun getCommonBaselineUpdateArgs(
- bootClasspath: Collection<File>,
+ bootClasspath: FileCollection,
dependencyClasspath: FileCollection,
baselineFile: File
): MutableList<String> {
val args = mutableListOf(
"--classpath",
- (bootClasspath + dependencyClasspath.files).joinToString(File.pathSeparator)
+ (bootClasspath.files + dependencyClasspath.files).joinToString(File.pathSeparator)
)
args += getCommonBaselineUpdateArgs(baselineFile)
return args
diff --git a/camera/camera-camera2-pipe-integration/build.gradle b/camera/camera-camera2-pipe-integration/build.gradle
index e14d471..d7ee6fb 100644
--- a/camera/camera-camera2-pipe-integration/build.gradle
+++ b/camera/camera-camera2-pipe-integration/build.gradle
@@ -38,7 +38,7 @@
)
dependencies {
- implementation("androidx.core:core:1.1.0")
+ implementation("androidx.core:core:1.3.2")
implementation("androidx.concurrent:concurrent-listenablefuture-callback:1.0.0-beta01")
bundleInside(project(path: ":camera:camera-camera2-pipe", configuration: "exportRelease"))
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/MeteringRepeating.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/MeteringRepeating.kt
index d038a85..2007786 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/MeteringRepeating.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/MeteringRepeating.kt
@@ -72,6 +72,13 @@
deferrableSurface = null
}
+ /** Sets up the use case's session configuration, mainly its [DeferrableSurface]. */
+ fun setupSession() {
+ // The suggested resolution passed to `updateSuggestedResolution` doesn't matter since
+ // this use case uses the min preview size.
+ updateSuggestedResolution(DEFAULT_PREVIEW_SIZE)
+ }
+
private fun createPipeline(): SessionConfig.Builder {
val surfaceTexture = SurfaceTexture(0).apply {
setDefaultBufferSize(meteringSurfaceSize.width, meteringSurfaceSize.height)
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt
index 8a78385..7f210f9 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt
@@ -21,6 +21,7 @@
import androidx.camera.camera2.pipe.integration.config.CameraScope
import androidx.camera.camera2.pipe.integration.config.UseCaseCameraComponent
import androidx.camera.camera2.pipe.integration.config.UseCaseCameraConfig
+import androidx.camera.core.ImageCapture
import androidx.camera.core.UseCase
import javax.inject.Inject
@@ -32,10 +33,12 @@
private val cameraConfig: CameraConfig,
private val builder: UseCaseCameraComponent.Builder,
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") // Java version required for Dagger
- private val controls: java.util.Set<UseCaseCameraControl>
+ private val controls: java.util.Set<UseCaseCameraControl>,
+ cameraProperties: CameraProperties,
) {
private val attachedUseCases = mutableListOf<UseCase>()
private val enabledUseCases = mutableSetOf<UseCase>()
+ private val meteringRepeating by lazy { MeteringRepeating.Builder(cameraProperties).build() }
@Volatile
private var _activeComponent: UseCaseCameraComponent? = null
@@ -109,8 +112,10 @@
override fun toString(): String = "UseCaseManager<${cameraConfig.cameraId}>"
private fun invalidate() {
- camera?.let {
- it.activeUseCases = enabledUseCases.toSet()
+ when {
+ shouldAddRepeatingUseCase() -> addRepeatingUseCase()
+ shouldRemoveRepeatingUseCase() -> removeRepeatingUseCase()
+ else -> camera?.activeUseCases = enabledUseCases.toSet()
}
}
@@ -139,4 +144,36 @@
invalidate()
}
+
+ private fun shouldAddRepeatingUseCase(): Boolean {
+ return enabledUseCases.only { it is ImageCapture }
+ }
+
+ private fun addRepeatingUseCase() {
+ meteringRepeating.setupSession()
+ attach(listOf(meteringRepeating))
+ enable(meteringRepeating)
+ }
+
+ private fun shouldRemoveRepeatingUseCase(): Boolean {
+ val onlyMeteringRepeatingEnabled = enabledUseCases.only { it is MeteringRepeating }
+ val meteringRepeatingAndNonImageCaptureEnabled =
+ enabledUseCases.any { it is MeteringRepeating } &&
+ enabledUseCases.any { it !is MeteringRepeating && it !is ImageCapture }
+ return onlyMeteringRepeatingEnabled || meteringRepeatingAndNonImageCaptureEnabled
+ }
+
+ private fun removeRepeatingUseCase() {
+ disable(meteringRepeating)
+ detach(listOf(meteringRepeating))
+ meteringRepeating.onDetached()
+ }
+
+ /**
+ * Returns true when the collection only has elements (1 or more) that verify the predicate,
+ * false otherwise.
+ */
+ private fun <T> Collection<T>.only(predicate: (T) -> Boolean): Boolean {
+ return isNotEmpty() && all(predicate)
+ }
}
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManagerTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManagerTest.kt
new file mode 100644
index 0000000..ef234d33
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManagerTest.kt
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe.integration.impl
+
+import android.os.Build
+import androidx.camera.camera2.pipe.CameraId
+import androidx.camera.camera2.pipe.integration.adapter.RobolectricCameraPipeTestRunner
+import androidx.camera.camera2.pipe.integration.config.CameraConfig
+import androidx.camera.camera2.pipe.integration.testing.FakeCameraProperties
+import androidx.camera.camera2.pipe.integration.testing.FakeUseCaseCameraComponentBuilder
+import androidx.camera.core.ImageCapture
+import androidx.camera.core.Preview
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.annotation.Config
+import java.util.HashSet
+
+@RunWith(RobolectricCameraPipeTestRunner::class)
+@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
+class UseCaseManagerTest {
+
+ @Test
+ fun enabledUseCasesEmpty_whenUseCaseAttachedOnly() {
+ // Arrange
+ val useCaseManager = createUseCaseManager()
+ val useCase = Preview.Builder().build()
+
+ // Act
+ useCaseManager.attach(listOf(useCase))
+
+ // Assert
+ val enabledUseCases = useCaseManager.camera?.activeUseCases
+ assertThat(enabledUseCases).isEmpty()
+ }
+
+ @Test
+ fun enabledUseCasesNotEmpty_whenUseCaseEnabled() {
+ // Arrange
+ val useCaseManager = createUseCaseManager()
+ val useCase = Preview.Builder().build()
+ useCaseManager.attach(listOf(useCase))
+
+ // Act
+ useCaseManager.enable(useCase)
+
+ // Assert
+ val enabledUseCases = useCaseManager.camera?.activeUseCases
+ assertThat(enabledUseCases).containsExactly(useCase)
+ }
+
+ @Test
+ fun meteringRepeatingNotEnabled_whenPreviewEnabled() {
+ // Arrange
+ val useCaseManager = createUseCaseManager()
+ val preview = Preview.Builder().build()
+ val imageCapture = ImageCapture.Builder().build()
+ useCaseManager.attach(listOf(preview, imageCapture))
+
+ // Act
+ useCaseManager.enable(preview)
+ useCaseManager.enable(imageCapture)
+
+ // Assert
+ val enabledUseCases = useCaseManager.camera?.activeUseCases
+ assertThat(enabledUseCases).containsExactly(preview, imageCapture)
+ }
+
+ @Test
+ fun meteringRepeatingEnabled_whenOnlyImageCaptureEnabled() {
+ // Arrange
+ val useCaseManager = createUseCaseManager()
+ val imageCapture = ImageCapture.Builder().build()
+ useCaseManager.attach(listOf(imageCapture))
+
+ // Act
+ useCaseManager.enable(imageCapture)
+
+ // Assert
+ val enabledUseCaseClasses = useCaseManager.camera?.activeUseCases?.map { it::class.java }
+ assertThat(enabledUseCaseClasses).containsExactly(
+ ImageCapture::class.java,
+ MeteringRepeating::class.java
+ )
+ }
+
+ @Test
+ fun meteringRepeatingDisabled_whenPreviewBecomesEnabled() {
+ // Arrange
+ val useCaseManager = createUseCaseManager()
+ val imageCapture = ImageCapture.Builder().build()
+ useCaseManager.attach(listOf(imageCapture))
+ useCaseManager.enable(imageCapture)
+
+ // Act
+ val preview = Preview.Builder().build()
+ useCaseManager.attach(listOf(preview))
+ useCaseManager.enable(preview)
+
+ // Assert
+ val activeUseCases = useCaseManager.camera?.activeUseCases
+ assertThat(activeUseCases).containsExactly(preview, imageCapture)
+ }
+
+ @Test
+ fun meteringRepeatingEnabled_afterAllUseCasesButImageCaptureDisabled() {
+ // Arrange
+ val useCaseManager = createUseCaseManager()
+ val preview = Preview.Builder().build()
+ val imageCapture = ImageCapture.Builder().build()
+ useCaseManager.attach(listOf(preview, imageCapture))
+ useCaseManager.enable(preview)
+ useCaseManager.enable(imageCapture)
+
+ // Act
+ useCaseManager.disable(preview)
+
+ // Assert
+ val enabledUseCaseClasses = useCaseManager.camera?.activeUseCases?.map { it::class.java }
+ assertThat(enabledUseCaseClasses).containsExactly(
+ ImageCapture::class.java,
+ MeteringRepeating::class.java
+ )
+ }
+
+ @Test
+ fun meteringRepeatingDisabled_whenAllUseCasesDisabled() {
+ // Arrange
+ val useCaseManager = createUseCaseManager()
+ val imageCapture = ImageCapture.Builder().build()
+ useCaseManager.attach(listOf(imageCapture))
+ useCaseManager.enable(imageCapture)
+
+ // Act
+ useCaseManager.disable(imageCapture)
+
+ // Assert
+ val enabledUseCases = useCaseManager.camera?.activeUseCases
+ assertThat(enabledUseCases).isEmpty()
+ }
+
+ @Suppress("UNCHECKED_CAST", "PLATFORM_CLASS_MAPPED_TO_KOTLIN")
+ private fun createUseCaseManager() = UseCaseManager(
+ cameraConfig = CameraConfig(CameraId("0")),
+ builder = FakeUseCaseCameraComponentBuilder(),
+ controls = HashSet<UseCaseCameraControl>() as java.util.Set<UseCaseCameraControl>,
+ cameraProperties = FakeCameraProperties()
+ )
+}
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeCameraProperties.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeCameraProperties.kt
new file mode 100644
index 0000000..3a459e0
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeCameraProperties.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe.integration.testing
+
+import androidx.camera.camera2.pipe.CameraId
+import androidx.camera.camera2.pipe.CameraMetadata
+import androidx.camera.camera2.pipe.integration.impl.CameraProperties
+import androidx.camera.camera2.pipe.testing.FakeCameraMetadata
+
+class FakeCameraProperties(
+ override val metadata: CameraMetadata = FakeCameraMetadata(),
+ override val cameraId: CameraId = metadata.camera,
+) : CameraProperties
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeUseCaseCamera.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeUseCaseCamera.kt
new file mode 100644
index 0000000..9b3adc9
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeUseCaseCamera.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe.integration.testing
+
+import android.hardware.camera2.CaptureRequest
+import android.hardware.camera2.params.MeteringRectangle
+import androidx.camera.camera2.pipe.Result3A
+import androidx.camera.camera2.pipe.integration.config.UseCaseCameraComponent
+import androidx.camera.camera2.pipe.integration.config.UseCaseCameraConfig
+import androidx.camera.camera2.pipe.integration.impl.UseCaseCamera
+import androidx.camera.core.UseCase
+import androidx.camera.core.impl.CaptureConfig
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.Deferred
+
+class FakeUseCaseCameraComponentBuilder : UseCaseCameraComponent.Builder {
+ private var config: UseCaseCameraConfig = UseCaseCameraConfig(emptyList())
+
+ override fun config(config: UseCaseCameraConfig): UseCaseCameraComponent.Builder {
+ this.config = config
+ return this
+ }
+
+ override fun build(): UseCaseCameraComponent {
+ return FakeUseCaseCameraComponent(config.provideUseCaseList())
+ }
+}
+
+class FakeUseCaseCameraComponent(useCases: List<UseCase>) : UseCaseCameraComponent {
+ private val fakeUseCaseCamera = FakeUseCaseCamera(useCases.toSet())
+
+ override fun getUseCaseCamera(): UseCaseCamera {
+ return fakeUseCaseCamera
+ }
+}
+
+// TODO: Further implement the methods in this class as needed
+class FakeUseCaseCamera(override var activeUseCases: Set<UseCase> = emptySet()) : UseCaseCamera {
+
+ override fun <T> setParameter(key: CaptureRequest.Key<T>, value: T) {
+ }
+
+ override fun <T> setParameterAsync(key: CaptureRequest.Key<T>, value: T): Deferred<Unit> {
+ return CompletableDeferred<Unit>().apply { complete(Unit) }
+ }
+
+ override fun <T> setParameters(values: Map<CaptureRequest.Key<*>, Any>) {
+ }
+
+ override fun <T> setParametersAsync(values: Map<CaptureRequest.Key<*>, Any>): Deferred<Unit> {
+ return CompletableDeferred<Unit>().apply { complete(Unit) }
+ }
+
+ override suspend fun setTorchAsync(enabled: Boolean): Deferred<Result3A> {
+ return CompletableDeferred<Result3A>().apply {
+ complete(Result3A(status = Result3A.Status.OK))
+ }
+ }
+
+ override suspend fun startFocusAndMeteringAsync(
+ aeRegions: List<MeteringRectangle>,
+ afRegions: List<MeteringRectangle>,
+ awbRegions: List<MeteringRectangle>
+ ): Deferred<Result3A> {
+ return CompletableDeferred<Result3A>().apply {
+ complete(Result3A(status = Result3A.Status.OK))
+ }
+ }
+
+ override fun capture(captureSequence: List<CaptureConfig>) {
+ }
+
+ override fun close() {
+ }
+}
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageCaptureTest.kt b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageCaptureTest.kt
index cc4004b..1f2babb 100644
--- a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageCaptureTest.kt
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ImageCaptureTest.kt
@@ -836,6 +836,8 @@
@Test
fun defaultAspectRatioWillBeSet_whenTargetResolutionIsNotSet() {
+ skipTestOnCameraPipeConfig()
+
val useCase = ImageCapture.Builder().build()
camera = CameraUtil.createCameraAndAttachUseCase(context, BACK_SELECTOR, useCase)
@@ -845,6 +847,8 @@
@Test
fun defaultAspectRatioWontBeSet_whenTargetResolutionIsSet() {
+ skipTestOnCameraPipeConfig()
+
val useCase = ImageCapture.Builder()
.setTargetResolution(DEFAULT_RESOLUTION)
.build()
@@ -875,6 +879,8 @@
@Test
fun targetResolutionIsUpdatedAfterTargetRotationIsUpdated() {
+ skipTestOnCameraPipeConfig()
+
val imageCapture = ImageCapture.Builder()
.setTargetResolution(DEFAULT_RESOLUTION)
.setTargetRotation(Surface.ROTATION_0)
@@ -1039,6 +1045,8 @@
@Test
fun useCaseConfigCanBeReset_afterUnbind() = runBlocking {
+ skipTestOnCameraPipeConfig()
+
val useCase = defaultBuilder.build()
val initialConfig = useCase.currentConfig
camera = CameraUtil.createCameraAndAttachUseCase(context, BACK_SELECTOR, useCase)
@@ -1053,6 +1061,8 @@
@Test
fun targetRotationIsRetained_whenUseCaseIsReused() = runBlocking {
+ skipTestOnCameraPipeConfig()
+
val useCase = defaultBuilder.build()
camera = CameraUtil.createCameraAndAttachUseCase(context, BACK_SELECTOR, useCase)
@@ -1206,6 +1216,8 @@
@Test
fun returnCorrectTargetRotation_afterUseCaseIsAttached() {
+ skipTestOnCameraPipeConfig()
+
val imageCapture = ImageCapture.Builder()
.setTargetRotation(Surface.ROTATION_180)
.build()
@@ -1221,6 +1233,8 @@
@Test
fun returnCorrectFlashMode_afterUseCaseIsAttached() {
+ skipTestOnCameraPipeConfig()
+
val imageCapture = ImageCapture.Builder()
.setFlashMode(ImageCapture.FLASH_MODE_ON)
.build()
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/UseCaseCombinationTest.kt b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/UseCaseCombinationTest.kt
index 9cdff8a..2ff1686 100644
--- a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/UseCaseCombinationTest.kt
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/UseCaseCombinationTest.kt
@@ -31,6 +31,7 @@
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import org.junit.After
+import org.junit.Assume
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -75,6 +76,8 @@
/** Test Combination: Preview + ImageCapture */
@Test
fun previewCombinesImageCapture() = runBlocking {
+ skipTestOnCameraPipeConfig()
+
val preview = initPreview()
val imageCapture = initImageCapture()
@@ -105,6 +108,8 @@
/** Test Combination: Preview + ImageAnalysis + ImageCapture */
@Test
fun previewCombinesImageAnalysisAndImageCapture() = runBlocking {
+ skipTestOnCameraPipeConfig()
+
val preview = initPreview()
val imageAnalysis = initImageAnalysis()
val imageCapture = initImageCapture()
@@ -134,4 +139,13 @@
private fun initImageCapture(): ImageCapture {
return ImageCapture.Builder().build()
}
+
+ // TODO(b/187015621): Remove when DeferrableSurface reference count support is added to
+ // Camera-pipe-integration
+ private fun skipTestOnCameraPipeConfig() {
+ Assume.assumeFalse(
+ "DeferrableSurface ref count isn't supported on Camera-pipe-integration (b/187015621)",
+ implName == CameraPipeConfig::class.simpleName
+ )
+ }
}
diff --git a/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/CameraExtensionsActivity.java b/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/CameraExtensionsActivity.java
index d84d77d..95d5f5b 100644
--- a/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/CameraExtensionsActivity.java
+++ b/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/CameraExtensionsActivity.java
@@ -119,27 +119,10 @@
}
}
- /**
- * Sets up the appropriate UseCases.
- */
- private void bindUseCases() {
- ImageCapture.Builder imageCaptureBuilder = new ImageCapture.Builder().setTargetName(
- "ImageCapture");
- mImageCapture = imageCaptureBuilder.build();
-
- Preview.Builder previewBuilder = new Preview.Builder().setTargetName("Preview");
-
- mPreview = previewBuilder.build();
- mPreview.setSurfaceProvider(mPreviewView.getSurfaceProvider());
-
- mCamera = mCameraProvider.bindToLifecycle(this, mCurrentCameraSelector,
- mImageCapture, mPreview);
- }
-
void setupButtons() {
Button btnToggleMode = findViewById(R.id.PhotoToggle);
Button btnSwitchCamera = findViewById(R.id.Switch);
- btnToggleMode.setOnClickListener(view -> enableNextExtension());
+ btnToggleMode.setOnClickListener(view -> bindUseCasesWithNextExtension());
btnSwitchCamera.setOnClickListener(view -> switchCameras());
}
@@ -147,8 +130,7 @@
mCameraProvider.unbindAll();
mCurrentCameraSelector = (mCurrentCameraSelector == CameraSelector.DEFAULT_BACK_CAMERA)
? CameraSelector.DEFAULT_FRONT_CAMERA : CameraSelector.DEFAULT_BACK_CAMERA;
- bindUseCases();
- enableExtension(mCurrentImageCaptureType);
+ bindUseCasesWithExtension(mCurrentImageCaptureType);
}
@Extensions.ExtensionMode
@@ -172,30 +154,37 @@
}
}
- void enableNextExtension() {
+ void bindUseCasesWithNextExtension() {
do {
mCurrentImageCaptureType = mCurrentImageCaptureType.getNextType();
- } while (!enableExtension(mCurrentImageCaptureType));
+ } while (!bindUseCasesWithExtension(mCurrentImageCaptureType));
}
// TODO(b/162875208) Suppress until new extensions API made public
@SuppressLint("RestrictedAPI")
- boolean enableExtension(ImageCaptureType imageCaptureType) {
+ boolean bindUseCasesWithExtension(ImageCaptureType imageCaptureType) {
// Check that extension can be enabled and if so enable it
@Extensions.ExtensionMode
int extensionMode = extensionModeFrom(imageCaptureType);
-
if (!mExtensions.isExtensionAvailable(mCameraProvider, mCurrentCameraSelector,
extensionMode)) {
return false;
}
+ ImageCapture.Builder imageCaptureBuilder = new ImageCapture.Builder().setTargetName(
+ "ImageCapture");
+ mImageCapture = imageCaptureBuilder.build();
+
+ Preview.Builder previewBuilder = new Preview.Builder().setTargetName("Preview");
+
+ mPreview = previewBuilder.build();
+ mPreview.setSurfaceProvider(mPreviewView.getSurfaceProvider());
+
CameraSelector cameraSelector = mExtensions.getExtensionCameraSelector(
mCurrentCameraSelector, extensionMode);
mCameraProvider.unbindAll();
-
- mCameraProvider.bindToLifecycle(this, cameraSelector, mImageCapture, mPreview);
+ mCamera = mCameraProvider.bindToLifecycle(this, cameraSelector, mImageCapture, mPreview);
// Update the UI and save location for ImageCapture
Button toggleButton = findViewById(R.id.PhotoToggle);
@@ -269,13 +258,6 @@
return true;
}
- /** Creates all the use cases. */
- void createUseCases() {
- ExtensionsManager.setExtensionsErrorListener((errorCode) ->
- Log.d(TAG, "Extensions error in error code: " + errorCode));
- bindUseCases();
- }
-
@SuppressWarnings("UnstableApiUsage")
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -339,8 +321,9 @@
case NONE:
mExtensions = ExtensionsManager.getExtensions(
getApplicationContext());
- createUseCases();
- enableNextExtension();
+ ExtensionsManager.setExtensionsErrorListener((errorCode) ->
+ Log.d(TAG, "Extensions error in error code: " + errorCode));
+ bindUseCasesWithNextExtension();
setupButtons();
break;
case LIBRARY_UNAVAILABLE_ERROR_LOADING:
@@ -407,9 +390,8 @@
motionEvent.getX(), motionEvent.getY());
mCamera.getCameraControl().startFocusAndMetering(
- new FocusMeteringAction.Builder(point).build())
- .addListener(() -> {},
- ContextCompat.getMainExecutor(CameraExtensionsActivity.this));
+ new FocusMeteringAction.Builder(point).build()).addListener(() -> {},
+ ContextCompat.getMainExecutor(CameraExtensionsActivity.this));
}
return true;
});
diff --git a/car/app/app-samples/navigation/common/src/main/java/androidx/car/app/sample/navigation/common/app/MainActivity.java b/car/app/app-samples/navigation/common/src/main/java/androidx/car/app/sample/navigation/common/app/MainActivity.java
index 9acd598..50f48ba2 100644
--- a/car/app/app-samples/navigation/common/src/main/java/androidx/car/app/sample/navigation/common/app/MainActivity.java
+++ b/car/app/app-samples/navigation/common/src/main/java/androidx/car/app/sample/navigation/common/app/MainActivity.java
@@ -30,7 +30,7 @@
import androidx.activity.ComponentActivity;
import androidx.annotation.Nullable;
-import androidx.car.app.connection.ConnectionToCar;
+import androidx.car.app.connection.CarConnection;
import androidx.car.app.sample.navigation.common.R;
import androidx.car.app.sample.navigation.common.nav.NavigationService;
@@ -82,7 +82,7 @@
Button stopNavButton = findViewById(R.id.stop_nav);
stopNavButton.setOnClickListener(this::stopNavigation);
- new ConnectionToCar(this).getType().observe(this,
+ new CarConnection(this).getType().observe(this,
this::onConnectionStateUpdate);
}
@@ -112,7 +112,7 @@
}
private void onConnectionStateUpdate(Integer connectionState) {
- String message = connectionState > ConnectionToCar.NOT_CONNECTED
+ String message = connectionState > CarConnection.CONNECTION_TYPE_NOT_CONNECTED
? "Connected to a car head unit"
: "Not Connected to a car head unit";
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
diff --git a/car/app/app-samples/showcase/common/src/main/AndroidManifest.xml b/car/app/app-samples/showcase/common/src/main/AndroidManifest.xml
index 4e1a805..330eeab 100644
--- a/car/app/app-samples/showcase/common/src/main/AndroidManifest.xml
+++ b/car/app/app-samples/showcase/common/src/main/AndroidManifest.xml
@@ -14,4 +14,10 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<manifest package="androidx.car.app.sample.showcase.common" />
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="androidx.car.app.sample.showcase.common">
+ <application>
+ <activity android:name=".templates.SignInWithGoogleActivity" />
+ </application>
+</manifest>
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/SignInTemplateDemoScreen.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/SignInTemplateDemoScreen.java
index 3f0c42f..365b683 100644
--- a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/SignInTemplateDemoScreen.java
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/SignInTemplateDemoScreen.java
@@ -17,7 +17,6 @@
package androidx.car.app.sample.showcase.common.templates;
import static androidx.car.app.CarToast.LENGTH_LONG;
-import static androidx.car.app.CarToast.LENGTH_SHORT;
import android.graphics.Color;
@@ -62,7 +61,7 @@
private String mErrorMessage;
private final CharSequence mAdditionalText = Utils.clickable("Please review our terms of "
- + "service", 18, 16,
+ + "service", 18, 16,
() -> getScreenManager().push(new LongMessageTemplateDemoScreen(getCarContext())));
private final Action mProviderSignInAction = new Action.Builder()
@@ -227,12 +226,7 @@
.setTint(noTint)
.build())
.setOnClickListener(ParkedOnlyOnClickListener.create(
- () -> CarToast.makeText(
- getCarContext(),
- "Sign-in with Google starts here",
- LENGTH_SHORT)
- .show()))
- .build()
+ this::performSignInWithGoogleFlow)).build()
).build();
return new SignInTemplate.Builder(providerSignInMethod)
@@ -243,6 +237,31 @@
.build();
}
+ private void performSignInWithGoogleFlow() {
+ // This is here for demonstration purposes, if the APK is not signed with a signature
+ // that has been registered for sign in with Google flow, the sign in will fail at runtime.
+
+// Bundle extras = new Bundle(1);
+// extras.putBinder(BINDER_KEY, new SignInWithGoogleActivity.OnSignInComplete() {
+// @Override
+// public void onSignInComplete(@Nullable GoogleSignInAccount account) {
+// if (account == null) {
+// CarToast.makeText(getCarContext(), "Error signing in", LENGTH_LONG).show();
+// return;
+// }
+//
+// // Use the account
+// CarToast.makeText(getCarContext(),
+// account.getGivenName() + " signed in", LENGTH_LONG).show();
+// }
+// });
+// getCarContext().startActivity(
+// new Intent()
+// .setClass(getCarContext(), SignInWithGoogleActivity.class)
+// .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+// .putExtras(extras));
+ }
+
private MessageTemplate getSignInCompletedMessageTemplate() {
return new MessageTemplate.Builder("You are signed in!")
.setTitle("Sign in completed")
@@ -256,4 +275,5 @@
.build())
.build();
}
+
}
diff --git a/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/SignInWithGoogleActivity.java b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/SignInWithGoogleActivity.java
new file mode 100644
index 0000000..d7bf59f
--- /dev/null
+++ b/car/app/app-samples/showcase/common/src/main/java/androidx/car/app/sample/showcase/common/templates/SignInWithGoogleActivity.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.car.app.sample.showcase.common.templates;
+
+import android.os.Bundle;
+
+import androidx.activity.ComponentActivity;
+import androidx.annotation.Nullable;
+
+/**
+ * An activity for use by the car app library to perform actions such as requesting permissions.
+ */
+public class SignInWithGoogleActivity extends ComponentActivity {
+ static final String BINDER_KEY = "binder";
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+// OnSignInComplete signInCompleteCallback =
+// (OnSignInComplete) getIntent().getExtras().getBinder(BINDER_KEY);
+//
+// ActivityResultLauncher<Intent> activityResultLauncher =
+// registerForActivityResult(
+// new ActivityResultContracts.StartActivityForResult(),
+// result -> {
+// GoogleSignInAccount account =
+// GoogleSignIn.getSignedInAccountFromIntent(
+// result.getData()).getResult();
+// signInCompleteCallback.onSignInComplete(account);
+// finish();
+// });
+//
+// GoogleSignInAccount account = GoogleSignIn.getLastSignedInAccount(this);
+// if (account != null) {
+// signInCompleteCallback.onSignInComplete(account);
+// finish();
+// }
+//
+// GoogleSignInOptions gso =
+// new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
+// .requestEmail()
+// .requestProfile()
+// .build();
+// GoogleSignInClient signInClient = GoogleSignIn.getClient(this, gso);
+// activityResultLauncher.launch(signInClient.getSignInIntent());
+ }
+
+
+// /**
+// * Binder callback to provide to the sign in activity.
+// */
+// abstract static class OnSignInComplete extends Binder implements IBinder {
+// /**
+// * Notifies that sign in flow completed.
+// *
+// * @param account the account signed in or {@code null} if there were issues signing in.
+// */
+// public abstract void onSignInComplete(@Nullable GoogleSignInAccount account);
+// }
+}
diff --git a/car/app/app/api/current.txt b/car/app/app/api/current.txt
index 8d47bcd..777910c 100644
--- a/car/app/app/api/current.txt
+++ b/car/app/app/api/current.txt
@@ -179,14 +179,24 @@
package androidx.car.app.connection {
- public final class ConnectionToCar {
- ctor public ConnectionToCar(android.content.Context);
+ public final class CarConnection {
+ ctor public CarConnection(android.content.Context);
method public androidx.lifecycle.LiveData<java.lang.Integer!> getType();
field public static final String ACTION_CAR_CONNECTION_UPDATED = "androidx.car.app.connection.action.CAR_CONNECTION_UPDATED";
field public static final String CAR_CONNECTION_STATE = "CarConnectionState";
- field public static final int NATIVE = 1; // 0x1
- field public static final int NOT_CONNECTED = 0; // 0x0
- field public static final int PROJECTION = 2; // 0x2
+ field public static final int CONNECTION_TYPE_NATIVE = 1; // 0x1
+ field public static final int CONNECTION_TYPE_NOT_CONNECTED = 0; // 0x0
+ field public static final int CONNECTION_TYPE_PROJECTION = 2; // 0x2
+ }
+
+ @Deprecated public final class ConnectionToCar {
+ ctor @Deprecated public ConnectionToCar(android.content.Context);
+ method @Deprecated public androidx.lifecycle.LiveData<java.lang.Integer!> getType();
+ field @Deprecated public static final String ACTION_CAR_CONNECTION_UPDATED = "androidx.car.app.connection.action.CAR_CONNECTION_UPDATED";
+ field @Deprecated public static final String CAR_CONNECTION_STATE = "CarConnectionState";
+ field @Deprecated public static final int NATIVE = 1; // 0x1
+ field @Deprecated public static final int NOT_CONNECTED = 0; // 0x0
+ field @Deprecated public static final int PROJECTION = 2; // 0x2
}
}
@@ -1050,6 +1060,7 @@
}
public static final class Step.Builder {
+ ctor public Step.Builder();
ctor public Step.Builder(CharSequence);
ctor public Step.Builder(androidx.car.app.model.CarText);
method public androidx.car.app.navigation.model.Step.Builder addLane(androidx.car.app.navigation.model.Lane);
diff --git a/car/app/app/api/public_plus_experimental_current.txt b/car/app/app/api/public_plus_experimental_current.txt
index 0fafb1f..29159e0 100644
--- a/car/app/app/api/public_plus_experimental_current.txt
+++ b/car/app/app/api/public_plus_experimental_current.txt
@@ -182,14 +182,24 @@
package androidx.car.app.connection {
- public final class ConnectionToCar {
- ctor public ConnectionToCar(android.content.Context);
+ public final class CarConnection {
+ ctor public CarConnection(android.content.Context);
method public androidx.lifecycle.LiveData<java.lang.Integer!> getType();
field public static final String ACTION_CAR_CONNECTION_UPDATED = "androidx.car.app.connection.action.CAR_CONNECTION_UPDATED";
field public static final String CAR_CONNECTION_STATE = "CarConnectionState";
- field public static final int NATIVE = 1; // 0x1
- field public static final int NOT_CONNECTED = 0; // 0x0
- field public static final int PROJECTION = 2; // 0x2
+ field public static final int CONNECTION_TYPE_NATIVE = 1; // 0x1
+ field public static final int CONNECTION_TYPE_NOT_CONNECTED = 0; // 0x0
+ field public static final int CONNECTION_TYPE_PROJECTION = 2; // 0x2
+ }
+
+ @Deprecated public final class ConnectionToCar {
+ ctor @Deprecated public ConnectionToCar(android.content.Context);
+ method @Deprecated public androidx.lifecycle.LiveData<java.lang.Integer!> getType();
+ field @Deprecated public static final String ACTION_CAR_CONNECTION_UPDATED = "androidx.car.app.connection.action.CAR_CONNECTION_UPDATED";
+ field @Deprecated public static final String CAR_CONNECTION_STATE = "CarConnectionState";
+ field @Deprecated public static final int NATIVE = 1; // 0x1
+ field @Deprecated public static final int NOT_CONNECTED = 0; // 0x0
+ field @Deprecated public static final int PROJECTION = 2; // 0x2
}
}
@@ -1053,6 +1063,7 @@
}
public static final class Step.Builder {
+ ctor public Step.Builder();
ctor public Step.Builder(CharSequence);
ctor public Step.Builder(androidx.car.app.model.CarText);
method public androidx.car.app.navigation.model.Step.Builder addLane(androidx.car.app.navigation.model.Lane);
diff --git a/car/app/app/api/restricted_current.txt b/car/app/app/api/restricted_current.txt
index 8d47bcd..777910c 100644
--- a/car/app/app/api/restricted_current.txt
+++ b/car/app/app/api/restricted_current.txt
@@ -179,14 +179,24 @@
package androidx.car.app.connection {
- public final class ConnectionToCar {
- ctor public ConnectionToCar(android.content.Context);
+ public final class CarConnection {
+ ctor public CarConnection(android.content.Context);
method public androidx.lifecycle.LiveData<java.lang.Integer!> getType();
field public static final String ACTION_CAR_CONNECTION_UPDATED = "androidx.car.app.connection.action.CAR_CONNECTION_UPDATED";
field public static final String CAR_CONNECTION_STATE = "CarConnectionState";
- field public static final int NATIVE = 1; // 0x1
- field public static final int NOT_CONNECTED = 0; // 0x0
- field public static final int PROJECTION = 2; // 0x2
+ field public static final int CONNECTION_TYPE_NATIVE = 1; // 0x1
+ field public static final int CONNECTION_TYPE_NOT_CONNECTED = 0; // 0x0
+ field public static final int CONNECTION_TYPE_PROJECTION = 2; // 0x2
+ }
+
+ @Deprecated public final class ConnectionToCar {
+ ctor @Deprecated public ConnectionToCar(android.content.Context);
+ method @Deprecated public androidx.lifecycle.LiveData<java.lang.Integer!> getType();
+ field @Deprecated public static final String ACTION_CAR_CONNECTION_UPDATED = "androidx.car.app.connection.action.CAR_CONNECTION_UPDATED";
+ field @Deprecated public static final String CAR_CONNECTION_STATE = "CarConnectionState";
+ field @Deprecated public static final int NATIVE = 1; // 0x1
+ field @Deprecated public static final int NOT_CONNECTED = 0; // 0x0
+ field @Deprecated public static final int PROJECTION = 2; // 0x2
}
}
@@ -1050,6 +1060,7 @@
}
public static final class Step.Builder {
+ ctor public Step.Builder();
ctor public Step.Builder(CharSequence);
ctor public Step.Builder(androidx.car.app.model.CarText);
method public androidx.car.app.navigation.model.Step.Builder addLane(androidx.car.app.navigation.model.Lane);
diff --git a/car/app/app/src/main/java/androidx/car/app/connection/AutomotiveConnectionToCarTypeLiveData.java b/car/app/app/src/main/java/androidx/car/app/connection/AutomotiveCarConnectionTypeLiveData.java
similarity index 79%
rename from car/app/app/src/main/java/androidx/car/app/connection/AutomotiveConnectionToCarTypeLiveData.java
rename to car/app/app/src/main/java/androidx/car/app/connection/AutomotiveCarConnectionTypeLiveData.java
index f8857be..b35606b 100644
--- a/car/app/app/src/main/java/androidx/car/app/connection/AutomotiveConnectionToCarTypeLiveData.java
+++ b/car/app/app/src/main/java/androidx/car/app/connection/AutomotiveCarConnectionTypeLiveData.java
@@ -16,15 +16,15 @@
package androidx.car.app.connection;
-import androidx.car.app.connection.ConnectionToCar.ConnectionType;
+import androidx.car.app.connection.CarConnection.ConnectionType;
import androidx.lifecycle.LiveData;
/**
* A {@link LiveData} that always returns that it is connected natively to a car head unit.
*/
-final class AutomotiveConnectionToCarTypeLiveData extends LiveData<@ConnectionType Integer> {
+final class AutomotiveCarConnectionTypeLiveData extends LiveData<@ConnectionType Integer> {
@Override
protected void onActive() {
- setValue(ConnectionToCar.NATIVE);
+ setValue(CarConnection.CONNECTION_TYPE_NATIVE);
}
}
diff --git a/car/app/app/src/main/java/androidx/car/app/connection/CarConnection.java b/car/app/app/src/main/java/androidx/car/app/connection/CarConnection.java
new file mode 100644
index 0000000..79d81bd
--- /dev/null
+++ b/car/app/app/src/main/java/androidx/car/app/connection/CarConnection.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.car.app.connection;
+
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+import static androidx.car.app.utils.CommonUtils.isAutomotiveOS;
+
+import static java.util.Objects.requireNonNull;
+
+import android.content.Context;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.lifecycle.LiveData;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * A class that allows retrieval of information about connection to a car head unit.
+ */
+public final class CarConnection {
+ /**
+ * Defines current car connection state.
+ *
+ * <p>This is used for communication with the car host's content provider on queries for
+ * connection type.
+ */
+ public static final String CAR_CONNECTION_STATE = "CarConnectionState";
+
+ /**
+ * Broadcast action that notifies that the car connection has changed and needs to be updated.
+ */
+ public static final String ACTION_CAR_CONNECTION_UPDATED =
+ "androidx.car.app.connection.action.CAR_CONNECTION_UPDATED";
+
+ /**
+ * Represents the types of connections that exist to a car head unit.
+ *
+ * @hide
+ */
+ @IntDef({CONNECTION_TYPE_NOT_CONNECTED, CONNECTION_TYPE_NATIVE, CONNECTION_TYPE_PROJECTION})
+ @Retention(RetentionPolicy.SOURCE)
+ @Target({ElementType.TYPE_USE})
+ @RestrictTo(LIBRARY)
+ public @interface ConnectionType {
+ }
+
+ /**
+ * Not connected to any car head unit.z
+ */
+ public static final int CONNECTION_TYPE_NOT_CONNECTED = 0;
+
+ /**
+ * Natively running on a head unit (Android Automotive OS).
+ */
+ public static final int CONNECTION_TYPE_NATIVE = 1;
+
+ /**
+ * Connected to a car head unit by projecting to it.
+ */
+ public static final int CONNECTION_TYPE_PROJECTION = 2;
+
+ private final LiveData<Integer> mConnectionTypeLiveData;
+
+ /**
+ * Constructs a {@link CarConnection} that can be used to get connection information.
+ *
+ * @throws NullPointerException if {@code context} is {@code null}
+ */
+ public CarConnection(@NonNull Context context) {
+ requireNonNull(context);
+ mConnectionTypeLiveData = isAutomotiveOS(context)
+ ? new AutomotiveCarConnectionTypeLiveData()
+ : new CarConnectionTypeLiveData(context);
+ }
+
+ /**
+ * Returns a {@link LiveData} that can be observed to get current connection type.
+ *
+ * <p>The recommended pattern is to observe the {@link LiveData} with the activity's
+ * lifecycle in order to get updates on the state change throughout the activity's lifetime.
+ *
+ * <p>Connection types are:
+ * <ol>
+ * <li>{@link #CONNECTION_TYPE_NOT_CONNECTED}
+ * <li>{@link #CONNECTION_TYPE_NATIVE}
+ * <li>{@link #CONNECTION_TYPE_PROJECTION}
+ * </ol>
+ */
+ @NonNull
+ public LiveData<@ConnectionType Integer> getType() {
+ return mConnectionTypeLiveData;
+ }
+}
diff --git a/car/app/app/src/main/java/androidx/car/app/connection/ConnectionToCarTypeLiveData.java b/car/app/app/src/main/java/androidx/car/app/connection/CarConnectionTypeLiveData.java
similarity index 84%
rename from car/app/app/src/main/java/androidx/car/app/connection/ConnectionToCarTypeLiveData.java
rename to car/app/app/src/main/java/androidx/car/app/connection/CarConnectionTypeLiveData.java
index 77d714a..79f30f9 100644
--- a/car/app/app/src/main/java/androidx/car/app/connection/ConnectionToCarTypeLiveData.java
+++ b/car/app/app/src/main/java/androidx/car/app/connection/CarConnectionTypeLiveData.java
@@ -16,8 +16,8 @@
package androidx.car.app.connection;
-import static androidx.car.app.connection.ConnectionToCar.ACTION_CAR_CONNECTION_UPDATED;
-import static androidx.car.app.connection.ConnectionToCar.CAR_CONNECTION_STATE;
+import static androidx.car.app.connection.CarConnection.ACTION_CAR_CONNECTION_UPDATED;
+import static androidx.car.app.connection.CarConnection.CAR_CONNECTION_STATE;
import static androidx.car.app.utils.LogTags.TAG_CONNECTION_TO_CAR;
import android.content.AsyncQueryHandler;
@@ -31,14 +31,14 @@
import android.util.Log;
import androidx.annotation.VisibleForTesting;
-import androidx.car.app.connection.ConnectionToCar.ConnectionType;
+import androidx.car.app.connection.CarConnection.ConnectionType;
import androidx.lifecycle.LiveData;
/**
* A {@link LiveData} that will query once while being observed and only again if it gets updates
* via a broadcast.
*/
-final class ConnectionToCarTypeLiveData extends LiveData<@ConnectionType Integer> {
+final class CarConnectionTypeLiveData extends LiveData<@ConnectionType Integer> {
@VisibleForTesting
static final String CAR_CONNECTION_AUTHORITY = "androidx.car.app.connection";
@@ -50,7 +50,7 @@
private final AsyncQueryHandler mQueryHandler;
private final CarConnectionBroadcastReceiver mBroadcastReceiver;
- ConnectionToCarTypeLiveData(Context context) {
+ CarConnectionTypeLiveData(Context context) {
mContext = context;
mQueryHandler = new CarConnectionQueryHandler(
@@ -88,7 +88,7 @@
if (response == null) {
Log.w(TAG_CONNECTION_TO_CAR, "Null response from content provider when checking "
+ "connection to the car, treating as disconnected");
- postValue(ConnectionToCar.NOT_CONNECTED);
+ postValue(CarConnection.CONNECTION_TYPE_NOT_CONNECTED);
return;
}
@@ -96,14 +96,14 @@
if (carConnectionTypeColumn < 0) {
Log.e(TAG_CONNECTION_TO_CAR, "Connection to car response is missing the "
+ "connection type, treating as disconnected");
- postValue(ConnectionToCar.NOT_CONNECTED);
+ postValue(CarConnection.CONNECTION_TYPE_NOT_CONNECTED);
return;
}
if (!response.moveToNext()) {
- Log.e(TAG_CONNECTION_TO_CAR, "Connection to car response is empty, treating as "
+ Log.e(TAG_CONNECTION_TO_CAR, "Connection to car response is empty, treating as "
+ "disconnected");
- postValue(ConnectionToCar.NOT_CONNECTED);
+ postValue(CarConnection.CONNECTION_TYPE_NOT_CONNECTED);
return;
}
diff --git a/car/app/app/src/main/java/androidx/car/app/connection/ConnectionToCar.java b/car/app/app/src/main/java/androidx/car/app/connection/ConnectionToCar.java
index 1397c0a..4caabbe 100644
--- a/car/app/app/src/main/java/androidx/car/app/connection/ConnectionToCar.java
+++ b/car/app/app/src/main/java/androidx/car/app/connection/ConnectionToCar.java
@@ -33,14 +33,19 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+// TODO(b/169537526): Remove this class
/**
* A class that allows retrieval of information about connection to a car head unit.
+ *
+ * @deprecated use {@link CarConnection} instead.
*/
+@Deprecated
public final class ConnectionToCar {
/**
* Defines current car connection state.
*
- * <p>This is used for communication with the car host.
+ * <p>This is used for communication with the car host's content provider on queries for
+ * connection type.
*/
public static final String CAR_CONNECTION_STATE = "CarConnectionState";
@@ -87,8 +92,8 @@
public ConnectionToCar(@NonNull Context context) {
requireNonNull(context);
mConnectionTypeLiveData = isAutomotiveOS(context)
- ? new AutomotiveConnectionToCarTypeLiveData()
- : new ConnectionToCarTypeLiveData(context);
+ ? new AutomotiveCarConnectionTypeLiveData()
+ : new CarConnectionTypeLiveData(context);
}
/**
diff --git a/car/app/app/src/main/java/androidx/car/app/navigation/model/Step.java b/car/app/app/src/main/java/androidx/car/app/navigation/model/Step.java
index 5604161..5af30b8 100644
--- a/car/app/app/src/main/java/androidx/car/app/navigation/model/Step.java
+++ b/car/app/app/src/main/java/androidx/car/app/navigation/model/Step.java
@@ -173,15 +173,21 @@
private Maneuver mManeuver;
@Nullable
private CarIcon mLanesImage;
+ @Nullable
private CarText mCue;
@Nullable
private CarText mRoad;
/**
+ * Constructs a new builder of {@link Step}.
+ */
+ public Builder() {
+ }
+
+ /**
* Constructs a new builder of {@link Step} with a cue.
*
- * <p>A cue must always be set when the step is created and is used as a fallback when
- * {@link Maneuver} is not set or is unavailable.
+ * <p>A cue can be used as a fallback when {@link Maneuver} is not set or is unavailable.
*
* <p>Some cluster displays do not support UTF-8 encoded characters, in which case
* unsupported characters will not be displayed properly.
@@ -267,8 +273,7 @@
/**
* Sets a text description of this maneuver.
*
- * <p>Must always be set when the step is created and is used as a fallback when {@link
- * Maneuver} is not set or is unavailable.
+ * <p>A cue can be used as a fallback when {@link Maneuver} is not set or is unavailable.
*
* <p>For example "Turn left", "Make a U-Turn", "Sharp Right", or "Take the exit using
* the left lane"
diff --git a/car/app/app/src/test/java/androidx/car/app/connection/AutomotiveConnectionToCarTypeLiveDataTest.java b/car/app/app/src/test/java/androidx/car/app/connection/AutomotiveCarConnectionTypeLiveDataTest.java
similarity index 81%
rename from car/app/app/src/test/java/androidx/car/app/connection/AutomotiveConnectionToCarTypeLiveDataTest.java
rename to car/app/app/src/test/java/androidx/car/app/connection/AutomotiveCarConnectionTypeLiveDataTest.java
index 83a95bb..aeb08a8 100644
--- a/car/app/app/src/test/java/androidx/car/app/connection/AutomotiveConnectionToCarTypeLiveDataTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/connection/AutomotiveCarConnectionTypeLiveDataTest.java
@@ -28,10 +28,10 @@
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.internal.DoNotInstrument;
-/** Tests for {@link AutomotiveConnectionToCarTypeLiveData}. */
+/** Tests for {@link AutomotiveCarConnectionTypeLiveData}. */
@RunWith(RobolectricTestRunner.class)
@DoNotInstrument
-public class AutomotiveConnectionToCarTypeLiveDataTest {
+public class AutomotiveCarConnectionTypeLiveDataTest {
@Mock private Observer<Integer> mMockObserver;
@Before
@@ -41,8 +41,8 @@
@Test
public void observe_returnsNative() {
- new AutomotiveConnectionToCarTypeLiveData().observeForever(mMockObserver);
+ new AutomotiveCarConnectionTypeLiveData().observeForever(mMockObserver);
- verify(mMockObserver).onChanged(ConnectionToCar.NATIVE);
+ verify(mMockObserver).onChanged(CarConnection.CONNECTION_TYPE_NATIVE);
}
}
diff --git a/car/app/app/src/test/java/androidx/car/app/connection/ConnectionToCarTest.java b/car/app/app/src/test/java/androidx/car/app/connection/CarConnectionTest.java
similarity index 80%
rename from car/app/app/src/test/java/androidx/car/app/connection/ConnectionToCarTest.java
rename to car/app/app/src/test/java/androidx/car/app/connection/CarConnectionTest.java
index 18c6222..e7f4a7b 100644
--- a/car/app/app/src/test/java/androidx/car/app/connection/ConnectionToCarTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/connection/CarConnectionTest.java
@@ -31,23 +31,23 @@
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.internal.DoNotInstrument;
-/** Tests for {@link ConnectionToCar}. */
+/** Tests for {@link CarConnection}. */
@RunWith(RobolectricTestRunner.class)
@DoNotInstrument
-public class ConnectionToCarTest {
+public class CarConnectionTest {
private final Context mContext = ApplicationProvider.getApplicationContext();
@Test
public void getType_projection() {
- assertThat(new ConnectionToCar(mContext).getType()).isInstanceOf(
- ConnectionToCarTypeLiveData.class);
+ assertThat(new CarConnection(mContext).getType()).isInstanceOf(
+ CarConnectionTypeLiveData.class);
}
@Test
public void getType_automotive() {
shadowOf(mContext.getPackageManager()).setSystemFeature(FEATURE_AUTOMOTIVE, true);
- assertThat(new ConnectionToCar(mContext).getType()).isInstanceOf(
- AutomotiveConnectionToCarTypeLiveData.class);
+ assertThat(new CarConnection(mContext).getType()).isInstanceOf(
+ AutomotiveCarConnectionTypeLiveData.class);
}
}
diff --git a/car/app/app/src/test/java/androidx/car/app/connection/ConnectionToCarTypeLiveDataTest.java b/car/app/app/src/test/java/androidx/car/app/connection/CarConnectionTypeLiveDataTest.java
similarity index 73%
rename from car/app/app/src/test/java/androidx/car/app/connection/ConnectionToCarTypeLiveDataTest.java
rename to car/app/app/src/test/java/androidx/car/app/connection/CarConnectionTypeLiveDataTest.java
index 976b8ee..ae02a8e 100644
--- a/car/app/app/src/test/java/androidx/car/app/connection/ConnectionToCarTypeLiveDataTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/connection/CarConnectionTypeLiveDataTest.java
@@ -49,16 +49,16 @@
import org.robolectric.shadows.ShadowApplication;
import org.robolectric.shadows.ShadowLooper;
-/** Tests for {@link ConnectionToCarTypeLiveData}. */
+/** Tests for {@link CarConnectionTypeLiveData}. */
@RunWith(RobolectricTestRunner.class)
@DoNotInstrument
-public class ConnectionToCarTypeLiveDataTest {
+public class CarConnectionTypeLiveDataTest {
@Mock
private Observer<Integer> mMockObserver;
private final Application mApplication = ApplicationProvider.getApplicationContext();
private final Context mContext = ApplicationProvider.getApplicationContext();
- private ConnectionToCarTypeLiveData mConnectionToCarTypeLiveData;
+ private CarConnectionTypeLiveData mCarConnectionTypeLiveData;
private TestContentProvider mContentProvider;
@Before
@@ -66,76 +66,76 @@
MockitoAnnotations.initMocks(this);
ProviderInfo info = new ProviderInfo();
- info.authority = ConnectionToCarTypeLiveData.CAR_CONNECTION_AUTHORITY;
+ info.authority = CarConnectionTypeLiveData.CAR_CONNECTION_AUTHORITY;
mContentProvider =
Robolectric.buildContentProvider(TestContentProvider.class).create(info).get();
// Starts with 1 broadcast receiver (for CarPendingIntent)
assertThat(shadowOf(mApplication).getRegisteredReceivers()).hasSize(1);
- mConnectionToCarTypeLiveData = new ConnectionToCarTypeLiveData(mContext);
+ mCarConnectionTypeLiveData = new CarConnectionTypeLiveData(mContext);
}
@Test
public void observe_registersBroadcastReceiver() {
assertThat(shadowOf(mApplication).getRegisteredReceivers()).hasSize(1);
- mConnectionToCarTypeLiveData.observeForever(mMockObserver);
+ mCarConnectionTypeLiveData.observeForever(mMockObserver);
assertThat(shadowOf(mApplication).getRegisteredReceivers()).hasSize(2);
}
@Test
public void getInstance_queriesContentProvider() {
- mConnectionToCarTypeLiveData.observeForever(mMockObserver);
+ mCarConnectionTypeLiveData.observeForever(mMockObserver);
assertThat(mContentProvider.mDidQueryContentProvider).isTrue();
}
@Test
public void contentProviderQuery_wasProjecting() {
mContentProvider.mIsProjecting = true;
- mConnectionToCarTypeLiveData = new ConnectionToCarTypeLiveData(mContext);
+ mCarConnectionTypeLiveData = new CarConnectionTypeLiveData(mContext);
- mConnectionToCarTypeLiveData.observeForever(mMockObserver);
+ mCarConnectionTypeLiveData.observeForever(mMockObserver);
ShadowLooper.runUiThreadTasks();
- verify(mMockObserver).onChanged(ConnectionToCar.PROJECTION);
+ verify(mMockObserver).onChanged(CarConnection.CONNECTION_TYPE_PROJECTION);
}
@Test
public void contentProviderQuery_nullReturn() {
mContentProvider.mReturnNull = true;
- mConnectionToCarTypeLiveData = new ConnectionToCarTypeLiveData(mContext);
+ mCarConnectionTypeLiveData = new CarConnectionTypeLiveData(mContext);
- mConnectionToCarTypeLiveData.observeForever(mMockObserver);
+ mCarConnectionTypeLiveData.observeForever(mMockObserver);
ShadowLooper.runUiThreadTasks();
- verify(mMockObserver).onChanged(ConnectionToCar.NOT_CONNECTED);
+ verify(mMockObserver).onChanged(CarConnection.CONNECTION_TYPE_NOT_CONNECTED);
}
@Test
public void contentProviderQuery_noColumn() {
mContentProvider.mReturnNoColumn = true;
- mConnectionToCarTypeLiveData = new ConnectionToCarTypeLiveData(mContext);
+ mCarConnectionTypeLiveData = new CarConnectionTypeLiveData(mContext);
- mConnectionToCarTypeLiveData.observeForever(mMockObserver);
+ mCarConnectionTypeLiveData.observeForever(mMockObserver);
ShadowLooper.runUiThreadTasks();
- verify(mMockObserver).onChanged(ConnectionToCar.NOT_CONNECTED);
+ verify(mMockObserver).onChanged(CarConnection.CONNECTION_TYPE_NOT_CONNECTED);
}
@Test
public void contentProviderQuery_noRow() {
mContentProvider.mReturnNoRow = true;
- mConnectionToCarTypeLiveData = new ConnectionToCarTypeLiveData(mContext);
+ mCarConnectionTypeLiveData = new CarConnectionTypeLiveData(mContext);
- mConnectionToCarTypeLiveData.observeForever(mMockObserver);
+ mCarConnectionTypeLiveData.observeForever(mMockObserver);
ShadowLooper.runUiThreadTasks();
- verify(mMockObserver).onChanged(ConnectionToCar.NOT_CONNECTED);
+ verify(mMockObserver).onChanged(CarConnection.CONNECTION_TYPE_NOT_CONNECTED);
}
@Test
public void broadcastReceived_queriesAndSetsValue() {
InOrder mocks = inOrder(mMockObserver);
- mConnectionToCarTypeLiveData.observeForever(mMockObserver);
+ mCarConnectionTypeLiveData.observeForever(mMockObserver);
ShadowLooper.runUiThreadTasks();
ShadowApplication.Wrapper receiverWrapper = shadowOf(
@@ -143,11 +143,11 @@
mContentProvider.mIsProjecting = true;
receiverWrapper.broadcastReceiver.onReceive(mContext,
- new Intent(ConnectionToCar.ACTION_CAR_CONNECTION_UPDATED));
+ new Intent(CarConnection.ACTION_CAR_CONNECTION_UPDATED));
ShadowLooper.runUiThreadTasks();
- mocks.verify(mMockObserver).onChanged(ConnectionToCar.NOT_CONNECTED);
- mocks.verify(mMockObserver).onChanged(ConnectionToCar.PROJECTION);
+ mocks.verify(mMockObserver).onChanged(CarConnection.CONNECTION_TYPE_NOT_CONNECTED);
+ mocks.verify(mMockObserver).onChanged(CarConnection.CONNECTION_TYPE_PROJECTION);
mocks.verifyNoMoreInteractions();
}
@@ -155,11 +155,11 @@
public void stopObserving_removedBroadcastReceiver() {
assertThat(shadowOf(mApplication).getRegisteredReceivers()).hasSize(1);
- mConnectionToCarTypeLiveData.observeForever(mMockObserver);
+ mCarConnectionTypeLiveData.observeForever(mMockObserver);
assertThat(shadowOf(mApplication).getRegisteredReceivers()).hasSize(2);
- mConnectionToCarTypeLiveData.removeObserver(mMockObserver);
+ mCarConnectionTypeLiveData.removeObserver(mMockObserver);
assertThat(shadowOf(mApplication).getRegisteredReceivers()).hasSize(1);
}
@@ -182,7 +182,7 @@
@Nullable String selection, @Nullable String[] selectionArgs,
@Nullable String sortOrder) {
mDidQueryContentProvider = true;
- assertThat(projection).asList().containsExactly(ConnectionToCar.CAR_CONNECTION_STATE);
+ assertThat(projection).asList().containsExactly(CarConnection.CAR_CONNECTION_STATE);
if (mReturnNull) {
return null;
@@ -199,9 +199,9 @@
if (mReturnNoColumn) {
return cursor;
}
- rowBuilder.add(ConnectionToCar.CAR_CONNECTION_STATE,
- mIsProjecting ? ConnectionToCar.PROJECTION :
- ConnectionToCar.NOT_CONNECTED);
+ rowBuilder.add(CarConnection.CAR_CONNECTION_STATE,
+ mIsProjecting ? CarConnection.CONNECTION_TYPE_PROJECTION :
+ CarConnection.CONNECTION_TYPE_NOT_CONNECTED);
return cursor;
}
diff --git a/car/app/app/src/test/java/androidx/car/app/navigation/model/StepTest.java b/car/app/app/src/test/java/androidx/car/app/navigation/model/StepTest.java
index 1a46ce9..ea2a1d5 100644
--- a/car/app/app/src/test/java/androidx/car/app/navigation/model/StepTest.java
+++ b/car/app/app/src/test/java/androidx/car/app/navigation/model/StepTest.java
@@ -77,6 +77,43 @@
}
@Test
+ public void createInstance_noCue() {
+ Lane lane = new Lane.Builder().addDirection(
+ LaneDirection.create(SHAPE_SHARP_LEFT, true)).build();
+
+ Step step =
+ new Step.Builder()
+ .addLane(lane)
+ .build();
+
+ assertThat(step.getCue()).isNull();
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void createInstance_cueIsNullInBuilder() {
+ Lane lane = new Lane.Builder().addDirection(
+ LaneDirection.create(SHAPE_SHARP_LEFT, true)).build();
+ CharSequence charSequence = null;
+
+ Step step =
+ new Step.Builder(charSequence)
+ .addLane(lane)
+ .build();
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void createInstance_cueIsNullInSetter() {
+ Lane lane = new Lane.Builder().addDirection(
+ LaneDirection.create(SHAPE_SHARP_LEFT, true)).build();
+
+ Step step =
+ new Step.Builder()
+ .setCue(null)
+ .addLane(lane)
+ .build();
+ }
+
+ @Test
public void createInstance_lanesImage_no_lanes_throws() {
String cue = "Left at State street.";
diff --git a/compose/animation/animation-core-lint/build.gradle b/compose/animation/animation-core-lint/build.gradle
index 9cee8fb..92c1abb 100644
--- a/compose/animation/animation-core-lint/build.gradle
+++ b/compose/animation/animation-core-lint/build.gradle
@@ -28,13 +28,7 @@
BundleInsideHelper.forInsideLintJar(project)
dependencies {
- // compileOnly because we use lintChecks and it doesn't allow other types of deps
- // this ugly hack exists because of b/63873667
- if (rootProject.hasProperty("android.injected.invoked.from.ide")) {
- compileOnly LINT_API_LATEST
- } else {
- compileOnly LINT_API_MIN
- }
+ compileOnly LINT_API_MIN
compileOnly KOTLIN_STDLIB
bundleInside(project(":compose:lint:common"))
diff --git a/compose/animation/animation-lint/build.gradle b/compose/animation/animation-lint/build.gradle
index 36f50b5..441f4b7 100644
--- a/compose/animation/animation-lint/build.gradle
+++ b/compose/animation/animation-lint/build.gradle
@@ -28,13 +28,7 @@
BundleInsideHelper.forInsideLintJar(project)
dependencies {
- // compileOnly because we use lintChecks and it doesn't allow other types of deps
- // this ugly hack exists because of b/63873667
- if (rootProject.hasProperty("android.injected.invoked.from.ide")) {
- compileOnly LINT_API_LATEST
- } else {
- compileOnly LINT_API_MIN
- }
+ compileOnly LINT_API_MIN
compileOnly KOTLIN_STDLIB
bundleInside(project(":compose:lint:common"))
diff --git a/compose/desktop/desktop/build.gradle b/compose/desktop/desktop/build.gradle
index ce53db3..a3ca2ad 100644
--- a/compose/desktop/desktop/build.gradle
+++ b/compose/desktop/desktop/build.gradle
@@ -19,6 +19,7 @@
import androidx.build.LibraryVersions
import androidx.build.RunApiTasks
import androidx.build.SupportConfigKt
+import androidx.build.ComposeJvmTarget
import static androidx.build.AndroidXPlugin.BUILD_ON_SERVER_TASK
import static androidx.build.dependencies.DependenciesKt.*
@@ -34,9 +35,9 @@
}
kotlin {
- jvm() {
- withJava()
- }
+ // Replace with jvm() { withJava() }
+ // Once the build switches to Kotlin 1.5
+ ComposeJvmTarget.withJava(jvm())
sourceSets {
commonMain.dependencies {
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionDelegateTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionDelegateTest.kt
index 474d1215..bf21e40 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionDelegateTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/text/selection/TextFieldSelectionDelegateTest.kt
@@ -344,6 +344,32 @@
assertThat(range.end).isEqualTo(endOffset)
}
+ @Test
+ fun getTextFieldSelection_empty_string() {
+ val text = ""
+ val fontSize = 20.sp
+
+ val textLayoutResult = simpleTextLayout(
+ text = text,
+ fontSize = fontSize,
+ density = defaultDensity
+ )
+
+ // Act.
+ val range = getTextFieldSelection(
+ textLayoutResult = textLayoutResult,
+ rawStartOffset = 0,
+ rawEndOffset = 0,
+ previousSelection = null,
+ isStartHandle = true,
+ adjustment = SelectionAdjustment.WORD
+ )
+
+ // Assert.
+ assertThat(range.start).isEqualTo(0)
+ assertThat(range.end).isEqualTo(0)
+ }
+
@OptIn(InternalFoundationTextApi::class)
private fun simpleTextLayout(
text: String = "",
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/TextSelectionDelegate.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/TextSelectionDelegate.kt
index fcd0212..63aaf64 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/TextSelectionDelegate.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/selection/TextSelectionDelegate.kt
@@ -101,7 +101,8 @@
previousHandlesCrossed: Boolean,
adjustment: SelectionAdjustment
): TextRange {
- if (adjustment == SelectionAdjustment.NONE) {
+ val textLength = textLayoutResult.layoutInput.text.text.length
+ if (adjustment == SelectionAdjustment.NONE || textLength == 0) {
return textRange
}
@@ -124,7 +125,7 @@
textLayoutResult.layoutInput.text.text::getParagraphBoundary
}
- val maxOffset = textLayoutResult.layoutInput.text.text.length - 1
+ val maxOffset = textLength - 1
val startBoundary = boundaryFun(textRange.start.coerceIn(0, maxOffset))
val endBoundary = boundaryFun(textRange.end.coerceIn(0, maxOffset))
diff --git a/compose/material/material-lint/build.gradle b/compose/material/material-lint/build.gradle
index 0ba4d6d..e413333 100644
--- a/compose/material/material-lint/build.gradle
+++ b/compose/material/material-lint/build.gradle
@@ -28,13 +28,7 @@
BundleInsideHelper.forInsideLintJar(project)
dependencies {
- // compileOnly because we use lintChecks and it doesn't allow other types of deps
- // this ugly hack exists because of b/63873667
- if (rootProject.hasProperty("android.injected.invoked.from.ide")) {
- compileOnly LINT_API_LATEST
- } else {
- compileOnly LINT_API_MIN
- }
+ compileOnly LINT_API_MIN
compileOnly KOTLIN_STDLIB
bundleInside(project(":compose:lint:common"))
diff --git a/compose/material/material-ripple/src/androidMain/kotlin/androidx/compose/material/ripple/Ripple.android.kt b/compose/material/material-ripple/src/androidMain/kotlin/androidx/compose/material/ripple/Ripple.android.kt
index d0e9e60..326c385 100644
--- a/compose/material/material-ripple/src/androidMain/kotlin/androidx/compose/material/ripple/Ripple.android.kt
+++ b/compose/material/material-ripple/src/androidMain/kotlin/androidx/compose/material/ripple/Ripple.android.kt
@@ -66,8 +66,10 @@
color: State<Color>,
rippleAlpha: State<RippleAlpha>
): RippleIndicationInstance {
+ val view = findNearestViewGroup()
// Fallback to drawing inside Compose if needed, using the common implementation
- if (!LocalRippleNativeRendering.current) {
+ // TODO(b/188112048): Remove isInEditMode once RenderThread support is fixed in Layoutlib.
+ if (!LocalRippleNativeRendering.current || view.isInEditMode) {
return remember(interactionSource, this) {
CommonRippleIndicationInstance(bounded, radius, color, rippleAlpha)
}
@@ -75,8 +77,6 @@
// Create or get the RippleContainer attached to the nearest root Compose view
- val view = findNearestViewGroup()
-
var rippleContainer: RippleContainer? = null
for (index in 0 until view.childCount) {
diff --git a/compose/runtime/runtime-lint/build.gradle b/compose/runtime/runtime-lint/build.gradle
index e2db424..30b5400 100644
--- a/compose/runtime/runtime-lint/build.gradle
+++ b/compose/runtime/runtime-lint/build.gradle
@@ -28,13 +28,7 @@
BundleInsideHelper.forInsideLintJar(project)
dependencies {
- // compileOnly because we use lintChecks and it doesn't allow other types of deps
- // this ugly hack exists because of b/63873667
- if (rootProject.hasProperty("android.injected.invoked.from.ide")) {
- compileOnly(LINT_API_LATEST)
- } else {
- compileOnly(LINT_API_MIN)
- }
+ compileOnly(LINT_API_MIN)
compileOnly(KOTLIN_STDLIB)
bundleInside(project(":compose:lint:common"))
diff --git a/compose/runtime/runtime-saveable-lint/build.gradle b/compose/runtime/runtime-saveable-lint/build.gradle
index a1a65a0..dca4aab 100644
--- a/compose/runtime/runtime-saveable-lint/build.gradle
+++ b/compose/runtime/runtime-saveable-lint/build.gradle
@@ -28,13 +28,7 @@
BundleInsideHelper.forInsideLintJar(project)
dependencies {
- // compileOnly because we use lintChecks and it doesn't allow other types of deps
- // this ugly hack exists because of b/63873667
- if (rootProject.hasProperty("android.injected.invoked.from.ide")) {
- compileOnly(LINT_API_LATEST)
- } else {
- compileOnly(LINT_API_MIN)
- }
+ compileOnly(LINT_API_MIN)
compileOnly(KOTLIN_STDLIB)
bundleInside(project(":compose:lint:common"))
diff --git a/compose/ui/ui-graphics-lint/build.gradle b/compose/ui/ui-graphics-lint/build.gradle
index 09e0ee9..8d06b87 100644
--- a/compose/ui/ui-graphics-lint/build.gradle
+++ b/compose/ui/ui-graphics-lint/build.gradle
@@ -28,13 +28,7 @@
BundleInsideHelper.forInsideLintJar(project)
dependencies {
- // compileOnly because we use lintChecks and it doesn't allow other types of deps
- // this ugly hack exists because of b/63873667
- if (rootProject.hasProperty("android.injected.invoked.from.ide")) {
- compileOnly(LINT_API_LATEST)
- } else {
- compileOnly(LINT_API_MIN)
- }
+ compileOnly(LINT_API_MIN)
compileOnly(KOTLIN_STDLIB)
bundleInside(project(":compose:lint:common"))
diff --git a/compose/ui/ui-lint/build.gradle b/compose/ui/ui-lint/build.gradle
index fe21cd0..ed38111 100644
--- a/compose/ui/ui-lint/build.gradle
+++ b/compose/ui/ui-lint/build.gradle
@@ -28,13 +28,7 @@
BundleInsideHelper.forInsideLintJar(project)
dependencies {
- // compileOnly because we use lintChecks and it doesn't allow other types of deps
- // this ugly hack exists because of b/63873667
- if (rootProject.hasProperty("android.injected.invoked.from.ide")) {
- compileOnly(LINT_API_LATEST)
- } else {
- compileOnly(LINT_API_MIN)
- }
+ compileOnly(LINT_API_MIN)
compileOnly(KOTLIN_STDLIB)
bundleInside(project(":compose:lint:common"))
diff --git a/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/android/ComposeRootRegistry.android.kt b/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/android/ComposeRootRegistry.android.kt
index 975fb6f..f60a598 100644
--- a/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/android/ComposeRootRegistry.android.kt
+++ b/compose/ui/ui-test-junit4/src/androidMain/kotlin/androidx/compose/ui/test/junit4/android/ComposeRootRegistry.android.kt
@@ -191,7 +191,9 @@
override fun onViewAttachedToWindow(view: View) {
// Only add lifecycle observer. If the root is resumed,
// the lifecycle observer will get notified.
- val lifecycle = checkNotNull(ViewTreeLifecycleOwner.get(view)?.lifecycle)
+ // TODO: This can be missing if the ComposeView is in a ViewOverlay.
+ // If so, we do nothing and bail.
+ val lifecycle = ViewTreeLifecycleOwner.get(view)?.lifecycle ?: return
lifecycle.addObserver(this)
// Setup a lambda to remove the observer when we're detached from the window. When
// that happens, we won't have access to the lifecycle anymore.
diff --git a/compose/ui/ui/api/1.0.0-beta08.txt b/compose/ui/ui/api/1.0.0-beta08.txt
index 1b73557..3c64838 100644
--- a/compose/ui/ui/api/1.0.0-beta08.txt
+++ b/compose/ui/ui/api/1.0.0-beta08.txt
@@ -1924,9 +1924,6 @@
property protected boolean shouldCreateCompositionOnAttachedToWindow;
}
- public final class ComposeView_androidKt {
- }
-
public final class CompositionLocalsKt {
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.AccessibilityManager> getLocalAccessibilityManager();
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.ClipboardManager> getLocalClipboardManager();
diff --git a/compose/ui/ui/api/current.ignore b/compose/ui/ui/api/current.ignore
index 8c133d6..b392991 100644
--- a/compose/ui/ui/api/current.ignore
+++ b/compose/ui/ui/api/current.ignore
@@ -21,6 +21,8 @@
RemovedClass: androidx.compose.ui.focus.FocusStateKt:
Removed class androidx.compose.ui.focus.FocusStateKt
+RemovedClass: androidx.compose.ui.platform.ComposeView_androidKt:
+ Removed class androidx.compose.ui.platform.ComposeView_androidKt
RemovedField: androidx.compose.ui.focus.FocusState#Active:
diff --git a/compose/ui/ui/api/current.txt b/compose/ui/ui/api/current.txt
index 1b73557..3c64838 100644
--- a/compose/ui/ui/api/current.txt
+++ b/compose/ui/ui/api/current.txt
@@ -1924,9 +1924,6 @@
property protected boolean shouldCreateCompositionOnAttachedToWindow;
}
- public final class ComposeView_androidKt {
- }
-
public final class CompositionLocalsKt {
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.AccessibilityManager> getLocalAccessibilityManager();
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.ClipboardManager> getLocalClipboardManager();
diff --git a/compose/ui/ui/api/public_plus_experimental_1.0.0-beta08.txt b/compose/ui/ui/api/public_plus_experimental_1.0.0-beta08.txt
index 286637c..6f6667c 100644
--- a/compose/ui/ui/api/public_plus_experimental_1.0.0-beta08.txt
+++ b/compose/ui/ui/api/public_plus_experimental_1.0.0-beta08.txt
@@ -2037,9 +2037,6 @@
property protected boolean shouldCreateCompositionOnAttachedToWindow;
}
- public final class ComposeView_androidKt {
- }
-
public final class CompositionLocalsKt {
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.AccessibilityManager> getLocalAccessibilityManager();
method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.autofill.Autofill> getLocalAutofill();
diff --git a/compose/ui/ui/api/public_plus_experimental_current.txt b/compose/ui/ui/api/public_plus_experimental_current.txt
index 286637c..6f6667c 100644
--- a/compose/ui/ui/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui/api/public_plus_experimental_current.txt
@@ -2037,9 +2037,6 @@
property protected boolean shouldCreateCompositionOnAttachedToWindow;
}
- public final class ComposeView_androidKt {
- }
-
public final class CompositionLocalsKt {
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.AccessibilityManager> getLocalAccessibilityManager();
method @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.autofill.Autofill> getLocalAutofill();
diff --git a/compose/ui/ui/api/restricted_1.0.0-beta08.txt b/compose/ui/ui/api/restricted_1.0.0-beta08.txt
index 386faca..196241f 100644
--- a/compose/ui/ui/api/restricted_1.0.0-beta08.txt
+++ b/compose/ui/ui/api/restricted_1.0.0-beta08.txt
@@ -1954,9 +1954,6 @@
property protected boolean shouldCreateCompositionOnAttachedToWindow;
}
- public final class ComposeView_androidKt {
- }
-
public final class CompositionLocalsKt {
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.AccessibilityManager> getLocalAccessibilityManager();
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.ClipboardManager> getLocalClipboardManager();
diff --git a/compose/ui/ui/api/restricted_current.ignore b/compose/ui/ui/api/restricted_current.ignore
index 8c133d6..b392991 100644
--- a/compose/ui/ui/api/restricted_current.ignore
+++ b/compose/ui/ui/api/restricted_current.ignore
@@ -21,6 +21,8 @@
RemovedClass: androidx.compose.ui.focus.FocusStateKt:
Removed class androidx.compose.ui.focus.FocusStateKt
+RemovedClass: androidx.compose.ui.platform.ComposeView_androidKt:
+ Removed class androidx.compose.ui.platform.ComposeView_androidKt
RemovedField: androidx.compose.ui.focus.FocusState#Active:
diff --git a/compose/ui/ui/api/restricted_current.txt b/compose/ui/ui/api/restricted_current.txt
index 386faca..196241f 100644
--- a/compose/ui/ui/api/restricted_current.txt
+++ b/compose/ui/ui/api/restricted_current.txt
@@ -1954,9 +1954,6 @@
property protected boolean shouldCreateCompositionOnAttachedToWindow;
}
- public final class ComposeView_androidKt {
- }
-
public final class CompositionLocalsKt {
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.AccessibilityManager> getLocalAccessibilityManager();
method public static androidx.compose.runtime.ProvidableCompositionLocal<androidx.compose.ui.platform.ClipboardManager> getLocalClipboardManager();
diff --git a/compose/ui/ui/src/androidAndroidTest/AndroidManifest.xml b/compose/ui/ui/src/androidAndroidTest/AndroidManifest.xml
index b6b7f01..5a120c7 100644
--- a/compose/ui/ui/src/androidAndroidTest/AndroidManifest.xml
+++ b/compose/ui/ui/src/androidAndroidTest/AndroidManifest.xml
@@ -25,6 +25,7 @@
android:name="androidx.compose.ui.input.pointer.AndroidPointerInputTestActivity" />
<activity android:name="androidx.appcompat.app.AppCompatActivity"
android:theme="@style/Theme.AppCompat"/>
+ <activity android:name="androidx.activity.ComponentActivity" />
<activity android:name="androidx.fragment.app.FragmentActivity"/>
<activity android:name="androidx.compose.ui.window.ActivityWithFlagSecure"/>
<activity android:name="androidx.compose.ui.RecyclerViewActivity" />
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt
index 81fa463..de0a2d9 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt
@@ -1305,12 +1305,13 @@
++alignmentLinesCalculations
0
}
+ var linePosition by mutableStateOf(10)
activityTestRule.runOnUiThreadIR {
activity.setContent {
val innerChild = @Composable {
offset.value // Artificial remeasure.
Layout(content = {}) { _, _ ->
- layout(0, 0, mapOf(TestLine to 10)) { }
+ layout(0, 0, mapOf(TestLine to linePosition)) { }
}
}
val child = @Composable {
@@ -1340,12 +1341,12 @@
assertEquals(1, alignmentLinesCalculations)
layoutLatch = CountDownLatch(1)
- activityTestRule.runOnUiThreadIR { offset.value = 20 }
+ activityTestRule.runOnUiThreadIR { offset.value = 20; linePosition = 20 }
assertTrue(layoutLatch.await(1, TimeUnit.SECONDS))
assertEquals(1, alignmentLinesCalculations)
layoutLatch = CountDownLatch(1)
- activityTestRule.runOnUiThreadIR { offset.value = 10 }
+ activityTestRule.runOnUiThreadIR { offset.value = 10; linePosition = 30 }
assertTrue(layoutLatch.await(1, TimeUnit.SECONDS))
assertEquals(2, alignmentLinesCalculations)
}
@@ -1473,26 +1474,44 @@
}
}
assertTrue(layoutLatch.await(1, TimeUnit.SECONDS))
- // Two layouts as the alignment line was only queried after the child was placed.
- assertEquals(2, childLayouts)
+ assertEquals(1, childLayouts)
}
@Test
fun testAlignmentLines_whenQueriedAfterPlacing_haveCorrectNumberOfLayouts() {
- val TestLine = VerticalAlignmentLine(::min)
- var layoutLatch = CountDownLatch(1)
var childLayouts = 0
+ var childAlignmentLinesCalculations = 0
+ val TestLine = VerticalAlignmentLine { v1, _ ->
+ ++childAlignmentLinesCalculations
+ v1
+ }
val offset = mutableStateOf(10)
+ var linePositionState by mutableStateOf(10)
+ var linePosition = 10
+ fun changeLinePosition() {
+ linePosition = 30 - linePosition
+ linePositionState = 30 - linePositionState
+ }
+ var layoutLatch = CountDownLatch(1)
activityTestRule.runOnUiThreadIR {
activity.setContent {
- val child = @Composable {
+ val childChild = @Composable {
Layout(content = {}) { _, constraints ->
layout(
constraints.minWidth,
constraints.minHeight,
- mapOf(TestLine to 10)
+ mapOf(TestLine to linePositionState)
) {
offset.value // To ensure relayout.
+ }
+ }
+ }
+ val child = @Composable {
+ Layout(content = { childChild(); childChild() }) { measurables, constraints ->
+ val placeables = measurables.map { it.measure(constraints) }
+ layout(constraints.minWidth, constraints.minHeight) {
+ offset.value // To ensure relayout.
+ placeables.forEach { it.place(0, 0) }
++childLayouts
}
}
@@ -1503,9 +1522,9 @@
}) { measurables, constraints ->
val placeable = measurables[0].measure(constraints)
layout(placeable.width, placeable.height) {
- if (offset.value > 15) assertEquals(10, placeable[TestLine])
+ if (offset.value > 15) assertEquals(linePosition, placeable[TestLine])
placeable.place(0, 0)
- if (offset.value > 5) assertEquals(10, placeable[TestLine])
+ if (offset.value > 5) assertEquals(linePosition, placeable[TestLine])
}
}
}
@@ -1522,37 +1541,42 @@
}
}
assertTrue(layoutLatch.await(1, TimeUnit.SECONDS))
- // Two layouts as the alignment line was only queried after the child was placed.
- assertEquals(2, childLayouts)
-
- layoutLatch = CountDownLatch(1)
- activityTestRule.runOnUiThreadIR { offset.value = 12 }
- assertTrue(layoutLatch.await(5, TimeUnit.SECONDS))
- // Just one more layout as the alignment lines were speculatively calculated this time.
- assertEquals(3, childLayouts)
-
- layoutLatch = CountDownLatch(1)
- activityTestRule.runOnUiThreadIR { offset.value = 17 }
- assertTrue(layoutLatch.await(1, TimeUnit.SECONDS))
- // One layout as the alignment lines are queried before.
- assertEquals(4, childLayouts)
-
- layoutLatch = CountDownLatch(1)
- activityTestRule.runOnUiThreadIR { offset.value = 12 }
- assertTrue(layoutLatch.await(1, TimeUnit.SECONDS))
- // One layout as the alignment lines are still calculated speculatively.
- assertEquals(5, childLayouts)
+ assertEquals(2, childLayouts + childAlignmentLinesCalculations)
layoutLatch = CountDownLatch(1)
activityTestRule.runOnUiThreadIR { offset.value = 1 }
assertTrue(layoutLatch.await(1, TimeUnit.SECONDS))
- assertEquals(6, childLayouts)
+ assertEquals(3, childLayouts + childAlignmentLinesCalculations)
+
layoutLatch = CountDownLatch(1)
- activityTestRule.runOnUiThreadIR { offset.value = 10 }
+ activityTestRule.runOnUiThreadIR { offset.value = 10; changeLinePosition() }
assertTrue(layoutLatch.await(1, TimeUnit.SECONDS))
- // Two layouts again, since alignment lines were not queried during last layout,
- // so we did not calculate them speculatively anymore.
- assertEquals(8, childLayouts)
+ assertEquals(5, childLayouts + childAlignmentLinesCalculations)
+
+ layoutLatch = CountDownLatch(1)
+ activityTestRule.runOnUiThreadIR { offset.value = 12; changeLinePosition() }
+ assertTrue(layoutLatch.await(1, TimeUnit.SECONDS))
+ assertEquals(7, childLayouts + childAlignmentLinesCalculations)
+
+ layoutLatch = CountDownLatch(1)
+ activityTestRule.runOnUiThreadIR { offset.value = 17; changeLinePosition() }
+ assertTrue(layoutLatch.await(1, TimeUnit.SECONDS))
+ assertEquals(9, childLayouts + childAlignmentLinesCalculations)
+
+ layoutLatch = CountDownLatch(1)
+ activityTestRule.runOnUiThreadIR { offset.value = 12; changeLinePosition() }
+ assertTrue(layoutLatch.await(1, TimeUnit.SECONDS))
+ assertEquals(11, childLayouts + childAlignmentLinesCalculations)
+
+ layoutLatch = CountDownLatch(1)
+ activityTestRule.runOnUiThreadIR { offset.value = 1; changeLinePosition() }
+ assertTrue(layoutLatch.await(1, TimeUnit.SECONDS))
+ assertEquals(13, childLayouts + childAlignmentLinesCalculations)
+
+ layoutLatch = CountDownLatch(1)
+ activityTestRule.runOnUiThreadIR { offset.value = 10; changeLinePosition() }
+ assertTrue(layoutLatch.await(1, TimeUnit.SECONDS))
+ assertEquals(15, childLayouts + childAlignmentLinesCalculations)
}
@Test
@@ -1632,7 +1656,7 @@
testHorizontalLine: HorizontalAlignmentLine,
assertLines: Modifier.(Int, Int) -> Modifier
) {
- val layoutLatch = CountDownLatch(7)
+ val layoutLatch = CountDownLatch(1)
activityTestRule.runOnUiThreadIR {
activity.setContent {
val layout = @Composable { modifier: Modifier ->
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/EditorInfoTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/EditorInfoTest.kt
new file mode 100644
index 0000000..5894e49
--- /dev/null
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/EditorInfoTest.kt
@@ -0,0 +1,523 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.input
+
+import android.text.InputType
+import android.view.inputmethod.EditorInfo
+import androidx.compose.ui.text.TextRange
+import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.text.input.ImeOptions
+import androidx.compose.ui.text.input.KeyboardCapitalization
+import androidx.compose.ui.text.input.KeyboardType
+import androidx.compose.ui.text.input.TextFieldValue
+import androidx.compose.ui.text.input.update
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class EditorInfoTest {
+
+ @Test
+ fun test_fill_editor_info_text() {
+ val info = EditorInfo()
+ info.update(
+ ImeOptions(
+ keyboardType = KeyboardType.Text,
+ imeAction = ImeAction.Default
+ )
+ )
+
+ assertThat((InputType.TYPE_CLASS_TEXT and info.inputType) != 0).isTrue()
+ assertThat(
+ (EditorInfo.IME_MASK_ACTION and info.imeOptions)
+ == EditorInfo.IME_ACTION_UNSPECIFIED
+ ).isTrue()
+ }
+
+ @Test
+ fun test_fill_editor_info_ascii() {
+ val info = EditorInfo()
+ info.update(
+ ImeOptions(
+ keyboardType = KeyboardType.Ascii,
+ imeAction = ImeAction.Default
+ )
+ )
+
+ assertThat((InputType.TYPE_CLASS_TEXT and info.inputType) != 0).isTrue()
+ assertThat((EditorInfo.IME_FLAG_FORCE_ASCII and info.imeOptions) != 0).isTrue()
+ assertThat(
+ (EditorInfo.IME_MASK_ACTION and info.imeOptions)
+ == EditorInfo.IME_ACTION_UNSPECIFIED
+ ).isTrue()
+ }
+
+ @Test
+ fun test_fill_editor_info_number() {
+ val info = EditorInfo()
+ info.update(
+ ImeOptions(
+ keyboardType = KeyboardType.Number,
+ imeAction = ImeAction.Default
+ )
+ )
+
+ assertThat((InputType.TYPE_CLASS_NUMBER and info.inputType) != 0).isTrue()
+ assertThat(
+ (EditorInfo.IME_MASK_ACTION and info.imeOptions)
+ == EditorInfo.IME_ACTION_UNSPECIFIED
+ ).isTrue()
+ }
+
+ @Test
+ fun test_fill_editor_info_phone() {
+ val info = EditorInfo()
+ info.update(
+ ImeOptions(
+ keyboardType = KeyboardType.Phone,
+ imeAction = ImeAction.Default
+ )
+ )
+
+ assertThat((InputType.TYPE_CLASS_PHONE and info.inputType) != 0).isTrue()
+ assertThat(
+ (EditorInfo.IME_MASK_ACTION and info.imeOptions)
+ == EditorInfo.IME_ACTION_UNSPECIFIED
+ ).isTrue()
+ }
+
+ @Test
+ fun test_fill_editor_info_uri() {
+ val info = EditorInfo()
+ info.update(
+ ImeOptions(
+ keyboardType = KeyboardType.Uri,
+ imeAction = ImeAction.Default
+ )
+ )
+
+ assertThat((InputType.TYPE_CLASS_TEXT and info.inputType) != 0).isTrue()
+ assertThat((InputType.TYPE_TEXT_VARIATION_URI and info.inputType) != 0).isTrue()
+ assertThat(
+ (EditorInfo.IME_MASK_ACTION and info.imeOptions)
+ == EditorInfo.IME_ACTION_UNSPECIFIED
+ ).isTrue()
+ }
+
+ @Test
+ fun test_fill_editor_info_email() {
+ val info = EditorInfo()
+ info.update(
+ ImeOptions(
+ keyboardType = KeyboardType.Email,
+ imeAction = ImeAction.Default
+ )
+ )
+
+ assertThat((InputType.TYPE_CLASS_TEXT and info.inputType) != 0).isTrue()
+ assertThat((InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS and info.inputType) != 0)
+ .isTrue()
+ assertThat(
+ (EditorInfo.IME_MASK_ACTION and info.imeOptions)
+ == EditorInfo.IME_ACTION_UNSPECIFIED
+ ).isTrue()
+ }
+
+ @Test
+ fun test_fill_editor_info_password() {
+ val info = EditorInfo()
+ info.update(
+ ImeOptions(
+ keyboardType = KeyboardType.Password,
+ imeAction = ImeAction.Default
+ )
+ )
+
+ assertThat((InputType.TYPE_CLASS_TEXT and info.inputType) != 0).isTrue()
+ assertThat((InputType.TYPE_TEXT_VARIATION_PASSWORD and info.inputType) != 0).isTrue()
+ assertThat(
+ (EditorInfo.IME_MASK_ACTION and info.imeOptions)
+ == EditorInfo.IME_ACTION_UNSPECIFIED
+ ).isTrue()
+ }
+
+ @Test
+ fun test_fill_editor_info_number_password() {
+ val info = EditorInfo()
+ info.update(
+ ImeOptions(
+ keyboardType = KeyboardType.NumberPassword,
+ imeAction = ImeAction.Default
+ )
+ )
+
+ assertThat((InputType.TYPE_CLASS_NUMBER and info.inputType) != 0).isTrue()
+ assertThat((InputType.TYPE_NUMBER_VARIATION_PASSWORD and info.inputType) != 0).isTrue()
+ assertThat(
+ (EditorInfo.IME_MASK_ACTION and info.imeOptions)
+ == EditorInfo.IME_ACTION_UNSPECIFIED
+ ).isTrue()
+ }
+
+ @Test
+ fun test_fill_editor_info_action_none() {
+ val info = EditorInfo()
+ info.update(
+ ImeOptions(
+ keyboardType = KeyboardType.Ascii,
+ imeAction = ImeAction.None
+ )
+ )
+
+ assertThat((InputType.TYPE_CLASS_TEXT and info.inputType) != 0).isTrue()
+ assertThat((EditorInfo.IME_FLAG_FORCE_ASCII and info.imeOptions) != 0).isTrue()
+ assertThat(
+ (EditorInfo.IME_MASK_ACTION and info.imeOptions)
+ == EditorInfo.IME_ACTION_NONE
+ ).isTrue()
+ }
+
+ @Test
+ fun test_fill_editor_info_action_go() {
+ val info = EditorInfo()
+ info.update(
+ ImeOptions(
+ keyboardType = KeyboardType.Ascii,
+ imeAction = ImeAction.Go
+ )
+ )
+
+ assertThat((InputType.TYPE_CLASS_TEXT and info.inputType) != 0).isTrue()
+ assertThat((EditorInfo.IME_FLAG_FORCE_ASCII and info.imeOptions) != 0).isTrue()
+ assertThat(
+ (EditorInfo.IME_MASK_ACTION and info.imeOptions)
+ == EditorInfo.IME_ACTION_GO
+ ).isTrue()
+ }
+
+ @Test
+ fun test_fill_editor_info_action_next() {
+ val info = EditorInfo()
+ info.update(
+ ImeOptions(
+ keyboardType = KeyboardType.Ascii,
+ imeAction = ImeAction.Next
+ )
+ )
+
+ assertThat((InputType.TYPE_CLASS_TEXT and info.inputType) != 0).isTrue()
+ assertThat((EditorInfo.IME_FLAG_FORCE_ASCII and info.imeOptions) != 0).isTrue()
+ assertThat(
+ (EditorInfo.IME_MASK_ACTION and info.imeOptions)
+ == EditorInfo.IME_ACTION_NEXT
+ ).isTrue()
+ }
+
+ @Test
+ fun test_fill_editor_info_action_previous() {
+ val info = EditorInfo()
+ info.update(
+ ImeOptions(
+ keyboardType = KeyboardType.Ascii,
+ imeAction = ImeAction.Previous
+ )
+ )
+
+ assertThat((InputType.TYPE_CLASS_TEXT and info.inputType) != 0).isTrue()
+ assertThat((EditorInfo.IME_FLAG_FORCE_ASCII and info.imeOptions) != 0).isTrue()
+ assertThat(
+ (EditorInfo.IME_MASK_ACTION and info.imeOptions)
+ == EditorInfo.IME_ACTION_PREVIOUS
+ ).isTrue()
+ }
+
+ @Test
+ fun test_fill_editor_info_action_search() {
+ val info = EditorInfo()
+ info.update(
+ ImeOptions(
+ keyboardType = KeyboardType.Ascii,
+ imeAction = ImeAction.Search,
+ )
+ )
+
+ assertThat((InputType.TYPE_CLASS_TEXT and info.inputType) != 0).isTrue()
+ assertThat((EditorInfo.IME_FLAG_FORCE_ASCII and info.imeOptions) != 0).isTrue()
+ assertThat(
+ (EditorInfo.IME_MASK_ACTION and info.imeOptions)
+ == EditorInfo.IME_ACTION_SEARCH
+ ).isTrue()
+ }
+
+ @Test
+ fun test_fill_editor_info_action_send() {
+ val info = EditorInfo()
+ info.update(
+ ImeOptions(
+ keyboardType = KeyboardType.Ascii,
+ imeAction = ImeAction.Send
+ )
+ )
+
+ assertThat((InputType.TYPE_CLASS_TEXT and info.inputType) != 0).isTrue()
+ assertThat((EditorInfo.IME_FLAG_FORCE_ASCII and info.imeOptions) != 0).isTrue()
+ assertThat(
+ (EditorInfo.IME_MASK_ACTION and info.imeOptions)
+ == EditorInfo.IME_ACTION_SEND
+ ).isTrue()
+ }
+
+ @Test
+ fun test_fill_editor_info_action_done() {
+ val info = EditorInfo()
+ info.update(
+ ImeOptions(
+ keyboardType = KeyboardType.Ascii,
+ imeAction = ImeAction.Done
+ )
+ )
+
+ assertThat((InputType.TYPE_CLASS_TEXT and info.inputType) != 0).isTrue()
+ assertThat((EditorInfo.IME_FLAG_FORCE_ASCII and info.imeOptions) != 0).isTrue()
+ assertThat(
+ (EditorInfo.IME_MASK_ACTION and info.imeOptions)
+ == EditorInfo.IME_ACTION_DONE
+ ).isTrue()
+ }
+
+ @Test
+ fun test_fill_editor_info_multi_line() {
+ val info = EditorInfo()
+ info.update(
+ ImeOptions(
+ singleLine = false,
+ keyboardType = KeyboardType.Ascii,
+ imeAction = ImeAction.Done
+ )
+ )
+
+ assertThat((InputType.TYPE_TEXT_FLAG_MULTI_LINE and info.inputType) == 0).isFalse()
+ assertThat((EditorInfo.IME_FLAG_NO_ENTER_ACTION and info.imeOptions) == 0).isTrue()
+ }
+
+ @Test
+ fun test_fill_editor_info_multi_line_with_default_action() {
+ val info = EditorInfo()
+ info.update(
+ ImeOptions(
+ singleLine = false,
+ keyboardType = KeyboardType.Text,
+ imeAction = ImeAction.Default
+ )
+ )
+
+ assertThat((InputType.TYPE_TEXT_FLAG_MULTI_LINE and info.inputType) == 0).isFalse()
+ assertThat((EditorInfo.IME_FLAG_NO_ENTER_ACTION and info.imeOptions) == 0).isFalse()
+ }
+
+ @Test
+ fun test_fill_editor_info_single_line() {
+ val info = EditorInfo()
+ info.update(
+ ImeOptions(
+ singleLine = true,
+ keyboardType = KeyboardType.Ascii,
+ imeAction = ImeAction.Done
+ )
+ )
+
+ assertThat((InputType.TYPE_TEXT_FLAG_MULTI_LINE and info.inputType) == 0).isTrue()
+ assertThat((EditorInfo.IME_FLAG_NO_ENTER_ACTION and info.imeOptions) == 0).isTrue()
+ }
+
+ @Test
+ fun test_fill_editor_info_single_line_changes_ime_from_unspecified_to_done() {
+ val info = EditorInfo()
+ info.update(
+ ImeOptions(
+ singleLine = true,
+ keyboardType = KeyboardType.Text,
+ imeAction = ImeAction.Default
+ )
+ )
+
+ assertThat((EditorInfo.IME_ACTION_DONE and info.imeOptions) == 0).isFalse()
+ assertThat((EditorInfo.IME_ACTION_UNSPECIFIED and info.imeOptions) == 0).isTrue()
+ }
+
+ @Test
+ fun test_fill_editor_info_multi_line_not_set_when_input_type_is_not_text() {
+ val info = EditorInfo()
+ info.update(
+ ImeOptions(
+ singleLine = false,
+ keyboardType = KeyboardType.Number,
+ imeAction = ImeAction.Done
+ )
+ )
+
+ assertThat((InputType.TYPE_TEXT_FLAG_MULTI_LINE and info.inputType) == 0).isTrue()
+ assertThat((EditorInfo.IME_FLAG_NO_ENTER_ACTION and info.imeOptions) == 0).isTrue()
+ }
+
+ @Test
+ fun test_fill_editor_info_capitalization_none() {
+ val info = EditorInfo()
+ info.update(
+ ImeOptions(
+ capitalization = KeyboardCapitalization.None,
+ keyboardType = KeyboardType.Ascii,
+ imeAction = ImeAction.Done
+ )
+ )
+
+ assertThat((InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS and info.inputType) == 0).isTrue()
+ assertThat((InputType.TYPE_TEXT_FLAG_CAP_WORDS and info.inputType) == 0).isTrue()
+ assertThat((InputType.TYPE_TEXT_FLAG_CAP_SENTENCES and info.inputType) == 0).isTrue()
+ }
+
+ @Test
+ fun test_fill_editor_info_capitalization_characters() {
+ val info = EditorInfo()
+ info.update(
+ ImeOptions(
+ capitalization = KeyboardCapitalization.Characters,
+ keyboardType = KeyboardType.Ascii,
+ imeAction = ImeAction.Done
+ )
+ )
+
+ assertThat((InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS and info.inputType) == 0).isFalse()
+ assertThat((InputType.TYPE_TEXT_FLAG_CAP_WORDS and info.inputType) == 0).isTrue()
+ assertThat((InputType.TYPE_TEXT_FLAG_CAP_SENTENCES and info.inputType) == 0).isTrue()
+ }
+
+ @Test
+ fun test_fill_editor_info_capitalization_words() {
+ val info = EditorInfo()
+ info.update(
+ ImeOptions(
+ capitalization = KeyboardCapitalization.Words,
+ keyboardType = KeyboardType.Ascii,
+ imeAction = ImeAction.Done
+ )
+ )
+
+ assertThat((InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS and info.inputType) == 0).isTrue()
+ assertThat((InputType.TYPE_TEXT_FLAG_CAP_WORDS and info.inputType) == 0).isFalse()
+ assertThat((InputType.TYPE_TEXT_FLAG_CAP_SENTENCES and info.inputType) == 0).isTrue()
+ }
+
+ @Test
+ fun test_fill_editor_info_capitalization_sentences() {
+ val info = EditorInfo()
+ info.update(
+ ImeOptions(
+ capitalization = KeyboardCapitalization.Sentences,
+ keyboardType = KeyboardType.Ascii,
+ imeAction = ImeAction.Done,
+ )
+ )
+
+ assertThat((InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS and info.inputType) == 0).isTrue()
+ assertThat((InputType.TYPE_TEXT_FLAG_CAP_WORDS and info.inputType) == 0).isTrue()
+ assertThat((InputType.TYPE_TEXT_FLAG_CAP_SENTENCES and info.inputType) == 0).isFalse()
+ }
+
+ @Test
+ fun test_fill_editor_info_capitalization_not_added_when_input_type_is_not_text() {
+ val info = EditorInfo()
+ info.update(
+ ImeOptions(
+ capitalization = KeyboardCapitalization.Sentences,
+ keyboardType = KeyboardType.Number,
+ imeAction = ImeAction.Done,
+ )
+ )
+
+ assertThat((InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS and info.inputType) == 0).isTrue()
+ assertThat((InputType.TYPE_TEXT_FLAG_CAP_WORDS and info.inputType) == 0).isTrue()
+ assertThat((InputType.TYPE_TEXT_FLAG_CAP_SENTENCES and info.inputType) == 0).isTrue()
+ }
+
+ @Test
+ fun test_fill_editor_info_auto_correct_on() {
+ val info = EditorInfo()
+ info.update(
+ ImeOptions(
+ autoCorrect = true,
+ keyboardType = KeyboardType.Ascii,
+ imeAction = ImeAction.Done
+ )
+ )
+
+ assertThat((InputType.TYPE_TEXT_FLAG_AUTO_CORRECT and info.inputType) == 0).isFalse()
+ }
+
+ @Test
+ fun test_fill_editor_info_auto_correct_off() {
+ val info = EditorInfo()
+ info.update(
+ ImeOptions(
+ autoCorrect = false,
+ keyboardType = KeyboardType.Ascii,
+ imeAction = ImeAction.Done
+ )
+ )
+
+ assertThat((InputType.TYPE_TEXT_FLAG_AUTO_CORRECT and info.inputType) == 0).isTrue()
+ }
+
+ @Test
+ fun autocorrect_not_added_when_input_type_is_not_text() {
+ val info = EditorInfo()
+ info.update(
+ ImeOptions(
+ autoCorrect = true,
+ keyboardType = KeyboardType.Number,
+ imeAction = ImeAction.Done
+ )
+ )
+
+ assertThat((InputType.TYPE_TEXT_FLAG_AUTO_CORRECT and info.inputType) == 0).isTrue()
+ }
+
+ @Test
+ fun initial_default_selection_info_is_set() {
+ val info = EditorInfo()
+ info.update(ImeOptions.Default)
+
+ assertThat(info.initialSelStart).isEqualTo(0)
+ assertThat(info.initialSelEnd).isEqualTo(0)
+ }
+
+ @Test
+ fun initial_selection_info_is_set() {
+ val selection = TextRange(1, 2)
+ val info = EditorInfo()
+ info.update(ImeOptions.Default, TextFieldValue("abc", selection))
+
+ assertThat(info.initialSelStart).isEqualTo(selection.start)
+ assertThat(info.initialSelEnd).isEqualTo(selection.end)
+ }
+
+ private fun EditorInfo.update(imeOptions: ImeOptions) {
+ this.update(imeOptions, TextFieldValue())
+ }
+}
\ No newline at end of file
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/input/TextInputServiceAndroidOnStateUpdateTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/TextInputServiceAndroidOnStateUpdateTest.kt
similarity index 74%
rename from compose/ui/ui/src/test/kotlin/androidx/compose/ui/input/TextInputServiceAndroidOnStateUpdateTest.kt
rename to compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/TextInputServiceAndroidOnStateUpdateTest.kt
index a43182af..25ba030 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/input/TextInputServiceAndroidOnStateUpdateTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/input/TextInputServiceAndroidOnStateUpdateTest.kt
@@ -16,43 +16,42 @@
package androidx.compose.ui.input
-import android.content.Context
import android.view.View
import android.view.inputmethod.EditorInfo
-import android.view.inputmethod.InputMethodManager
import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.input.ImeOptions
+import androidx.compose.ui.text.input.InputMethodManager
import androidx.compose.ui.text.input.RecordingInputConnection
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.input.TextInputServiceAndroid
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
import com.google.common.truth.Truth.assertThat
import com.nhaarman.mockitokotlin2.any
-import com.nhaarman.mockitokotlin2.eq
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.never
import com.nhaarman.mockitokotlin2.times
import com.nhaarman.mockitokotlin2.verify
-import com.nhaarman.mockitokotlin2.whenever
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-@RunWith(JUnit4::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
class TextInputServiceAndroidOnStateUpdateTest {
private lateinit var textInputService: TextInputServiceAndroid
- private lateinit var imm: InputMethodManager
+ private lateinit var inputMethodManager: InputMethodManager
private lateinit var inputConnection: RecordingInputConnection
@Before
fun setup() {
- imm = mock()
- val view: View = mock()
- val context: Context = mock()
- whenever(context.getSystemService(eq(Context.INPUT_METHOD_SERVICE))).thenReturn(imm)
- whenever(view.context).thenReturn(context)
- textInputService = TextInputServiceAndroid(view)
+ val view = View(InstrumentationRegistry.getInstrumentation().context)
+ inputMethodManager = mock()
+ textInputService = TextInputServiceAndroid(view, mock())
+
+ textInputService = TextInputServiceAndroid(view, inputMethodManager)
textInputService.startInput(
value = TextFieldValue(""),
imeOptions = ImeOptions.Default,
@@ -71,8 +70,8 @@
newValue = newValue
)
- verify(imm, times(1)).restartInput(any())
- verify(imm, never()).updateSelection(any(), any(), any(), any(), any())
+ verify(inputMethodManager, times(1)).restartInput(any())
+ verify(inputMethodManager, never()).updateSelection(any(), any(), any(), any(), any())
assertThat(inputConnection.mTextFieldValue).isEqualTo(newValue)
assertThat(textInputService.state).isEqualTo(newValue)
@@ -86,8 +85,8 @@
newValue = newValue
)
- verify(imm, times(1)).restartInput(any())
- verify(imm, never()).updateSelection(any(), any(), any(), any(), any())
+ verify(inputMethodManager, times(1)).restartInput(any())
+ verify(inputMethodManager, never()).updateSelection(any(), any(), any(), any(), any())
assertThat(inputConnection.mTextFieldValue).isEqualTo(newValue)
assertThat(textInputService.state).isEqualTo(newValue)
@@ -101,8 +100,8 @@
newValue = newValue
)
- verify(imm, never()).restartInput(any())
- verify(imm, times(1)).updateSelection(any(), any(), any(), any(), any())
+ verify(inputMethodManager, never()).restartInput(any())
+ verify(inputMethodManager, times(1)).updateSelection(any(), any(), any(), any(), any())
assertThat(inputConnection.mTextFieldValue).isEqualTo(newValue)
assertThat(textInputService.state).isEqualTo(newValue)
@@ -116,8 +115,8 @@
newValue = newValue
)
- verify(imm, never()).restartInput(any())
- verify(imm, times(1)).updateSelection(any(), any(), any(), any(), any())
+ verify(inputMethodManager, never()).restartInput(any())
+ verify(inputMethodManager, times(1)).updateSelection(any(), any(), any(), any(), any())
assertThat(inputConnection.mTextFieldValue).isEqualTo(newValue)
assertThat(textInputService.state).isEqualTo(newValue)
@@ -131,8 +130,8 @@
newValue = value
)
- verify(imm, never()).restartInput(any())
- verify(imm, never()).updateSelection(any(), any(), any(), any(), any())
+ verify(inputMethodManager, never()).restartInput(any())
+ verify(inputMethodManager, never()).updateSelection(any(), any(), any(), any(), any())
assertThat(inputConnection.mTextFieldValue).isEqualTo(value)
assertThat(textInputService.state).isEqualTo(value)
@@ -146,8 +145,8 @@
newValue = value
)
- verify(imm, never()).restartInput(any())
- verify(imm, never()).updateSelection(any(), any(), any(), any(), any())
+ verify(inputMethodManager, never()).restartInput(any())
+ verify(inputMethodManager, never()).updateSelection(any(), any(), any(), any(), any())
// recreate the connection
inputConnection = textInputService.createInputConnection(EditorInfo())
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/AlignmentLineTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/AlignmentLineTest.kt
index 81b70cd..9c76455 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/AlignmentLineTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/AlignmentLineTest.kt
@@ -18,11 +18,18 @@
import androidx.activity.ComponentActivity
import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.requiredSize
+import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateMapOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.IntOffset
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import org.junit.Assert.assertEquals
@@ -126,4 +133,1166 @@
}
rule.waitForIdle()
}
+
+ @Test
+ fun alignmentLinesArePropagated_whenSuppliedViaModifier() {
+ val size = 50
+ val sizeDp = with(rule.density) { size.toDp() }
+ val linePosition = 25
+ val hLine = HorizontalAlignmentLine(::min)
+ val vLine = VerticalAlignmentLine(::min)
+ rule.setContent {
+ val content = @Composable {
+ Box(Modifier.size(sizeDp)) {
+ Box(
+ Modifier.supplyAlignmentLines {
+ mapOf(
+ hLine to linePosition,
+ vLine to linePosition
+ )
+ }.size(sizeDp)
+ )
+ }
+ }
+
+ Layout(content) { measurables, constraints ->
+ val placeable = measurables.first().measure(constraints)
+ assertEquals(linePosition, placeable[hLine])
+ assertEquals(linePosition, placeable[vLine])
+ layout(0, 0) {}
+ }
+ }
+ }
+
+ @Test
+ fun alignmentLinesArePropagated_whenSuppliedViaModifier_withCorrectPosition() {
+ val size = 50
+ val sizeDp = with(rule.density) { size.toDp() }
+ val offset = 10
+ val offsetDp = with(rule.density) { offset.toDp() }
+ val linePosition = 25
+ val hLine = HorizontalAlignmentLine(::min)
+ val vLine = VerticalAlignmentLine(::min)
+ rule.setContent {
+ val content = @Composable {
+ Box(Modifier.size(sizeDp)) {
+ Box(
+ Modifier.offset(offsetDp, offsetDp)
+ .supplyAlignmentLines {
+ mapOf(
+ hLine to linePosition,
+ vLine to linePosition
+ )
+ }.size(sizeDp)
+ .offset(offsetDp, offsetDp)
+ )
+ }
+ }
+
+ Layout(content) { measurables, constraints ->
+ val placeable = measurables.first().measure(constraints)
+ assertEquals(linePosition + offset, placeable[hLine])
+ assertEquals(linePosition + offset, placeable[vLine])
+ layout(0, 0) {}
+ }
+ }
+ }
+
+ @Test
+ fun alignmentLinesChangeCausesRemeasure_whenSuppliedViaModifier() {
+ val size = 50
+ val sizeDp = with(rule.density) { size.toDp() }
+ val offset = 10
+ val linePosition = 25
+ val hLine = HorizontalAlignmentLine(::min)
+ val vLine = VerticalAlignmentLine(::min)
+ val alignmentLines = mutableStateMapOf(hLine to linePosition, vLine to linePosition)
+ var obtainedHLinePosition = -1
+ var obtainedVLinePosition = -1
+ rule.setContent {
+ val content = @Composable {
+ Box(Modifier.size(sizeDp)) {
+ Box(Modifier.supplyAlignmentLines { alignmentLines.toMap() }.size(sizeDp))
+ }
+ }
+
+ Layout(content) { measurables, constraints ->
+ val placeable = measurables.first().measure(constraints)
+ obtainedHLinePosition = placeable[hLine]
+ obtainedVLinePosition = placeable[vLine]
+ layout(0, 0) {}
+ }
+ }
+
+ rule.runOnIdle {
+ assertEquals(linePosition, obtainedHLinePosition)
+ assertEquals(linePosition, obtainedVLinePosition)
+ alignmentLines[hLine] = linePosition + offset
+ alignmentLines[vLine] = linePosition + offset
+ }
+
+ rule.runOnIdle {
+ assertEquals(linePosition + offset, obtainedHLinePosition)
+ assertEquals(linePosition + offset, obtainedVLinePosition)
+ }
+ }
+
+ @Test
+ fun alignmentLinesChangeCausesRemeasure_whenSuppliedViaLayout() {
+ val size = 50
+ val sizeDp = with(rule.density) { size.toDp() }
+ val offset = 10
+ val linePosition = 25
+ val hLine = HorizontalAlignmentLine(::min)
+ val vLine = VerticalAlignmentLine(::min)
+ val alignmentLines = mutableStateMapOf(hLine to linePosition, vLine to linePosition)
+ var obtainedHLinePosition = -1
+ var obtainedVLinePosition = -1
+ rule.setContent {
+ val content = @Composable {
+ val innerContent = @Composable {
+ Layout({}) { _, _ ->
+ layout(size, size, alignmentLines) {}
+ }
+ }
+ Layout(content = innerContent, Modifier.size(sizeDp)) { measurables, constraints ->
+ val placeable = measurables.first().measure(constraints)
+ layout(constraints.maxWidth, constraints.maxHeight) {
+ placeable.place(0, 0)
+ }
+ }
+ }
+
+ Layout(content) { measurables, constraints ->
+ val placeable = measurables.first().measure(constraints)
+ obtainedHLinePosition = placeable[hLine]
+ obtainedVLinePosition = placeable[vLine]
+ layout(0, 0) {}
+ }
+ }
+
+ rule.runOnIdle {
+ assertEquals(linePosition, obtainedHLinePosition)
+ assertEquals(linePosition, obtainedVLinePosition)
+ alignmentLines[hLine] = linePosition + offset
+ alignmentLines[vLine] = linePosition + offset
+ }
+
+ rule.runOnIdle {
+ assertEquals(linePosition + offset, obtainedHLinePosition)
+ assertEquals(linePosition + offset, obtainedVLinePosition)
+ }
+ }
+
+ @Test
+ fun scenario1() {
+ var parentMeasures = 0
+ var measures = 0
+ rule.setContent {
+ Parent(onMeasure = { ++parentMeasures }) {
+ Parent(onMeasure = { ++measures }, readDuringMeasure = true) {
+ Parent {
+ Provider()
+ }
+ }
+ }
+ }
+ rule.runOnIdle {
+ assertEquals(1, parentMeasures)
+ assertEquals(1, measures)
+ changeLinePosition()
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, parentMeasures)
+ assertEquals(2, measures)
+ }
+ }
+
+ @Test
+ fun scenario2() {
+ var parentLayouts = 0
+ var measures = 0
+ var layouts = 0
+ rule.setContent {
+ Parent(onLayout = { ++parentLayouts }) {
+ Parent(
+ onMeasure = { ++measures },
+ onLayout = { ++layouts },
+ readDuringLayoutBeforePlacing = true
+ ) {
+ Parent {
+ Provider()
+ }
+ }
+ }
+ }
+ rule.runOnIdle {
+ assertEquals(1, parentLayouts)
+ assertEquals(1, measures)
+ assertEquals(1, layouts)
+ changeLinePosition()
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, parentLayouts)
+ assertEquals(1, measures)
+ assertEquals(2, layouts)
+ }
+ }
+
+ @Test
+ fun scenario3() {
+ var parentLayouts = 0
+ var measures = 0
+ var layouts = 0
+ rule.setContent {
+ Parent(onLayout = { ++parentLayouts }) {
+ Parent(
+ onMeasure = { ++measures },
+ onLayout = { ++layouts },
+ readDuringLayoutAfterPlacing = true
+ ) {
+ Parent {
+ Provider()
+ }
+ }
+ }
+ }
+ rule.runOnIdle {
+ assertEquals(1, parentLayouts)
+ assertEquals(1, measures)
+ assertEquals(1, layouts)
+ changeLinePosition()
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, parentLayouts)
+ assertEquals(1, measures)
+ assertEquals(2, layouts)
+ }
+ }
+
+ @Test
+ fun scenario4() {
+ var parentMeasures = 0
+ var parentLayouts = 0
+ var measures = 0
+ var layouts = 0
+ rule.setContent {
+ Parent(
+ onMeasure = { ++parentMeasures },
+ onLayout = { ++parentLayouts },
+ readDuringMeasure = true
+ ) {
+ Parent(
+ onMeasure = { ++measures },
+ onLayout = { ++layouts },
+ readDuringLayoutBeforePlacing = true
+ ) {
+ Parent {
+ Provider()
+ }
+ }
+ }
+ }
+ rule.runOnIdle {
+ assertEquals(1, parentMeasures)
+ assertEquals(1, parentLayouts)
+ assertEquals(1, measures)
+ assertEquals(1, layouts)
+ changeLinePosition()
+ }
+
+ rule.runOnIdle {
+ assertEquals(2, parentMeasures)
+ assertEquals(2, parentLayouts)
+ assertEquals(1, measures)
+ assertEquals(2, layouts)
+ }
+ }
+
+ @Test
+ fun scenario5() {
+ var parentMeasures = 0
+ var parentLayouts = 0
+ var measures = 0
+ var layouts = 0
+ rule.setContent {
+ Parent(
+ onMeasure = { ++parentMeasures },
+ onLayout = { ++parentLayouts },
+ readDuringMeasure = true
+ ) {
+ Parent(
+ onMeasure = { ++measures },
+ onLayout = { ++layouts },
+ readDuringLayoutAfterPlacing = true
+ ) {
+ Parent {
+ Provider()
+ }
+ }
+ }
+ }
+ rule.runOnIdle {
+ assertEquals(1, parentMeasures)
+ assertEquals(1, parentLayouts)
+ assertEquals(1, measures)
+ assertEquals(1, layouts)
+ changeLinePosition()
+ }
+
+ rule.runOnIdle {
+ assertEquals(2, parentMeasures)
+ assertEquals(2, parentLayouts)
+ assertEquals(1, measures)
+ assertEquals(2, layouts)
+ }
+ }
+
+ @Test
+ fun scenario6() {
+ var parentMeasures = 0
+ var parentLayouts = 0
+ var measures = 0
+ var layouts = 0
+ rule.setContent {
+ Parent(
+ onMeasure = { ++parentMeasures },
+ onLayout = { ++parentLayouts },
+ readDuringLayoutAfterPlacing = true
+ ) {
+ Parent(
+ onMeasure = { ++measures },
+ onLayout = { ++layouts },
+ readDuringMeasure = true
+ ) {
+ Parent {
+ Provider()
+ }
+ }
+ }
+ }
+ rule.runOnIdle {
+ assertEquals(1, parentMeasures)
+ assertEquals(1, parentLayouts)
+ assertEquals(1, measures)
+ assertEquals(1, layouts)
+ changeLinePosition()
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, parentMeasures)
+ assertEquals(2, parentLayouts)
+ assertEquals(2, measures)
+ assertEquals(2, layouts)
+ }
+ }
+
+ @Test
+ fun scenario7() {
+ var parentMeasures = 0
+ var measures = 0
+ var layouts = 0
+ var childMeasures = 0
+ rule.setContent {
+ Parent(onMeasure = { ++parentMeasures }) {
+ Parent(
+ onMeasure = { ++measures },
+ onLayout = { ++layouts },
+ readDuringMeasure = true
+ ) {
+ Parent(modifier = Modifier.provider(), onMeasure = { ++childMeasures }) {
+ Parent()
+ }
+ }
+ }
+ }
+ rule.runOnIdle {
+ assertEquals(1, parentMeasures)
+ assertEquals(1, measures)
+ assertEquals(1, layouts)
+ assertEquals(1, childMeasures)
+ changeLinePosition()
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, parentMeasures)
+ assertEquals(2, measures)
+ assertEquals(2, layouts)
+ assertEquals(2, childMeasures)
+ }
+ }
+
+ @Test
+ fun scenario8() {
+ var parentMeasures = 0
+ var measures = 0
+ var layouts = 0
+ var childMeasures = 0
+ rule.setContent {
+ Parent(onMeasure = { ++parentMeasures }) {
+ Parent(
+ onMeasure = { ++measures },
+ onLayout = { ++layouts },
+ readDuringLayoutBeforePlacing = true
+ ) {
+ Parent(modifier = Modifier.provider(), onMeasure = { ++childMeasures }) {
+ Parent()
+ }
+ }
+ }
+ }
+ rule.runOnIdle {
+ assertEquals(1, parentMeasures)
+ assertEquals(1, measures)
+ assertEquals(1, layouts)
+ assertEquals(1, childMeasures)
+ changeLinePosition()
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, parentMeasures)
+ assertEquals(1, measures)
+ assertEquals(2, layouts)
+ assertEquals(2, childMeasures)
+ }
+ }
+
+ @Test
+ fun scenario9() {
+ var parentMeasures = 0
+ var measures = 0
+ var layouts = 0
+ var childMeasures = 0
+ rule.setContent {
+ Parent(onMeasure = { ++parentMeasures }) {
+ Parent(
+ onMeasure = { ++measures },
+ onLayout = { ++layouts },
+ readDuringLayoutAfterPlacing = true
+ ) {
+ Parent(modifier = Modifier.provider(), onMeasure = { ++childMeasures }) {
+ Parent()
+ }
+ }
+ }
+ }
+ rule.runOnIdle {
+ assertEquals(1, parentMeasures)
+ assertEquals(1, measures)
+ assertEquals(1, layouts)
+ assertEquals(1, childMeasures)
+ changeLinePosition()
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, parentMeasures)
+ assertEquals(1, measures)
+ assertEquals(2, layouts)
+ assertEquals(2, childMeasures)
+ }
+ }
+
+ @Test
+ fun scenario10() {
+ var parentMeasures = 0
+ var measures = 0
+ var layouts = 0
+ var childMeasures = 0
+ rule.setContent {
+ Parent(onMeasure = { ++parentMeasures }, readDuringMeasure = true) {
+ Parent(
+ onMeasure = { ++measures },
+ onLayout = { ++layouts },
+ readDuringLayoutAfterPlacing = true
+ ) {
+ Parent(modifier = Modifier.provider(), onMeasure = { ++childMeasures }) {
+ Parent()
+ }
+ }
+ }
+ }
+ rule.runOnIdle {
+ assertEquals(1, parentMeasures)
+ assertEquals(1, measures)
+ assertEquals(1, layouts)
+ assertEquals(1, childMeasures)
+ changeLinePosition()
+ }
+
+ rule.runOnIdle {
+ assertEquals(2, parentMeasures)
+ assertEquals(1, measures)
+ assertEquals(2, layouts)
+ assertEquals(2, childMeasures)
+ }
+ }
+
+ @Test
+ fun scenario11() {
+ var measures = 0
+ var layouts = 0
+ var childMeasures = 0
+ rule.setContent {
+ Parent {
+ Parent(
+ onMeasure = { ++measures },
+ onLayout = { ++layouts },
+ readDuringLayoutAfterPlacing = true
+ ) {
+ Parent(
+ modifier = Modifier.reader(readDuringMeasure = true),
+ onMeasure = { ++childMeasures }
+ ) {
+ Provider()
+ }
+ }
+ }
+ }
+ rule.runOnIdle {
+ assertEquals(1, measures)
+ assertEquals(1, layouts)
+ assertEquals(1, childMeasures)
+ changeLinePosition()
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, measures)
+ assertEquals(2, layouts)
+ assertEquals(2, childMeasures)
+ }
+ }
+
+ @Test
+ fun scenario12() {
+ var childMeasures = 0
+ rule.setContent {
+ Parent {
+ Provider(
+ modifier = Modifier.reader(readDuringMeasure = true),
+ onMeasure = { ++childMeasures }
+ )
+ }
+ }
+ rule.runOnIdle {
+ assertEquals(1, childMeasures)
+ changeLinePosition()
+ }
+
+ rule.runOnIdle {
+ assertEquals(2, childMeasures)
+ }
+ }
+
+ @Test
+ fun scenario13() {
+ var measures = 0
+ var childMeasures = 0
+ var childLayouts = 0
+ rule.setContent {
+ Parent(onMeasure = { ++measures }) {
+ Provider(
+ modifier = Modifier.reader(readDuringLayoutBeforePlacing = true),
+ onMeasure = { ++childMeasures },
+ onLayout = { ++childLayouts }
+ )
+ }
+ }
+ rule.runOnIdle {
+ assertEquals(1, measures)
+ assertEquals(1, childMeasures)
+ assertEquals(1, childLayouts)
+ changeLinePosition()
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, measures)
+ assertEquals(2, childMeasures)
+ assertEquals(2, childLayouts)
+ }
+ }
+
+ @Test
+ fun scenario14() {
+ var measures = 0
+ var childMeasures = 0
+ var childLayouts = 0
+ rule.setContent {
+ Parent(onMeasure = { ++measures }) {
+ Provider(
+ modifier = Modifier.reader(readDuringLayoutAfterPlacing = true),
+ onMeasure = { ++childMeasures },
+ onLayout = { ++childLayouts }
+ )
+ }
+ }
+ rule.runOnIdle {
+ assertEquals(1, measures)
+ assertEquals(1, childMeasures)
+ assertEquals(1, childLayouts)
+ changeLinePosition()
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, measures)
+ assertEquals(2, childMeasures)
+ assertEquals(2, childLayouts)
+ }
+ }
+
+ @Test
+ fun scenario15() {
+ var parentMeasures = 0
+ var measures = 0
+ var childMeasures = 0
+ rule.setContent {
+ Parent(onMeasure = { ++parentMeasures }) {
+ Parent(
+ modifier = Modifier.reader(readDuringMeasure = true).provider(),
+ onMeasure = { ++measures }
+ ) {
+ Parent(onMeasure = { ++childMeasures })
+ }
+ }
+ }
+ rule.runOnIdle {
+ assertEquals(1, parentMeasures)
+ assertEquals(1, measures)
+ assertEquals(1, childMeasures)
+ changeLinePosition()
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, parentMeasures)
+ assertEquals(2, measures)
+ assertEquals(1, childMeasures)
+ }
+ }
+
+ @Test
+ fun scenario16() {
+ var parentMeasures = 0
+ var measures = 0
+ var childMeasures = 0
+ rule.setContent {
+ Parent(onMeasure = { ++parentMeasures }, readDuringMeasure = true) {
+ Parent(
+ modifier = Modifier.reader(readDuringMeasure = true).provider(),
+ onMeasure = { ++measures }
+ ) {
+ Parent(onMeasure = { ++childMeasures })
+ }
+ }
+ }
+ rule.runOnIdle {
+ assertEquals(1, parentMeasures)
+ assertEquals(1, measures)
+ assertEquals(1, childMeasures)
+ changeLinePosition()
+ }
+
+ rule.runOnIdle {
+ assertEquals(2, parentMeasures)
+ assertEquals(2, measures)
+ assertEquals(1, childMeasures)
+ }
+ }
+
+ @Test
+ fun scenario17() {
+ var parentMeasures = 0
+ var measures = 0
+ var childMeasures = 0
+ var read by mutableStateOf(true)
+ rule.setContent {
+ Parent(onMeasure = { ++parentMeasures }, readDuringMeasure = true) {
+ ChangingParent(
+ onMeasure = { ++measures },
+ readDuringMeasure = { read }
+ ) {
+ Parent(onMeasure = { ++childMeasures }) {
+ Provider()
+ }
+ }
+ }
+ }
+ rule.runOnIdle {
+ assertEquals(1, parentMeasures)
+ assertEquals(1, measures)
+ assertEquals(1, childMeasures)
+ changeLinePosition()
+ read = false
+ }
+
+ rule.runOnIdle {
+ assertEquals(2, parentMeasures)
+ assertEquals(2, measures)
+ assertEquals(1, childMeasures)
+ changeLinePosition()
+ }
+
+ rule.runOnIdle {
+ assertEquals(3, parentMeasures)
+ assertEquals(2, measures)
+ assertEquals(1, childMeasures)
+ }
+ }
+
+ @Test
+ fun scenario18() {
+ var parentLayouts = 0
+ var parentMeasures = 0
+ var measures = 0
+ var childMeasures = 0
+ var read by mutableStateOf(true)
+ rule.setContent {
+ Parent(
+ onMeasure = { ++parentMeasures },
+ onLayout = { ++parentLayouts },
+ readDuringLayoutAfterPlacing = true
+ ) {
+ ChangingParent(
+ onMeasure = { ++measures },
+ readDuringMeasure = { read }
+ ) {
+ Parent(onMeasure = { ++childMeasures }) {
+ Provider()
+ }
+ }
+ }
+ }
+ rule.runOnIdle {
+ assertEquals(1, parentMeasures)
+ assertEquals(1, parentLayouts)
+ assertEquals(1, measures)
+ assertEquals(1, childMeasures)
+ changeLinePosition()
+ read = false
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, parentMeasures)
+ assertEquals(2, parentLayouts)
+ assertEquals(2, measures)
+ assertEquals(1, childMeasures)
+ changeLinePosition()
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, parentMeasures)
+ assertEquals(3, parentLayouts)
+ assertEquals(2, measures)
+ assertEquals(1, childMeasures)
+ }
+ }
+
+ @Test
+ fun scenario19() {
+ var offset by mutableStateOf(IntOffset.Zero)
+ rule.setContent {
+ Parent(readDuringLayoutBeforePlacing = true) {
+ Provider(modifier = Modifier.offset { offset })
+ }
+ }
+ rule.runOnIdle {
+ offset = IntOffset(10, 10)
+ linePosition += 10
+ }
+
+ rule.waitForIdle()
+ }
+
+ @Test
+ fun scenario20() {
+ var parentLayouts = 0
+ var offset by mutableStateOf(IntOffset.Zero)
+ rule.setContent {
+ Parent(readDuringLayoutBeforePlacing = true, onLayout = { ++parentLayouts }) {
+ Parent {
+ Provider(modifier = Modifier.offset { offset })
+ }
+ }
+ }
+ rule.runOnIdle {
+ offset = IntOffset(10, 10)
+ linePosition += 10
+ }
+
+ rule.runOnIdle {
+ assertEquals(2, parentLayouts)
+ }
+
+ rule.waitForIdle()
+ }
+
+ @Test
+ fun scenario21() {
+ var parentMeasures = 0
+ var read by mutableStateOf(false)
+ rule.setContent {
+ ChangingParent(
+ readDuringMeasure = { read },
+ onMeasure = { ++parentMeasures }
+ ) {
+ Parent {
+ Provider()
+ }
+ }
+ }
+ rule.runOnIdle {
+ read = true
+ }
+
+ rule.runOnIdle {
+ assertEquals(2, parentMeasures)
+ }
+ }
+
+ @Test
+ fun scenario22() {
+ var parentLayouts = 0
+ var read by mutableStateOf(false)
+ rule.setContent {
+ ChangingParent(
+ readDuringLayoutBeforePlacing = { read },
+ onLayout = { ++parentLayouts }
+ ) {
+ Parent {
+ Provider()
+ }
+ }
+ }
+ rule.runOnIdle {
+ read = true
+ }
+
+ rule.runOnIdle {
+ assertEquals(2, parentLayouts)
+ }
+ }
+
+ @Test
+ fun scenario23() {
+ var obtainedPosition = 0
+ var changingState by mutableStateOf(false)
+ rule.setContent {
+ Layout(
+ content = {
+ Parent {
+ Provider()
+ }
+ }
+ ) { measurables, constraints ->
+ val placeable = measurables.first().measure(constraints)
+ layout(constraints.maxWidth, constraints.maxHeight) {
+ if (changingState) require(true)
+ obtainedPosition = placeable[TestLine]
+ placeable.place(0, 0)
+ }
+ }
+ }
+ rule.runOnIdle {
+ assertEquals(linePosition, obtainedPosition)
+ changeLinePosition()
+ changingState = true
+ }
+
+ rule.runOnIdle {
+ assertEquals(linePosition, obtainedPosition)
+ }
+ }
+
+ @Test
+ fun scenario24() {
+ var obtainedPosition = 0
+ var changingState by mutableStateOf(false)
+ rule.setContent {
+ Layout(
+ content = {
+ Parent {
+ Provider()
+ }
+ }
+ ) { measurables, constraints ->
+ val placeable = measurables.first().measure(constraints)
+ layout(constraints.maxWidth, constraints.maxHeight) {
+ if (changingState) require(true)
+ placeable.place(0, 0)
+ obtainedPosition = placeable[TestLine]
+ }
+ }
+ }
+ rule.runOnIdle {
+ assertEquals(linePosition, obtainedPosition)
+ changeLinePosition()
+ changingState = true
+ }
+
+ rule.runOnIdle {
+ assertEquals(linePosition, obtainedPosition)
+ }
+ }
+
+ @Test
+ fun scenario25() {
+ var obtainedPosition = 0
+ rule.setContent {
+ Parent(modifier = Modifier.onGloballyPositioned { obtainedPosition = it[TestLine] }) {
+ Parent {
+ Provider()
+ }
+ }
+ }
+ rule.runOnIdle {
+ assertEquals(linePosition, obtainedPosition)
+ changeLinePosition()
+ }
+
+ rule.runOnIdle {
+ assertEquals(linePosition, obtainedPosition)
+ }
+ }
+
+ @Test
+ fun scenario26() {
+ var measures = 0
+ var layouts = 0
+ rule.setContent {
+ Parent(
+ modifier = Modifier.reader(
+ readDuringMeasure = true,
+ readDuringLayoutBeforePlacing = true,
+ onMeasure = { ++measures },
+ onLayout = { ++layouts }
+ )
+ ) {
+ Parent {
+ Provider()
+ }
+ }
+ }
+ rule.runOnIdle {
+ assertEquals(1, measures)
+ assertEquals(1, layouts)
+ changeLinePosition()
+ }
+
+ rule.runOnIdle {
+ assertEquals(2, measures)
+ assertEquals(2, layouts)
+ }
+ }
+
+ @Test
+ fun scenario27() {
+ var measures = 0
+ var layouts = 0
+ rule.setContent {
+ Parent(
+ modifier = Modifier.reader(
+ readDuringLayoutBeforePlacing = true,
+ onMeasure = { ++measures },
+ onLayout = { ++layouts }
+ )
+ ) {
+ Parent {
+ Provider()
+ }
+ }
+ }
+ rule.runOnIdle {
+ assertEquals(1, measures)
+ assertEquals(1, layouts)
+ changeLinePosition()
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, measures)
+ assertEquals(2, layouts)
+ }
+ }
+
+ @Test
+ fun scenario28() {
+ var measures = 0
+ var layouts = 0
+ rule.setContent {
+ Parent(
+ modifier = Modifier.reader(
+ readDuringLayoutAfterPlacing = true,
+ onMeasure = { ++measures },
+ onLayout = { ++layouts }
+ )
+ ) {
+ Parent {
+ Provider()
+ }
+ }
+ }
+ rule.runOnIdle {
+ assertEquals(1, measures)
+ assertEquals(1, layouts)
+ changeLinePosition()
+ }
+
+ rule.runOnIdle {
+ assertEquals(1, measures)
+ assertEquals(2, layouts)
+ }
+ }
+
+ private var linePosition = 10
+ private var linePositionState by mutableStateOf(10)
+ private fun changeLinePosition() {
+ linePosition += 10
+ linePositionState += 10
+ }
+ private val TestLine = HorizontalAlignmentLine(::min)
+
+ @Composable
+ private fun Parent(
+ modifier: Modifier = Modifier,
+ onMeasure: () -> Unit = {},
+ onLayout: () -> Unit = {},
+ readDuringMeasure: Boolean = false,
+ readDuringLayoutBeforePlacing: Boolean = false,
+ readDuringLayoutAfterPlacing: Boolean = false,
+ content: @Composable () -> Unit = {}
+ ) {
+ ChangingParent(
+ modifier,
+ onMeasure,
+ onLayout,
+ { readDuringMeasure },
+ { readDuringLayoutBeforePlacing },
+ { readDuringLayoutAfterPlacing },
+ content
+ )
+ }
+
+ @Composable
+ private fun ChangingParent(
+ modifier: Modifier = Modifier,
+ onMeasure: () -> Unit = {},
+ onLayout: () -> Unit = {},
+ readDuringMeasure: () -> Boolean = { false },
+ readDuringLayoutBeforePlacing: () -> Boolean = { false },
+ readDuringLayoutAfterPlacing: () -> Boolean = { false },
+ content: @Composable () -> Unit = {}
+ ) {
+ Layout(content, modifier) { measurables, constraints ->
+ onMeasure()
+ val placeables = measurables.map {
+ it.measure(constraints).also {
+ if (readDuringMeasure()) assertEquals(linePosition, it[TestLine])
+ }
+ }
+ layout(constraints.maxWidth, constraints.maxHeight) {
+ onLayout()
+ placeables.forEach { placeable ->
+ if (readDuringLayoutBeforePlacing()) {
+ // placeable[TestLine]
+ assertEquals(linePosition, placeable[TestLine])
+ }
+ placeable.place(0, 0)
+ if (readDuringLayoutAfterPlacing()) {
+ // placeable[TestLine]
+ assertEquals(linePosition, placeable[TestLine])
+ }
+ }
+ }
+ }
+ }
+
+ @Composable
+ private fun Provider(
+ modifier: Modifier = Modifier,
+ onMeasure: () -> Unit = {},
+ onLayout: () -> Unit = {},
+ content: @Composable () -> Unit = {}
+ ) {
+ Layout(content, modifier) { _, constraints ->
+ onMeasure()
+ layout(
+ constraints.maxWidth,
+ constraints.maxHeight,
+ mapOf(TestLine to linePositionState)
+ ) {
+ onLayout()
+ }
+ }
+ }
+
+ private fun Modifier.reader(
+ onMeasure: () -> Unit = {},
+ onLayout: () -> Unit = {},
+ readDuringMeasure: Boolean = false,
+ readDuringLayoutBeforePlacing: Boolean = false,
+ readDuringLayoutAfterPlacing: Boolean = false
+ ) = this.then(
+ ReaderModifier(
+ onMeasure,
+ onLayout,
+ readDuringMeasure,
+ readDuringLayoutBeforePlacing,
+ readDuringLayoutAfterPlacing
+ )
+ )
+ private inner class ReaderModifier(
+ val onMeasure: () -> Unit,
+ val onLayout: () -> Unit,
+ val readDuringMeasure: Boolean,
+ val readDuringLayoutBeforePlacing: Boolean,
+ val readDuringLayoutAfterPlacing: Boolean
+ ) : LayoutModifier {
+ override fun MeasureScope.measure(
+ measurable: Measurable,
+ constraints: Constraints
+ ): MeasureResult {
+ onMeasure()
+ val placeable = measurable.measure(constraints)
+ if (readDuringMeasure) assertEquals(linePosition, placeable[TestLine])
+ return layout(constraints.maxWidth, constraints.maxHeight) {
+ onLayout()
+ if (readDuringLayoutBeforePlacing) assertEquals(linePosition, placeable[TestLine])
+ placeable.place(0, 0)
+ if (readDuringLayoutAfterPlacing) assertEquals(linePosition, placeable[TestLine])
+ }
+ }
+ }
+
+ private fun Modifier.provider() = this.then(ProviderModifier())
+ private inner class ProviderModifier : LayoutModifier {
+ override fun MeasureScope.measure(
+ measurable: Measurable,
+ constraints: Constraints
+ ): MeasureResult {
+ val placeable = measurable.measure(constraints)
+ return layout(
+ constraints.maxWidth,
+ constraints.maxHeight,
+ mapOf(TestLine to linePositionState)
+ ) {
+ placeable.place(0, 0)
+ }
+ }
+
+ override fun hashCode(): Int {
+ return 0
+ }
+
+ override fun equals(other: Any?): Boolean {
+ return other is ProviderModifier
+ }
+ }
+
+ private fun Modifier.supplyAlignmentLines(alignmentLines: () -> Map<AlignmentLine, Int>) =
+ layout { measurable, constraints ->
+ val placeable = measurable.measure(constraints)
+ layout(placeable.width, placeable.height, alignmentLines()) {
+ placeable.place(0, 0)
+ }
+ }
}
\ No newline at end of file
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/OnGloballyPositionedTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/OnGloballyPositionedTest.kt
index d7ae773..c66b1c7 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/OnGloballyPositionedTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/OnGloballyPositionedTest.kt
@@ -621,16 +621,24 @@
@Test
fun testAlignmentLinesArePresent() {
val latch = CountDownLatch(1)
- val line = VerticalAlignmentLine(::min)
+ val line1 = VerticalAlignmentLine(::min)
+ val line2 = HorizontalAlignmentLine(::min)
val lineValue = 10
rule.setContent {
val onPositioned = Modifier.onGloballyPositioned { coordinates: LayoutCoordinates ->
- assertEquals(1, coordinates.providedAlignmentLines.size)
- assertEquals(lineValue, coordinates[line])
+ assertEquals(2, coordinates.providedAlignmentLines.size)
+ assertEquals(lineValue, coordinates[line1])
+ assertEquals(lineValue, coordinates[line2])
latch.countDown()
}
- Layout(modifier = onPositioned, content = { }) { _, _ ->
- layout(0, 0, mapOf(line to lineValue)) { }
+ val lineProvider = Modifier.layout { measurable, constraints ->
+ val placeable = measurable.measure(constraints)
+ layout(0, 0, mapOf(line2 to lineValue)) {
+ placeable.place(0, 0)
+ }
+ }
+ Layout(modifier = onPositioned.then(lineProvider), content = { }) { _, _ ->
+ layout(0, 0, mapOf(line1 to lineValue)) { }
}
}
assertTrue(latch.await(1, TimeUnit.SECONDS))
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/ComposeViewOverlayTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/ComposeViewOverlayTest.kt
new file mode 100644
index 0000000..142aa4d
--- /dev/null
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/platform/ComposeViewOverlayTest.kt
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.platform
+
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.LinearLayout
+import androidx.activity.ComponentActivity
+import androidx.compose.foundation.text.BasicText
+import androidx.compose.runtime.Recomposer
+import androidx.compose.runtime.SideEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.runtime.snapshots.Snapshot
+import androidx.compose.ui.InternalComposeUiApi
+import androidx.lifecycle.LifecycleOwner
+import androidx.test.ext.junit.rules.activityScenarioRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.runBlocking
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotNull
+import org.junit.Assert.assertTrue
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class ComposeViewOverlayTest {
+ /**
+ * Note: this test does not use the compose rule to ensure default behavior
+ * of window-scoped Recomposer installation.
+ */
+ @get:Rule
+ val rule = activityScenarioRule<ComponentActivity>()
+
+ /**
+ * Moving a ComposeView to an [android.view.ViewOverlay] means it won't have a correct parent
+ * chain. This happens when user code performs view transitions on the rest of the view
+ * hierarchy. Test that ComposeView still works when moved back and forth as long as it was
+ * originally attached to the target window "for real."
+ */
+ @OptIn(InternalComposeUiApi::class)
+ @Test
+ fun testComposeViewMovedToOverlay() {
+ var factoryCallCount = 0
+ lateinit var createdRecomposer: Recomposer
+ WindowRecomposerPolicy.withFactory(
+ { view ->
+ factoryCallCount++
+ WindowRecomposerFactory.LifecycleAware.createRecomposer(view).also {
+ createdRecomposer = it
+ }
+ }
+ ) {
+ val expectedText = "Hello, world"
+ lateinit var composeView: ComposeView
+ lateinit var contentAView: ViewGroup
+ lateinit var contentBView: ViewGroup
+ var localLifecycleOwner by mutableStateOf<LifecycleOwner?>(null)
+ var publishedStage by mutableStateOf(0)
+ var consumedStage by mutableStateOf(-1)
+ var compositionCount = 0
+ rule.scenario.onActivity { activity ->
+ composeView = ComposeView(activity).apply {
+ setContent {
+ BasicText(expectedText)
+ localLifecycleOwner = LocalLifecycleOwner.current
+ consumedStage = publishedStage
+ SideEffect {
+ compositionCount++
+ }
+ }
+ }
+ contentAView = FrameLayout(activity).apply {
+ addView(composeView)
+ }
+ contentBView = FrameLayout(activity)
+ val views = LinearLayout(activity).apply {
+ addView(
+ contentAView,
+ LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, 1f)
+ )
+ addView(
+ contentBView,
+ LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, 1f)
+ )
+ }
+ activity.setContentView(views)
+ }
+
+ createdRecomposer.waitForIdle()
+
+ assertNotNull("expected non-null LocalLifecycleOwner", localLifecycleOwner)
+ assertEquals("unexpected recomposition result", publishedStage, consumedStage)
+ assertTrue("composed at least once", compositionCount > 0)
+ val compositionCountAtFirstIdle: Int = compositionCount
+ rule.scenario.onActivity {
+ contentAView.removeView(composeView)
+ contentBView.overlay.add(composeView)
+ publishedStage++
+ // Send apply notifications right away so that we know it happened
+ // before this onActivity block returns back to the test thread.
+ // Otherwise the waitForIdleRecomposers below can run before the publishedStage
+ // change is picked up by the recomposer.
+ Snapshot.sendApplyNotifications()
+ }
+
+ createdRecomposer.waitForIdle()
+
+ assertNotNull("overlay expected non-null LocalLifecycleOwner", localLifecycleOwner)
+ assertEquals("unexpected recomposition overlay result", publishedStage, consumedStage)
+ assertTrue(
+ "recomposed at least once since idle",
+ compositionCount > compositionCountAtFirstIdle
+ )
+ }
+ assertEquals("Created recomposer count", 1, factoryCallCount)
+ }
+
+ private fun Recomposer.waitForIdle() = runBlocking {
+ state.filter { it == Recomposer.State.Idle }.first()
+ }
+}
\ No newline at end of file
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ComposeView.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ComposeView.android.kt
index 7d990253..69f0564 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ComposeView.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/ComposeView.android.kt
@@ -17,6 +17,7 @@
package androidx.compose.ui.platform
import android.content.Context
+import android.os.IBinder
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
@@ -29,14 +30,6 @@
import androidx.compose.ui.node.Owner
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ViewTreeLifecycleOwner
-import androidx.savedstate.ViewTreeSavedStateRegistryOwner
-
-private const val MissingViewTreeDependenciesMessage =
- "If you are adding this ComposeView to an AppCompatActivity, make sure you " +
- "are using AppCompat version 1.3+. If you are adding this ComposeView to a " +
- "Fragment, make sure you are using Fragment version 1.3+. For other cases, manually " +
- "set owners on this view by using `ViewTreeLifecycleOwner.set()` and " +
- "`ViewTreeSavedStateRegistryOwner.set()`."
/**
* Base class for custom [android.view.View]s implemented using Jetpack Compose UI.
@@ -63,12 +56,44 @@
clipToPadding = false
}
+ /**
+ * The first time we successfully locate this we'll save it here.
+ * If this View moves to the [android.view.ViewOverlay] we won't be able
+ * to find view tree dependencies; this happens when using transition APIs
+ * to animate views out in particular.
+ */
+ private var cachedViewTreeCompositionContext: CompositionContext? = null
+
+ /**
+ * The [getWindowToken] of the window this view was last attached to.
+ * If we become attached to a new window we clear [cachedViewTreeCompositionContext]
+ * so that we might appeal to the (possibly lazily created) [windowRecomposer]
+ * if [findViewTreeCompositionContext] can't locate one instead of using the previous
+ * [cachedViewTreeCompositionContext].
+ */
+ private var previousAttachedWindowToken: IBinder? = null
+ set(value) {
+ if (field !== value) {
+ field = value
+ cachedViewTreeCompositionContext = null
+ }
+ }
+
private var composition: Composition? = null
+ /**
+ * The explicitly set [CompositionContext] to use as the parent of compositions created
+ * for this view. Set by [setParentCompositionContext].
+ *
+ * If set to a non-null value [cachedViewTreeCompositionContext] will be cleared.
+ */
private var parentContext: CompositionContext? = null
set(value) {
if (field !== value) {
field = value
+ if (value != null) {
+ cachedViewTreeCompositionContext = null
+ }
val old = composition
if (old !== null) {
old.dispose()
@@ -175,30 +200,31 @@
}
}
- private fun checkViewTreeOwners() {
- checkNotNull(ViewTreeLifecycleOwner.get(this)) {
- "ViewTreeLifecycleOwner not set for this ComposeView. " +
- MissingViewTreeDependenciesMessage
- }
- checkNotNull(ViewTreeSavedStateRegistryOwner.get(this)) {
- "ViewTreeSavedStateRegistryOwner not set for this ComposeView. " +
- MissingViewTreeDependenciesMessage
- }
- // Not checking for ViewTreeViewModelStoreOwner as we don't need it inside Compose,
- // but we provide it in ComponentActivity.setContent for convenience.
- }
+ /**
+ * Determine the correct [CompositionContext] to use as the parent of this view's
+ * composition. This can result in caching a looked-up [CompositionContext] for use
+ * later. See [cachedViewTreeCompositionContext] for more details.
+ *
+ * If [cachedViewTreeCompositionContext] is available but [findViewTreeCompositionContext]
+ * cannot find a parent context, we will use the cached context if present before appealing
+ * to the [windowRecomposer], as [windowRecomposer] can lazily create a recomposer.
+ * If we're reattached to the same window and [findViewTreeCompositionContext] can't find the
+ * context that [windowRecomposer] would install, we might be in the [getOverlay] of some
+ * part of the view hierarchy to animate the disappearance of this and other views. We still
+ * need to be able to compose/recompose in this state without creating a brand new recomposer
+ * to do it, as well as still locate any view tree dependencies.
+ */
+ private fun resolveParentCompositionContext() = parentContext
+ ?: findViewTreeCompositionContext()?.also { cachedViewTreeCompositionContext = it }
+ ?: cachedViewTreeCompositionContext
+ ?: windowRecomposer.also { cachedViewTreeCompositionContext = it }
@Suppress("DEPRECATION") // Still using ViewGroup.setContent for now
private fun ensureCompositionCreated() {
if (composition == null) {
- if (isAttachedToWindow) {
- checkViewTreeOwners()
- }
try {
creatingComposition = true
- composition = setContent(
- parentContext ?: findViewTreeCompositionContext() ?: windowRecomposer
- ) {
+ composition = setContent(resolveParentCompositionContext()) {
Content()
}
} finally {
@@ -227,9 +253,7 @@
override fun onAttachedToWindow() {
super.onAttachedToWindow()
- if (composition != null) {
- checkViewTreeOwners()
- }
+ previousAttachedWindowToken = windowToken
if (shouldCreateCompositionOnAttachedToWindow) {
ensureCompositionCreated()
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/InputMethodManager.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/InputMethodManager.kt
new file mode 100644
index 0000000..39071a16
--- /dev/null
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/InputMethodManager.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.text.input
+
+import android.content.Context
+import android.os.IBinder
+import android.view.View
+import android.view.inputmethod.ExtractedText
+
+internal interface InputMethodManager {
+ fun restartInput(view: View)
+
+ fun showSoftInput(view: View)
+
+ fun hideSoftInputFromWindow(windowToken: IBinder?)
+
+ fun updateExtractedText(
+ view: View,
+ token: Int,
+ extractedText: ExtractedText
+ )
+
+ fun updateSelection(
+ view: View,
+ selectionStart: Int,
+ selectionEnd: Int,
+ compositionStart: Int,
+ compositionEnd: Int
+ )
+}
+
+/**
+ * Wrapper class to prevent depending on getSystemService and final InputMethodManager.
+ * Let's us test TextInputServiceAndroid class.
+ */
+internal class InputMethodManagerImpl(context: Context) : InputMethodManager {
+
+ private val imm by lazy(LazyThreadSafetyMode.NONE) {
+ context.getSystemService(Context.INPUT_METHOD_SERVICE)
+ as android.view.inputmethod.InputMethodManager
+ }
+
+ override fun restartInput(view: View) {
+ imm.restartInput(view)
+ }
+
+ override fun showSoftInput(view: View) {
+ imm.showSoftInput(view, 0)
+ }
+
+ override fun hideSoftInputFromWindow(windowToken: IBinder?) {
+ imm.hideSoftInputFromWindow(windowToken, 0)
+ }
+
+ override fun updateExtractedText(
+ view: View,
+ token: Int,
+ extractedText: ExtractedText
+ ) {
+ imm.updateExtractedText(view, token, extractedText)
+ }
+
+ override fun updateSelection(
+ view: View,
+ selectionStart: Int,
+ selectionEnd: Int,
+ compositionStart: Int,
+ compositionEnd: Int
+ ) {
+ imm.updateSelection(view, selectionStart, selectionEnd, compositionStart, compositionEnd)
+ }
+}
\ No newline at end of file
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/RecordingInputConnection.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/RecordingInputConnection.android.kt
index 6ff574f..ceedc8c 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/RecordingInputConnection.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/RecordingInputConnection.android.kt
@@ -29,7 +29,6 @@
import android.view.inputmethod.ExtractedTextRequest
import android.view.inputmethod.InputConnection
import android.view.inputmethod.InputContentInfo
-import android.view.inputmethod.InputMethodManager
import androidx.annotation.VisibleForTesting
internal const val DEBUG = false
@@ -94,7 +93,11 @@
* This function may emits updateSelection and updateExtractedText to notify IMEs that the text
* contents has changed if needed.
*/
- fun updateInputState(state: TextFieldValue, imm: InputMethodManager, view: View) {
+ fun updateInputState(
+ state: TextFieldValue,
+ inputMethodManager: InputMethodManager,
+ view: View
+ ) {
if (!isActive) return
if (DEBUG) { logDebug("RecordingInputConnection.updateInputState: $state") }
@@ -102,7 +105,11 @@
mTextFieldValue = state
if (extractedTextMonitorMode) {
- imm.updateExtractedText(view, currentExtractedTextRequestToken, state.toExtractedText())
+ inputMethodManager.updateExtractedText(
+ view,
+ currentExtractedTextRequestToken,
+ state.toExtractedText()
+ )
}
// updateSelection API requires -1 if there is no composition
@@ -115,7 +122,7 @@
"composition = ($compositionStart, $compositionEnd))"
)
}
- imm.updateSelection(
+ inputMethodManager.updateSelection(
view, state.selection.min, state.selection.max, compositionStart, compositionEnd
)
}
diff --git a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/TextInputServiceAndroid.android.kt b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/TextInputServiceAndroid.android.kt
index 0ecb4fe..e008960 100644
--- a/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/TextInputServiceAndroid.android.kt
+++ b/compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/TextInputServiceAndroid.android.kt
@@ -16,9 +16,6 @@
package androidx.compose.ui.text.input
-import android.annotation.SuppressLint
-import android.content.Context
-import android.os.Build
import android.text.InputType
import android.util.Log
import android.view.KeyEvent
@@ -27,9 +24,9 @@
import android.view.inputmethod.BaseInputConnection
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputConnection
-import android.view.inputmethod.InputMethodManager
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.text.TextRange
+import androidx.core.view.inputmethod.EditorInfoCompat
import kotlinx.coroutines.channels.Channel
import kotlin.math.roundToInt
@@ -38,7 +35,10 @@
/**
* Provide Android specific input service with the Operating System.
*/
-internal class TextInputServiceAndroid(val view: View) : PlatformTextInputService {
+internal class TextInputServiceAndroid(
+ val view: View,
+ private val inputMethodManager: InputMethodManager
+) : PlatformTextInputService {
/** True if the currently editable composable has connected */
private var editorHasFocus = false
@@ -55,21 +55,11 @@
private var imeOptions = ImeOptions.Default
private var ic: RecordingInputConnection? = null
// used for sendKeyEvent delegation
- private var baseInputConnection: BaseInputConnection = BaseInputConnection(view, false)
- private var focusedRect: android.graphics.Rect? = null
+ private val baseInputConnection by lazy(LazyThreadSafetyMode.NONE) {
+ BaseInputConnection(view, false)
+ }
- private var _imm: InputMethodManager? = null
- /**
- * The editable buffer used for BaseInputConnection.
- */
- private val imm: InputMethodManager
- get() {
- if (_imm == null) {
- _imm = view.context.getSystemService(Context.INPUT_METHOD_SERVICE) as
- InputMethodManager
- }
- return _imm!!
- }
+ private var focusedRect: android.graphics.Rect? = null
/**
* A channel that is used to send ShowKeyboard/HideKeyboard commands. Send 'true' for
@@ -83,6 +73,8 @@
focusedRect?.let { view.requestRectangleOnScreen(it) }
}
+ internal constructor(view: View) : this(view, InputMethodManagerImpl(view.context))
+
init {
if (DEBUG) { Log.d(TAG, "$DEBUG_CLASS.create") }
@@ -104,7 +96,8 @@
if (!editorHasFocus) {
return null
}
- fillEditorInfo(outAttrs)
+
+ outAttrs.update(imeOptions, state)
return RecordingInputConnection(
initState = state,
@@ -167,7 +160,7 @@
private fun restartInput() {
if (DEBUG) Log.d(TAG, "$DEBUG_CLASS.restartInput")
- imm.restartInput(view)
+ inputMethodManager.restartInput(view)
}
override fun showSoftwareKeyboard() {
@@ -189,10 +182,10 @@
// to make sure that we use the most recent value.
if (showKeyboardChannel.poll() ?: showKeyboard) {
if (DEBUG) { Log.d(TAG, "$DEBUG_CLASS.keyboardVisibilityEventLoop.showSoftInput") }
- imm.showSoftInput(view, 0)
+ inputMethodManager.showSoftInput(view)
} else {
if (DEBUG) { Log.d(TAG, "$DEBUG_CLASS.keyboardVisibilityEventLoop.hideSoftInput") }
- imm.hideSoftInputFromWindow(view.windowToken, 0)
+ inputMethodManager.hideSoftInputFromWindow(view.windowToken)
}
}
}
@@ -224,7 +217,7 @@
if (restartInput) {
restartInput()
} else {
- ic?.updateInputState(this.state, imm, view)
+ ic?.updateInputState(this.state, inputMethodManager, view)
}
}
@@ -246,97 +239,94 @@
view.requestRectangleOnScreen(focusedRect)
}
}
+}
- /**
- * Fills necessary info of EditorInfo.
- */
- private fun fillEditorInfo(outInfo: EditorInfo) {
- outInfo.imeOptions = when (imeOptions.imeAction) {
- ImeAction.Default -> {
- if (imeOptions.singleLine) {
- // this is the last resort to enable single line
- // Android IME still show return key even if multi line is not send
- // TextView.java#onCreateInputConnection
- EditorInfo.IME_ACTION_DONE
- } else {
- EditorInfo.IME_ACTION_UNSPECIFIED
- }
- }
- ImeAction.None -> EditorInfo.IME_ACTION_NONE
- ImeAction.Go -> EditorInfo.IME_ACTION_GO
- ImeAction.Next -> EditorInfo.IME_ACTION_NEXT
- ImeAction.Previous -> EditorInfo.IME_ACTION_PREVIOUS
- ImeAction.Search -> EditorInfo.IME_ACTION_SEARCH
- ImeAction.Send -> EditorInfo.IME_ACTION_SEND
- ImeAction.Done -> EditorInfo.IME_ACTION_DONE
- else -> error("invalid ImeAction")
- }
- when (imeOptions.keyboardType) {
- KeyboardType.Text -> outInfo.inputType = InputType.TYPE_CLASS_TEXT
- KeyboardType.Ascii -> {
- outInfo.inputType = InputType.TYPE_CLASS_TEXT
- outInfo.imeOptions = outInfo.imeOptions or EditorInfo.IME_FLAG_FORCE_ASCII
- }
- KeyboardType.Number -> outInfo.inputType = InputType.TYPE_CLASS_NUMBER
- KeyboardType.Phone -> outInfo.inputType = InputType.TYPE_CLASS_PHONE
- KeyboardType.Uri ->
- outInfo.inputType = InputType.TYPE_CLASS_TEXT or EditorInfo.TYPE_TEXT_VARIATION_URI
- KeyboardType.Email ->
- outInfo.inputType =
- InputType.TYPE_CLASS_TEXT or EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
- KeyboardType.Password -> {
- outInfo.inputType =
- InputType.TYPE_CLASS_TEXT or EditorInfo.TYPE_TEXT_VARIATION_PASSWORD
- }
- KeyboardType.NumberPassword -> {
- outInfo.inputType =
- InputType.TYPE_CLASS_NUMBER or EditorInfo.TYPE_NUMBER_VARIATION_PASSWORD
- }
- else -> error("Invalid Keyboard Type")
- }
-
- if (!imeOptions.singleLine) {
- if (hasFlag(outInfo.inputType, InputType.TYPE_CLASS_TEXT)) {
- // TextView.java#setInputTypeSingleLine
- outInfo.inputType = outInfo.inputType or InputType.TYPE_TEXT_FLAG_MULTI_LINE
-
- if (imeOptions.imeAction == ImeAction.Default) {
- outInfo.imeOptions = outInfo.imeOptions or EditorInfo.IME_FLAG_NO_ENTER_ACTION
- }
+/**
+ * Fills necessary info of EditorInfo.
+ */
+internal fun EditorInfo.update(imeOptions: ImeOptions, textFieldValue: TextFieldValue) {
+ this.imeOptions = when (imeOptions.imeAction) {
+ ImeAction.Default -> {
+ if (imeOptions.singleLine) {
+ // this is the last resort to enable single line
+ // Android IME still show return key even if multi line is not send
+ // TextView.java#onCreateInputConnection
+ EditorInfo.IME_ACTION_DONE
+ } else {
+ EditorInfo.IME_ACTION_UNSPECIFIED
}
}
-
- if (hasFlag(outInfo.inputType, InputType.TYPE_CLASS_TEXT)) {
- when (imeOptions.capitalization) {
- KeyboardCapitalization.None -> {
- /* do nothing */
- }
- KeyboardCapitalization.Characters -> {
- outInfo.inputType = outInfo.inputType or InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS
- }
- KeyboardCapitalization.Words -> {
- outInfo.inputType = outInfo.inputType or InputType.TYPE_TEXT_FLAG_CAP_WORDS
- }
- KeyboardCapitalization.Sentences -> {
- outInfo.inputType = outInfo.inputType or InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
- }
- }
-
- if (imeOptions.autoCorrect) {
- outInfo.inputType = outInfo.inputType or InputType.TYPE_TEXT_FLAG_AUTO_CORRECT
- }
+ ImeAction.None -> EditorInfo.IME_ACTION_NONE
+ ImeAction.Go -> EditorInfo.IME_ACTION_GO
+ ImeAction.Next -> EditorInfo.IME_ACTION_NEXT
+ ImeAction.Previous -> EditorInfo.IME_ACTION_PREVIOUS
+ ImeAction.Search -> EditorInfo.IME_ACTION_SEARCH
+ ImeAction.Send -> EditorInfo.IME_ACTION_SEND
+ ImeAction.Done -> EditorInfo.IME_ACTION_DONE
+ else -> error("invalid ImeAction")
+ }
+ when (imeOptions.keyboardType) {
+ KeyboardType.Text -> this.inputType = InputType.TYPE_CLASS_TEXT
+ KeyboardType.Ascii -> {
+ this.inputType = InputType.TYPE_CLASS_TEXT
+ this.imeOptions = this.imeOptions or EditorInfo.IME_FLAG_FORCE_ASCII
}
-
- outInfo.initialSelStart = state.selection.start
- outInfo.initialSelEnd = state.selection.end
-
- @SuppressLint("UnsafeNewApiCall")
- if (Build.VERSION.SDK_INT >= 30) {
- outInfo.setInitialSurroundingText(state.text)
+ KeyboardType.Number -> this.inputType = InputType.TYPE_CLASS_NUMBER
+ KeyboardType.Phone -> this.inputType = InputType.TYPE_CLASS_PHONE
+ KeyboardType.Uri ->
+ this.inputType = InputType.TYPE_CLASS_TEXT or EditorInfo.TYPE_TEXT_VARIATION_URI
+ KeyboardType.Email ->
+ this.inputType =
+ InputType.TYPE_CLASS_TEXT or EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
+ KeyboardType.Password -> {
+ this.inputType =
+ InputType.TYPE_CLASS_TEXT or EditorInfo.TYPE_TEXT_VARIATION_PASSWORD
}
-
- outInfo.imeOptions = outInfo.imeOptions or EditorInfo.IME_FLAG_NO_FULLSCREEN
+ KeyboardType.NumberPassword -> {
+ this.inputType =
+ InputType.TYPE_CLASS_NUMBER or EditorInfo.TYPE_NUMBER_VARIATION_PASSWORD
+ }
+ else -> error("Invalid Keyboard Type")
}
- private fun hasFlag(bits: Int, flag: Int): Boolean = (bits and flag) == flag
-}
\ No newline at end of file
+ if (!imeOptions.singleLine) {
+ if (hasFlag(this.inputType, InputType.TYPE_CLASS_TEXT)) {
+ // TextView.java#setInputTypeSingleLine
+ this.inputType = this.inputType or InputType.TYPE_TEXT_FLAG_MULTI_LINE
+
+ if (imeOptions.imeAction == ImeAction.Default) {
+ this.imeOptions = this.imeOptions or EditorInfo.IME_FLAG_NO_ENTER_ACTION
+ }
+ }
+ }
+
+ if (hasFlag(this.inputType, InputType.TYPE_CLASS_TEXT)) {
+ when (imeOptions.capitalization) {
+ KeyboardCapitalization.None -> {
+ /* do nothing */
+ }
+ KeyboardCapitalization.Characters -> {
+ this.inputType = this.inputType or InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS
+ }
+ KeyboardCapitalization.Words -> {
+ this.inputType = this.inputType or InputType.TYPE_TEXT_FLAG_CAP_WORDS
+ }
+ KeyboardCapitalization.Sentences -> {
+ this.inputType = this.inputType or InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
+ }
+ }
+
+ if (imeOptions.autoCorrect) {
+ this.inputType = this.inputType or InputType.TYPE_TEXT_FLAG_AUTO_CORRECT
+ }
+ }
+
+ this.initialSelStart = textFieldValue.selection.start
+ this.initialSelEnd = textFieldValue.selection.end
+
+ EditorInfoCompat.setInitialSurroundingText(this, textFieldValue.text)
+
+ this.imeOptions = this.imeOptions or EditorInfo.IME_FLAG_NO_FULLSCREEN
+}
+
+private fun hasFlag(bits: Int, flag: Int): Boolean = (bits and flag) == flag
\ No newline at end of file
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DelegatingLayoutNodeWrapper.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DelegatingLayoutNodeWrapper.kt
index 387da83..b349ac6 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DelegatingLayoutNodeWrapper.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/DelegatingLayoutNodeWrapper.kt
@@ -36,9 +36,6 @@
override var wrapped: LayoutNodeWrapper,
open var modifier: T
) : LayoutNodeWrapper(wrapped.layoutNode) {
- override val providedAlignmentLines: Set<AlignmentLine>
- get() = wrapped.providedAlignmentLines
-
override val measureScope: MeasureScope get() = wrapped.measureScope
/**
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/InnerPlaceable.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/InnerPlaceable.kt
index a8b4970..162c404 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/InnerPlaceable.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/InnerPlaceable.kt
@@ -35,9 +35,6 @@
layoutNode: LayoutNode
) : LayoutNodeWrapper(layoutNode), Density by layoutNode.measureScope {
- override val providedAlignmentLines: Set<AlignmentLine>
- get() = layoutNode.providedAlignmentLines.keys
-
override val measureScope get() = layoutNode.measureScope
override fun measure(constraints: Constraints): Placeable = performingMeasure(constraints) {
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 394e52c..427f1fa 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
@@ -349,7 +349,7 @@
parent.invalidateLayer()
parent.requestRemeasure()
}
- alignmentLinesQueryOwner = null
+ alignmentLines.reset()
alignmentUsageByParent = UsageByParent.NotUsed
onDetach?.invoke(owner)
forEachDelegate { it.detach() }
@@ -494,15 +494,9 @@
override val height: Int get() = outerMeasurablePlaceable.height
/**
- * The alignment lines of this layout, inherited + intrinsic
+ * State corresponding to the alignment lines of this layout, inherited + intrinsic
*/
- internal var alignmentLines: LayoutNodeAlignmentLines? = null
- private set
-
- /**
- * The alignment lines provided by this layout at the last measurement
- */
- internal var providedAlignmentLines: Map<AlignmentLine, Int> = emptyMap()
+ internal var alignmentLines = LayoutNodeAlignmentLines(this)
internal val mDrawScope: LayoutNodeDrawScope = sharedDrawScope
@@ -532,36 +526,6 @@
*/
internal var measuredByParent: UsageByParent = UsageByParent.NotUsed
- /**
- * `true` while doing [calculateAlignmentLines]
- */
- private var isCalculatingAlignmentLines = false
-
- /**
- * `true` when the parent reads our alignment lines
- */
- private var alignmentLinesRead = false
-
- private var alignmentLinesCalculatedDuringLastLayout = false
-
- /**
- * `true` when an ancestor relies on our alignment lines
- */
- internal val alignmentLinesRequired
- get() = alignmentLinesQueryOwner != null && alignmentLinesQueryOwner!!.alignmentLinesRead
-
- /**
- * Used by the parent to identify if the child has been queried for alignment lines since
- * last measurement.
- */
- private var alignmentLinesQueriedSinceLastLayout = false
-
- /**
- * The closest layout node above in the hierarchy which asked for alignment lines.
- */
- internal var alignmentLinesQueryOwner: LayoutNode? = null
- private set
-
internal var alignmentUsageByParent = UsageByParent.NotUsed
@Deprecated("Temporary API to support ConstraintLayout prototyping.")
@@ -885,7 +849,9 @@
layoutChildren()
}
- private fun layoutChildren() {
+ internal fun layoutChildren() {
+ alignmentLines.recalculateQueryOwner()
+
if (layoutState == NeedsRelayout) {
onBeforeLayoutChildren()
}
@@ -900,16 +866,9 @@
_children.forEach { child ->
// and reset the place order for all the children before placing them
child.placeOrder = NotPlacedPlaceOrder
- if (alignmentLinesRequired && child.layoutState == Ready &&
- !child.alignmentLinesCalculatedDuringLastLayout
- ) {
- child.layoutState = NeedsRelayout
- }
- if (!child.alignmentLinesRequired) {
- child.alignmentLinesQueryOwner = alignmentLinesQueryOwner
- }
- child.alignmentLinesQueriedSinceLastLayout = false
+ child.alignmentLines.usedDuringParentLayout = false
}
+
innerLayoutNodeWrapper.measureResult.placeChildren()
_children.forEach { child ->
// we set `placeOrder` to NotPlacedPlaceOrder for all the children, then
@@ -921,20 +880,18 @@
// which is not placed anymore.
invalidateLayer()
}
- child.alignmentLinesRead = child.alignmentLinesQueriedSinceLastLayout
+ child.alignmentLines.previousUsedDuringParentLayout =
+ child.alignmentLines.usedDuringParentLayout
}
}
- alignmentLinesCalculatedDuringLastLayout = false
- if (alignmentLinesRequired) {
- alignmentLinesCalculatedDuringLastLayout = true
- val alignments = alignmentLines ?: LayoutNodeAlignmentLines(this).also {
- alignmentLines = it
- }
- alignments.recalculate()
- }
layoutState = Ready
}
+
+ if (alignmentLines.usedDuringParentLayout) {
+ alignmentLines.previousUsedDuringParentLayout = true
+ }
+ if (alignmentLines.dirty && alignmentLines.required) alignmentLines.recalculate()
}
private fun markSubtreeAsPlaced() {
@@ -991,56 +948,46 @@
}
internal fun onAlignmentsChanged() {
- val parent = parent
- if (parent != null) {
- if (alignmentUsageByParent == UsageByParent.InMeasureBlock &&
- parent.layoutState != LayingOut
- ) {
- parent.requestRemeasure()
- } else if (alignmentUsageByParent == UsageByParent.InLayoutBlock) {
- parent.requestRelayout()
- }
+ if (alignmentLines.dirty) return
+ alignmentLines.dirty = true
+
+ val parent = parent ?: return
+ if (alignmentLines.usedDuringParentMeasurement) {
+ parent.requestRemeasure()
+ } else if (alignmentLines.previousUsedDuringParentLayout) {
+ parent.requestRelayout()
}
+ if (alignmentLines.usedByModifierMeasurement) {
+ requestRemeasure()
+ }
+ if (alignmentLines.usedByModifierLayout) {
+ parent.requestRelayout()
+ }
+ parent.onAlignmentsChanged()
}
internal fun calculateAlignmentLines(): Map<AlignmentLine, Int> {
- isCalculatingAlignmentLines = true
- alignmentLinesRead = true
- alignmentLinesQueryOwner = this
- alignmentLinesQueriedSinceLastLayout = true
- val newUsageByParent = when (parent?.layoutState) {
- Measuring -> UsageByParent.InMeasureBlock
- LayingOut -> UsageByParent.InLayoutBlock
- else -> UsageByParent.NotUsed
+ if (!outerMeasurablePlaceable.duringAlignmentLinesQuery) {
+ alignmentLinesQueriedByModifier()
}
- val newUsageHasLowerPriority = newUsageByParent == UsageByParent.InLayoutBlock &&
- alignmentUsageByParent == UsageByParent.InMeasureBlock
- if (!newUsageHasLowerPriority) {
- alignmentUsageByParent = newUsageByParent
+ layoutChildren()
+ return alignmentLines.getLastCalculation()
+ }
+
+ private fun alignmentLinesQueriedByModifier() {
+ if (layoutState == Measuring) {
+ alignmentLines.usedByModifierMeasurement = true
+ // We quickly transition to NeedsRelayout as we need the alignment lines now.
+ // Later we will see that we also laid out as part of measurement and will skip layout.
+ if (alignmentLines.dirty) layoutState = NeedsRelayout
+ } else {
+ // Note this can also happen for onGloballyPositioned queries.
+ alignmentLines.usedByModifierLayout = true
}
- if (layoutState == NeedsRelayout || !alignmentLinesCalculatedDuringLastLayout) {
- // layoutChildren() is a state transformation from NeedsRelayout to Ready.
- // when we are already in NeedsRelayout we need to end up with Ready, but if we are
- // currently measuring or need remeasure this extra layoutChildren is just a side effect
- // and we will need to restore the current state.
- val endState = if (layoutState == Measuring || layoutState == NeedsRemeasure) {
- layoutState
- } else {
- Ready
- }
- if (!alignmentLinesCalculatedDuringLastLayout) {
- layoutState = NeedsRelayout
- }
- layoutChildren()
- layoutState = endState
- }
- isCalculatingAlignmentLines = false
- return alignmentLines?.getLastCalculation() ?: emptyMap()
}
internal fun handleMeasureResult(measureResult: MeasureResult) {
innerLayoutNodeWrapper.measureResult = measureResult
- providedAlignmentLines = measureResult.alignmentLines
}
/**
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeAlignmentLines.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeAlignmentLines.kt
index fba763a..3e1c26c 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeAlignmentLines.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeAlignmentLines.kt
@@ -26,57 +26,154 @@
private val layoutNode: LayoutNode
) {
/**
- * The alignment lines of this layout, inherited + intrinsic
+ * `true` when the alignment lines needs to be recalculated because they might have changed.
*/
- private val alignmentLines: MutableMap<AlignmentLine, Int> = hashMapOf()
+ internal var dirty = true
- private val previousAlignmentLines = mutableMapOf<AlignmentLine, Int>()
+ /**
+ * `true` when the alignment lines were used by the parent during measurement.
+ */
+ internal var usedDuringParentMeasurement = false
- fun getLastCalculation(): Map<AlignmentLine, Int> = alignmentLines
+ /**
+ * `true` when the alignment lines have been used by the parent during the current layout (or
+ * previous layout if there is no layout in progress).
+ */
+ internal var usedDuringParentLayout = false
+ /**
+ * `true` when the alignment lines were used by the parent during the last completed layout.
+ */
+ internal var previousUsedDuringParentLayout = false
- fun recalculate() {
- previousAlignmentLines.clear()
- previousAlignmentLines.putAll(alignmentLines)
- alignmentLines.clear()
- layoutNode._children.forEach { child ->
- val childAlignments = child.alignmentLines
- if (!child.isPlaced || childAlignments == null) return@forEach
- childAlignments.alignmentLines.keys.forEach { childLine ->
- val linePositionInContainer = childAlignments.getAlignmentLine(childLine)!!
- // If the line was already provided by a previous child, merge the values.
- alignmentLines[childLine] = if (childLine in alignmentLines) {
- childLine.merge(
- alignmentLines.getValue(childLine),
- linePositionInContainer
- )
- } else {
- linePositionInContainer
- }
+ /**
+ * `true` when the alignment lines were used by the modifier of the node during measurement.
+ */
+ internal var usedByModifierMeasurement = false
+
+ /**
+ * `true` when the alignment lines were used by the modifier of the node during measurement.
+ */
+ internal var usedByModifierLayout = false
+
+ /**
+ * `true` when the direct parent or our modifier relies on our alignment lines.
+ */
+ internal val queried get() = usedDuringParentMeasurement ||
+ previousUsedDuringParentLayout || usedByModifierMeasurement ||
+ usedByModifierLayout
+
+ /**
+ * The closest layout node ancestor who was asked for alignment lines, either by the parent or
+ * their own modifier. If the owner stops being queried for alignment lines, we have to
+ * [recalculateQueryOwner] to find the new owner if one exists.
+ */
+ private var queryOwner: LayoutNode? = null
+
+ /**
+ * Whether the alignment lines of this node are relevant (whether an ancestor depends on them).
+ */
+ internal val required: Boolean get() {
+ recalculateQueryOwner()
+ return queryOwner != null
+ }
+
+ /**
+ * Updates the alignment lines query owner according to the current values of the
+ * alignmentUsedBy* of the layout nodes in the hierarchy.
+ */
+ internal fun recalculateQueryOwner() {
+ queryOwner = if (queried) {
+ layoutNode
+ } else {
+ val parent = layoutNode.parent ?: return
+ val parentQueryOwner = parent.alignmentLines.queryOwner
+ if (parentQueryOwner != null && parentQueryOwner.alignmentLines.queried) {
+ parentQueryOwner
+ } else {
+ val owner = queryOwner
+ if (owner == null || owner.alignmentLines.queried) return
+ owner.parent?.alignmentLines?.recalculateQueryOwner()
+ owner.parent?.alignmentLines?.queryOwner
}
}
- alignmentLines += layoutNode.providedAlignmentLines
- if (previousAlignmentLines != alignmentLines) {
- layoutNode.onAlignmentsChanged()
- }
}
/**
- * Returns the alignment line value for a given alignment line without affecting whether
- * the flag for whether the alignment line was read.
+ * The alignment lines of this layout, inherited + intrinsic
*/
- private fun getAlignmentLine(alignmentLine: AlignmentLine): Int? {
- val linePos = alignmentLines[alignmentLine] ?: return null
- var pos = Offset(linePos.toFloat(), linePos.toFloat())
- var wrapper = layoutNode.innerLayoutNodeWrapper
- while (wrapper != layoutNode.outerLayoutNodeWrapper) {
- pos = wrapper.toParentPosition(pos)
- wrapper = wrapper.wrappedBy!!
+ private val alignmentLines: MutableMap<AlignmentLine, Int> = hashMapOf()
+
+ fun getLastCalculation(): Map<AlignmentLine, Int> = alignmentLines
+
+ fun recalculate() {
+ alignmentLines.clear()
+ /**
+ * Returns the alignment line value for a given alignment line without affecting whether
+ * the flag for whether the alignment line was read.
+ */
+ fun addAlignmentLine(
+ alignmentLine: AlignmentLine,
+ initialPosition: Int,
+ initialWrapper: LayoutNodeWrapper
+ ) {
+ var position = Offset(initialPosition.toFloat(), initialPosition.toFloat())
+ var wrapper = initialWrapper
+ while (true) {
+ position = wrapper.toParentPosition(position)
+ wrapper = wrapper.wrappedBy!!
+ if (wrapper == layoutNode.innerLayoutNodeWrapper) break
+ if (alignmentLine in wrapper.providedAlignmentLines) {
+ val newPosition = wrapper[alignmentLine]
+ position = Offset(newPosition.toFloat(), newPosition.toFloat())
+ }
+ }
+ val positionInContainer = if (alignmentLine is HorizontalAlignmentLine) {
+ position.y.roundToInt()
+ } else {
+ position.x.roundToInt()
+ }
+ // If the line was already provided by a previous child, merge the values.
+ alignmentLines[alignmentLine] = if (alignmentLine in alignmentLines) {
+ alignmentLine.merge(
+ alignmentLines.getValue(alignmentLine),
+ positionInContainer
+ )
+ } else {
+ positionInContainer
+ }
}
- pos = wrapper.toParentPosition(pos)
- return if (alignmentLine is HorizontalAlignmentLine) {
- pos.y.roundToInt()
- } else {
- pos.x.roundToInt()
+ layoutNode._children.forEach { child ->
+ if (!child.isPlaced) return@forEach
+ if (child.alignmentLines.dirty) {
+ // It did not need relayout, but we still call layout to recalculate
+ // alignment lines.
+ child.layoutChildren()
+ }
+ // Add alignment lines on the child node.
+ child.alignmentLines.alignmentLines.forEach { (childLine, linePosition) ->
+ addAlignmentLine(childLine, linePosition, child.innerLayoutNodeWrapper)
+ }
+
+ // Add alignment lines on the modifier of the child.
+ var wrapper = child.innerLayoutNodeWrapper.wrappedBy!!
+ while (wrapper != layoutNode.innerLayoutNodeWrapper) {
+ wrapper.providedAlignmentLines.forEach { childLine ->
+ addAlignmentLine(childLine, wrapper[childLine], wrapper)
+ }
+ wrapper = wrapper.wrappedBy!!
+ }
}
+ alignmentLines += layoutNode.innerLayoutNodeWrapper.measureResult.alignmentLines
+ dirty = false
+ }
+
+ internal fun reset() {
+ dirty = true
+ usedDuringParentMeasurement = false
+ previousUsedDuringParentLayout = false
+ usedDuringParentLayout = false
+ usedByModifierMeasurement = false
+ usedByModifierLayout = false
+ queryOwner = null
}
}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeWrapper.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeWrapper.kt
index b584d63..dde4d76 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeWrapper.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeWrapper.kt
@@ -88,9 +88,40 @@
if (old == null || value.width != old.width || value.height != old.height) {
onMeasureResultChanged(value.width, value.height)
}
+ // We do not simply compare against old.alignmentLines in case this is a
+ // MutableStateMap and the same instance might be passed.
+ if ((!oldAlignmentLines.isNullOrEmpty() || value.alignmentLines.isNotEmpty()) &&
+ value.alignmentLines != oldAlignmentLines
+ ) {
+ if (wrapped?.layoutNode == layoutNode) {
+ layoutNode.parent?.onAlignmentsChanged()
+ // We might need to request remeasure or relayout for the parent in
+ // case they ask for the lines so we are the query owner, without
+ // marking dirty our alignment lines (because only the modifier's changed).
+ if (layoutNode.alignmentLines.usedDuringParentMeasurement) {
+ layoutNode.parent?.requestRemeasure()
+ } else if (layoutNode.alignmentLines.usedDuringParentLayout) {
+ layoutNode.parent?.requestRelayout()
+ }
+ } else {
+ // It means we are an InnerPlaceable.
+ layoutNode.onAlignmentsChanged()
+ }
+ layoutNode.alignmentLines.dirty = true
+
+ val oldLines = oldAlignmentLines
+ ?: (mutableMapOf<AlignmentLine, Int>().also { oldAlignmentLines = it })
+ oldLines.clear()
+ oldLines.putAll(value.alignmentLines)
+ }
}
}
+ private var oldAlignmentLines: MutableMap<AlignmentLine, Int>? = null
+
+ override val providedAlignmentLines: Set<AlignmentLine>
+ get() = _measureResult?.alignmentLines?.keys ?: emptySet()
+
/**
* Called when the width or height of [measureResult] change. The object instance pointed to
* by [measureResult] may or may not have changed.
@@ -190,6 +221,11 @@
} else {
wrappedBy?.invalidateLayer()
}
+ if (wrapped?.layoutNode != layoutNode) {
+ layoutNode.onAlignmentsChanged()
+ } else {
+ layoutNode.parent?.onAlignmentsChanged()
+ }
layoutNode.owner?.onLayoutChange(layoutNode)
}
this.zIndex = zIndex
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutTreeConsistencyChecker.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutTreeConsistencyChecker.kt
index 289268a..337e2b0 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutTreeConsistencyChecker.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutTreeConsistencyChecker.kt
@@ -68,7 +68,8 @@
return relayoutNodes.contains(this) ||
parentLayoutState == LayoutNode.LayoutState.NeedsRemeasure ||
parentLayoutState == LayoutNode.LayoutState.NeedsRelayout ||
- parentLayoutState == LayoutNode.LayoutState.Measuring
+ parentLayoutState == LayoutNode.LayoutState.Measuring ||
+ parentLayoutState == LayoutNode.LayoutState.LayingOut
}
}
return true
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/MeasureAndLayoutDelegate.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/MeasureAndLayoutDelegate.kt
index 33e625a..02ec181 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/MeasureAndLayoutDelegate.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/MeasureAndLayoutDelegate.kt
@@ -23,7 +23,6 @@
import androidx.compose.ui.node.LayoutNode.LayoutState.Ready
import androidx.compose.ui.node.LayoutNode.UsageByParent.InLayoutBlock
import androidx.compose.ui.node.LayoutNode.UsageByParent.InMeasureBlock
-import androidx.compose.ui.node.LayoutNode.UsageByParent.NotUsed
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.util.fastForEach
@@ -200,13 +199,9 @@
duringMeasureLayout = true
try {
relayoutNodes.popEach { layoutNode ->
- val alignmentLinesOwner = layoutNode.alignmentLinesQueryOwner
if (layoutNode.isPlaced ||
layoutNode.canAffectParent ||
- (
- alignmentLinesOwner != null && alignmentLinesOwner
- .alignmentUsageByParent != NotUsed
- )
+ layoutNode.alignmentLines.required
) {
if (layoutNode.layoutState == NeedsRemeasure) {
if (doRemeasure(layoutNode, rootConstraints)) {
@@ -266,5 +261,5 @@
private val LayoutNode.canAffectParent
get() = layoutState == NeedsRemeasure &&
- (measuredByParent == InMeasureBlock || alignmentLinesQueryOwner != null)
+ (measuredByParent == InMeasureBlock || alignmentLines.required)
}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/OnGloballyPositionedModifierWrapper.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/OnGloballyPositionedModifierWrapper.kt
index 21a928e..9e4370a 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/OnGloballyPositionedModifierWrapper.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/OnGloballyPositionedModifierWrapper.kt
@@ -16,6 +16,7 @@
package androidx.compose.ui.node
+import androidx.compose.ui.layout.AlignmentLine
import androidx.compose.ui.layout.OnGloballyPositionedModifier
/**
@@ -24,4 +25,17 @@
internal class OnGloballyPositionedModifierWrapper(
wrapped: LayoutNodeWrapper,
modifier: OnGloballyPositionedModifier
-) : DelegatingLayoutNodeWrapper<OnGloballyPositionedModifier>(wrapped, modifier)
+) : DelegatingLayoutNodeWrapper<OnGloballyPositionedModifier>(wrapped, modifier) {
+ override val providedAlignmentLines: Set<AlignmentLine>
+ get() {
+ val result = mutableSetOf<AlignmentLine>()
+ layoutNode
+ var wrapper = wrapped as LayoutNodeWrapper?
+ while (wrapper != null) {
+ result += wrapper.providedAlignmentLines
+ if (wrapper == layoutNode.innerLayoutNodeWrapper) break
+ wrapper = wrapper.wrapped
+ }
+ return result
+ }
+}
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/OuterMeasurablePlaceable.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/OuterMeasurablePlaceable.kt
index eadbc1c..6ca6b5f 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/OuterMeasurablePlaceable.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/OuterMeasurablePlaceable.kt
@@ -36,9 +36,10 @@
check(measuredOnce)
return measurementConstraints
}
+ internal var duringAlignmentLinesQuery = false
+
private var lastPosition: IntOffset = IntOffset.Zero
private var lastLayerBlock: (GraphicsLayerScope.() -> Unit)? = null
- private var lastProvidedAlignmentLines: MutableMap<AlignmentLine, Int>? = null
private var lastZIndex: Float = 0f
/**
@@ -87,6 +88,8 @@
if (layoutNode.layoutState == LayoutState.NeedsRemeasure ||
measurementConstraints != constraints
) {
+ layoutNode.alignmentLines.usedByModifierMeasurement = false
+ layoutNode._children.forEach { it.alignmentLines.usedDuringParentMeasurement = false }
measuredOnce = true
layoutNode.layoutState = LayoutState.Measuring
measurementConstraints = constraints
@@ -94,8 +97,12 @@
owner.snapshotObserver.observeMeasureSnapshotReads(layoutNode) {
outerWrapper.measure(constraints)
}
- layoutNode.layoutState = LayoutState.NeedsRelayout
- notifyAlignmentChanges()
+ // The resulting layout state might be Ready. This can happen when the layout node's
+ // own modifier is querying an alignment line during measurement, therefore we
+ // need to also layout the layout node.
+ if (layoutNode.layoutState == LayoutState.Measuring) {
+ layoutNode.layoutState = LayoutState.NeedsRelayout
+ }
val sizeChanged = outerWrapper.size != outerWrapperPreviousMeasuredSize ||
outerWrapper.width != width ||
outerWrapper.height != height
@@ -106,26 +113,6 @@
return false
}
- private fun notifyAlignmentChanges() {
- // optimized to only create a lastProvidedAlignmentLines when we do have non empty map
- if (layoutNode.providedAlignmentLines.isNotEmpty()) {
- val previous = lastProvidedAlignmentLines ?: mutableMapOf<AlignmentLine, Int>().also {
- lastProvidedAlignmentLines = it
- }
- if (layoutNode.providedAlignmentLines != previous) {
- previous.clear()
- previous.putAll(layoutNode.providedAlignmentLines)
- layoutNode.onAlignmentsChanged()
- }
- } else {
- val previous = lastProvidedAlignmentLines
- if (previous != null && previous.isNotEmpty()) {
- previous.clear()
- layoutNode.onAlignmentsChanged()
- }
- }
- }
-
// We are setting our measuredSize to match the coerced outerWrapper size, to prevent
// double offseting for layout cooperation. However, this means that here we need
// to override these getters to make the measured values correct in Measured.
@@ -133,7 +120,17 @@
override val measuredWidth: Int get() = outerWrapper.measuredWidth
override val measuredHeight: Int get() = outerWrapper.measuredHeight
- override fun get(alignmentLine: AlignmentLine): Int = outerWrapper[alignmentLine]
+ override fun get(alignmentLine: AlignmentLine): Int {
+ if (layoutNode.parent?.layoutState == LayoutState.Measuring) {
+ layoutNode.alignmentLines.usedDuringParentMeasurement = true
+ } else if (layoutNode.parent?.layoutState == LayoutState.LayingOut) {
+ layoutNode.alignmentLines.usedDuringParentLayout = true
+ }
+ duringAlignmentLinesQuery = true
+ val result = outerWrapper[alignmentLine]
+ duringAlignmentLinesQuery = false
+ return result
+ }
override fun placeAt(
position: IntOffset,
@@ -144,6 +141,7 @@
lastPosition = position
lastZIndex = zIndex
lastLayerBlock = layerBlock
+ layoutNode.alignmentLines.usedByModifierLayout = false
with(PlacementScope) {
if (layerBlock == null) {
outerWrapper.place(position, lastZIndex)
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposeLayer.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposeLayer.desktop.kt
index 296411b..03dbbbc 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposeLayer.desktop.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposeLayer.desktop.kt
@@ -54,7 +54,12 @@
// method?
private val events = AWTDebounceEventQueue()
- internal val wrapped = Wrapped()
+ internal val wrapped = Wrapped().apply {
+ onStateChanged(SkiaLayer.PropertyKind.ContentScale) { _ ->
+ resetDensity()
+ }
+ }
+
internal val owners: DesktopOwners = DesktopOwners(
coroutineScope,
wrapped,
@@ -82,12 +87,7 @@
initOwner()
}
- override fun contentScaleChanged() {
- super.contentScaleChanged()
- resetDensity()
- }
-
- private fun resetDensity() {
+ internal fun resetDensity() {
this@ComposeLayer.density = detectCurrentDensity()
owner?.density = density
}
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposePanel.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposePanel.desktop.kt
index cfe712b..a29563d 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposePanel.desktop.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposePanel.desktop.kt
@@ -18,6 +18,7 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import org.jetbrains.skiko.ClipComponent
+import org.jetbrains.skiko.GraphicsApi
import java.awt.Color
import java.awt.Component
import javax.swing.JLayeredPane
@@ -112,4 +113,12 @@
layer!!.component.requestFocus()
}
}
+
+ /**
+ * Returns low-level rendering API used for rendering in this ComposeWindow. API is
+ * automatically selected based on operating system, graphical hardware and `SKIKO_RENDER_API`
+ * environment variable.
+ */
+ val renderApi: GraphicsApi
+ get() = if (layer != null) layer!!.component.renderApi else GraphicsApi.UNKNOWN
}
diff --git a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposeWindow.desktop.kt b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposeWindow.desktop.kt
index 273b3b0..9b865cf 100644
--- a/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposeWindow.desktop.kt
+++ b/compose/ui/ui/src/desktopMain/kotlin/androidx/compose/desktop/ComposeWindow.desktop.kt
@@ -20,6 +20,7 @@
import androidx.compose.runtime.CompositionLocalProvider
import org.jetbrains.skiko.ClipComponent
import org.jetbrains.skiko.GraphicsApi
+import org.jetbrains.skiko.SkiaLayer
import java.awt.Component
import javax.swing.JFrame
import javax.swing.JLayeredPane
@@ -98,15 +99,26 @@
}
/**
- * Retrieve underlying platform-specific operating system handle for the window where ComposeWindow is rendered.
- * Currently returns HWND on Windows, Drawable on X11 and 0 on macOS.
+ * Registers a task to run when the rendering API changes.
+ */
+ fun onRenderApiChanged(action: () -> Unit) {
+ layer.component.onStateChanged(SkiaLayer.PropertyKind.Renderer) {
+ action()
+ }
+ }
+
+ /**
+ * Retrieve underlying platform-specific operating system handle for the root window where
+ * ComposeWindow is rendered. Currently returns HWND on Windows, Display on X11 and NSWindow
+ * on macOS.
*/
val windowHandle: Long
get() = layer.component.windowHandle
/**
- * Returns low level rendering API used for rendering in this ComposeWindow. API is automatically selected based on
- * operating system, graphical hardware and `SKIKO_RENDER_API` environment variable.
+ * Returns low-level rendering API used for rendering in this ComposeWindow. API is
+ * automatically selected based on operating system, graphical hardware and `SKIKO_RENDER_API`
+ * environment variable.
*/
val renderApi: GraphicsApi
get() = layer.component.renderApi
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/input/RecordingInputConnectionUpdateTextFieldValueTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/input/RecordingInputConnectionUpdateTextFieldValueTest.kt
index 2721714..bb39d23 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/input/RecordingInputConnectionUpdateTextFieldValueTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/input/RecordingInputConnectionUpdateTextFieldValueTest.kt
@@ -19,9 +19,9 @@
import android.view.View
import android.view.inputmethod.ExtractedText
import android.view.inputmethod.InputConnection
-import android.view.inputmethod.InputMethodManager
import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.input.InputEventCallback2
+import androidx.compose.ui.text.input.InputMethodManager
import androidx.compose.ui.text.input.RecordingInputConnection
import androidx.compose.ui.text.input.TextFieldValue
import com.google.common.truth.Truth.assertThat
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/input/TextInputServiceAndroidTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/input/TextInputServiceAndroidTest.kt
deleted file mode 100644
index 48050b9..0000000
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/input/TextInputServiceAndroidTest.kt
+++ /dev/null
@@ -1,693 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.input
-
-import android.content.Context
-import android.text.InputType
-import android.view.View
-import android.view.inputmethod.EditorInfo
-import android.view.inputmethod.InputMethodManager
-import androidx.compose.ui.text.TextRange
-import androidx.compose.ui.text.input.ImeAction
-import androidx.compose.ui.text.input.ImeOptions
-import androidx.compose.ui.text.input.KeyboardCapitalization
-import androidx.compose.ui.text.input.KeyboardType
-import androidx.compose.ui.text.input.TextFieldValue
-import androidx.compose.ui.text.input.TextInputServiceAndroid
-import com.google.common.truth.Truth.assertThat
-import com.nhaarman.mockitokotlin2.eq
-import com.nhaarman.mockitokotlin2.mock
-import com.nhaarman.mockitokotlin2.whenever
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-
-@RunWith(JUnit4::class)
-class TextInputServiceAndroidTest {
-
- private lateinit var textInputService: TextInputServiceAndroid
- private lateinit var imm: InputMethodManager
-
- @Before
- fun setup() {
- imm = mock()
- val view: View = mock()
- val context: Context = mock()
- whenever(context.getSystemService(eq(Context.INPUT_METHOD_SERVICE))).thenReturn(imm)
- whenever(view.context).thenReturn(context)
- textInputService = TextInputServiceAndroid(view)
- }
-
- @Test
- fun test_fill_editor_info_text() {
- textInputService.startInput(
- value = TextFieldValue(""),
- imeOptions = ImeOptions(
- keyboardType = KeyboardType.Text,
- imeAction = ImeAction.Default
- ),
- onEditCommand = {},
- onImeActionPerformed = {}
- )
-
- EditorInfo().let { info ->
- textInputService.createInputConnection(info)
- assertThat((InputType.TYPE_CLASS_TEXT and info.inputType) != 0).isTrue()
- assertThat(
- (EditorInfo.IME_MASK_ACTION and info.imeOptions)
- == EditorInfo.IME_ACTION_UNSPECIFIED
- ).isTrue()
- }
- }
-
- @Test
- fun test_fill_editor_info_ascii() {
- textInputService.startInput(
- value = TextFieldValue(""),
- imeOptions = ImeOptions(
- keyboardType = KeyboardType.Ascii,
- imeAction = ImeAction.Default
- ),
- onEditCommand = {},
- onImeActionPerformed = {}
- )
-
- EditorInfo().let { info ->
- textInputService.createInputConnection(info)
- assertThat((InputType.TYPE_CLASS_TEXT and info.inputType) != 0).isTrue()
- assertThat((EditorInfo.IME_FLAG_FORCE_ASCII and info.imeOptions) != 0).isTrue()
- assertThat(
- (EditorInfo.IME_MASK_ACTION and info.imeOptions)
- == EditorInfo.IME_ACTION_UNSPECIFIED
- ).isTrue()
- }
- }
-
- @Test
- fun test_fill_editor_info_number() {
- textInputService.startInput(
- value = TextFieldValue(""),
- imeOptions = ImeOptions(
- keyboardType = KeyboardType.Number,
- imeAction = ImeAction.Default
- ),
- onEditCommand = {},
- onImeActionPerformed = {}
- )
-
- EditorInfo().let { info ->
- textInputService.createInputConnection(info)
- assertThat((InputType.TYPE_CLASS_NUMBER and info.inputType) != 0).isTrue()
- assertThat(
- (EditorInfo.IME_MASK_ACTION and info.imeOptions)
- == EditorInfo.IME_ACTION_UNSPECIFIED
- ).isTrue()
- }
- }
-
- @Test
- fun test_fill_editor_info_phone() {
- textInputService.startInput(
- value = TextFieldValue(""),
- imeOptions = ImeOptions(
- keyboardType = KeyboardType.Phone,
- imeAction = ImeAction.Default
- ),
- onEditCommand = {},
- onImeActionPerformed = {}
- )
-
- EditorInfo().let { info ->
- textInputService.createInputConnection(info)
- assertThat((InputType.TYPE_CLASS_PHONE and info.inputType) != 0).isTrue()
- assertThat(
- (EditorInfo.IME_MASK_ACTION and info.imeOptions)
- == EditorInfo.IME_ACTION_UNSPECIFIED
- ).isTrue()
- }
- }
-
- @Test
- fun test_fill_editor_info_uri() {
- textInputService.startInput(
- value = TextFieldValue(""),
- imeOptions = ImeOptions(
- keyboardType = KeyboardType.Uri,
- imeAction = ImeAction.Default
- ),
- onEditCommand = {},
- onImeActionPerformed = {}
- )
-
- EditorInfo().let { info ->
- textInputService.createInputConnection(info)
- assertThat((InputType.TYPE_CLASS_TEXT and info.inputType) != 0).isTrue()
- assertThat((InputType.TYPE_TEXT_VARIATION_URI and info.inputType) != 0).isTrue()
- assertThat(
- (EditorInfo.IME_MASK_ACTION and info.imeOptions)
- == EditorInfo.IME_ACTION_UNSPECIFIED
- ).isTrue()
- }
- }
-
- @Test
- fun test_fill_editor_info_email() {
- textInputService.startInput(
- value = TextFieldValue(""),
- imeOptions = ImeOptions(
- keyboardType = KeyboardType.Email,
- imeAction = ImeAction.Default
- ),
- onEditCommand = {},
- onImeActionPerformed = {}
- )
-
- EditorInfo().let { info ->
- textInputService.createInputConnection(info)
- assertThat((InputType.TYPE_CLASS_TEXT and info.inputType) != 0).isTrue()
- assertThat((InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS and info.inputType) != 0)
- .isTrue()
- assertThat(
- (EditorInfo.IME_MASK_ACTION and info.imeOptions)
- == EditorInfo.IME_ACTION_UNSPECIFIED
- ).isTrue()
- }
- }
-
- @Test
- fun test_fill_editor_info_password() {
- textInputService.startInput(
- value = TextFieldValue(""),
- imeOptions = ImeOptions(
- keyboardType = KeyboardType.Password,
- imeAction = ImeAction.Default
- ),
- onEditCommand = {},
- onImeActionPerformed = {}
- )
-
- EditorInfo().let { info ->
- textInputService.createInputConnection(info)
- assertThat((InputType.TYPE_CLASS_TEXT and info.inputType) != 0).isTrue()
- assertThat((InputType.TYPE_TEXT_VARIATION_PASSWORD and info.inputType) != 0).isTrue()
- assertThat(
- (EditorInfo.IME_MASK_ACTION and info.imeOptions)
- == EditorInfo.IME_ACTION_UNSPECIFIED
- ).isTrue()
- }
- }
-
- @Test
- fun test_fill_editor_info_number_password() {
- textInputService.startInput(
- value = TextFieldValue(""),
- imeOptions = ImeOptions(
- keyboardType = KeyboardType.NumberPassword,
- imeAction = ImeAction.Default
- ),
- onEditCommand = {},
- onImeActionPerformed = {}
- )
-
- EditorInfo().let { info ->
- textInputService.createInputConnection(info)
- assertThat((InputType.TYPE_CLASS_NUMBER and info.inputType) != 0).isTrue()
- assertThat((InputType.TYPE_NUMBER_VARIATION_PASSWORD and info.inputType) != 0).isTrue()
- assertThat(
- (EditorInfo.IME_MASK_ACTION and info.imeOptions)
- == EditorInfo.IME_ACTION_UNSPECIFIED
- ).isTrue()
- }
- }
-
- @Test
- fun test_fill_editor_info_action_none() {
- textInputService.startInput(
- value = TextFieldValue(""),
- imeOptions = ImeOptions(
- keyboardType = KeyboardType.Ascii,
- imeAction = ImeAction.None
- ),
- onEditCommand = {},
- onImeActionPerformed = {}
- )
-
- EditorInfo().let { info ->
- textInputService.createInputConnection(info)
- assertThat((InputType.TYPE_CLASS_TEXT and info.inputType) != 0).isTrue()
- assertThat((EditorInfo.IME_FLAG_FORCE_ASCII and info.imeOptions) != 0).isTrue()
- assertThat(
- (EditorInfo.IME_MASK_ACTION and info.imeOptions)
- == EditorInfo.IME_ACTION_NONE
- ).isTrue()
- }
- }
-
- @Test
- fun test_fill_editor_info_action_go() {
- textInputService.startInput(
- value = TextFieldValue(""),
- imeOptions = ImeOptions(
- keyboardType = KeyboardType.Ascii,
- imeAction = ImeAction.Go
- ),
- onEditCommand = {},
- onImeActionPerformed = {}
- )
-
- EditorInfo().let { info ->
- textInputService.createInputConnection(info)
- assertThat((InputType.TYPE_CLASS_TEXT and info.inputType) != 0).isTrue()
- assertThat((EditorInfo.IME_FLAG_FORCE_ASCII and info.imeOptions) != 0).isTrue()
- assertThat(
- (EditorInfo.IME_MASK_ACTION and info.imeOptions)
- == EditorInfo.IME_ACTION_GO
- ).isTrue()
- }
- }
-
- @Test
- fun test_fill_editor_info_action_next() {
- textInputService.startInput(
- value = TextFieldValue(""),
- imeOptions = ImeOptions(
- keyboardType = KeyboardType.Ascii,
- imeAction = ImeAction.Next
- ),
- onEditCommand = {},
- onImeActionPerformed = {}
- )
-
- EditorInfo().let { info ->
- textInputService.createInputConnection(info)
- assertThat((InputType.TYPE_CLASS_TEXT and info.inputType) != 0).isTrue()
- assertThat((EditorInfo.IME_FLAG_FORCE_ASCII and info.imeOptions) != 0).isTrue()
- assertThat(
- (EditorInfo.IME_MASK_ACTION and info.imeOptions)
- == EditorInfo.IME_ACTION_NEXT
- ).isTrue()
- }
- }
-
- @Test
- fun test_fill_editor_info_action_previous() {
- textInputService.startInput(
- value = TextFieldValue(""),
- imeOptions = ImeOptions(
- keyboardType = KeyboardType.Ascii,
- imeAction = ImeAction.Previous
- ),
- onEditCommand = {},
- onImeActionPerformed = {}
- )
-
- EditorInfo().let { info ->
- textInputService.createInputConnection(info)
- assertThat((InputType.TYPE_CLASS_TEXT and info.inputType) != 0).isTrue()
- assertThat((EditorInfo.IME_FLAG_FORCE_ASCII and info.imeOptions) != 0).isTrue()
- assertThat(
- (EditorInfo.IME_MASK_ACTION and info.imeOptions)
- == EditorInfo.IME_ACTION_PREVIOUS
- ).isTrue()
- }
- }
-
- @Test
- fun test_fill_editor_info_action_search() {
- textInputService.startInput(
- value = TextFieldValue(""),
- imeOptions = ImeOptions(
- keyboardType = KeyboardType.Ascii,
- imeAction = ImeAction.Search,
- ),
- onEditCommand = {},
- onImeActionPerformed = {}
- )
-
- EditorInfo().let { info ->
- textInputService.createInputConnection(info)
- assertThat((InputType.TYPE_CLASS_TEXT and info.inputType) != 0).isTrue()
- assertThat((EditorInfo.IME_FLAG_FORCE_ASCII and info.imeOptions) != 0).isTrue()
- assertThat(
- (EditorInfo.IME_MASK_ACTION and info.imeOptions)
- == EditorInfo.IME_ACTION_SEARCH
- ).isTrue()
- }
- }
-
- @Test
- fun test_fill_editor_info_action_send() {
- textInputService.startInput(
- value = TextFieldValue(""),
- imeOptions = ImeOptions(
- keyboardType = KeyboardType.Ascii,
- imeAction = ImeAction.Send
- ),
- onEditCommand = {},
- onImeActionPerformed = {}
- )
-
- EditorInfo().let { info ->
- textInputService.createInputConnection(info)
- assertThat((InputType.TYPE_CLASS_TEXT and info.inputType) != 0).isTrue()
- assertThat((EditorInfo.IME_FLAG_FORCE_ASCII and info.imeOptions) != 0).isTrue()
- assertThat(
- (EditorInfo.IME_MASK_ACTION and info.imeOptions)
- == EditorInfo.IME_ACTION_SEND
- ).isTrue()
- }
- }
-
- @Test
- fun test_fill_editor_info_action_done() {
- textInputService.startInput(
- value = TextFieldValue(""),
- imeOptions = ImeOptions(
- keyboardType = KeyboardType.Ascii,
- imeAction = ImeAction.Done
- ),
- onEditCommand = {},
- onImeActionPerformed = {}
- )
-
- EditorInfo().let { info ->
- textInputService.createInputConnection(info)
- assertThat((InputType.TYPE_CLASS_TEXT and info.inputType) != 0).isTrue()
- assertThat((EditorInfo.IME_FLAG_FORCE_ASCII and info.imeOptions) != 0).isTrue()
- assertThat(
- (EditorInfo.IME_MASK_ACTION and info.imeOptions)
- == EditorInfo.IME_ACTION_DONE
- ).isTrue()
- }
- }
-
- @Test
- fun test_fill_editor_info_multi_line() {
- textInputService.startInput(
- value = TextFieldValue(""),
- imeOptions = ImeOptions(
- singleLine = false,
- keyboardType = KeyboardType.Ascii,
- imeAction = ImeAction.Done
- ),
- onEditCommand = {},
- onImeActionPerformed = {}
- )
-
- EditorInfo().let { info ->
- textInputService.createInputConnection(info)
- assertThat((InputType.TYPE_TEXT_FLAG_MULTI_LINE and info.inputType) == 0).isFalse()
- assertThat((EditorInfo.IME_FLAG_NO_ENTER_ACTION and info.imeOptions) == 0).isTrue()
- }
- }
-
- @Test
- fun test_fill_editor_info_multi_line_with_default_action() {
- textInputService.startInput(
- value = TextFieldValue(""),
- imeOptions = ImeOptions(
- singleLine = false,
- keyboardType = KeyboardType.Text,
- imeAction = ImeAction.Default
- ),
- onEditCommand = {},
- onImeActionPerformed = {}
- )
-
- EditorInfo().let { info ->
- textInputService.createInputConnection(info)
- assertThat((InputType.TYPE_TEXT_FLAG_MULTI_LINE and info.inputType) == 0).isFalse()
- assertThat((EditorInfo.IME_FLAG_NO_ENTER_ACTION and info.imeOptions) == 0).isFalse()
- }
- }
-
- @Test
- fun test_fill_editor_info_single_line() {
- textInputService.startInput(
- value = TextFieldValue(""),
- imeOptions = ImeOptions(
- singleLine = true,
- keyboardType = KeyboardType.Ascii,
- imeAction = ImeAction.Done
- ),
- onEditCommand = {},
- onImeActionPerformed = {}
- )
-
- EditorInfo().let { info ->
- textInputService.createInputConnection(info)
- assertThat((InputType.TYPE_TEXT_FLAG_MULTI_LINE and info.inputType) == 0).isTrue()
- assertThat((EditorInfo.IME_FLAG_NO_ENTER_ACTION and info.imeOptions) == 0).isTrue()
- }
- }
-
- @Test
- fun test_fill_editor_info_single_line_changes_ime_from_unspecified_to_done() {
- textInputService.startInput(
- value = TextFieldValue(""),
- imeOptions = ImeOptions(
- singleLine = true,
- keyboardType = KeyboardType.Text,
- imeAction = ImeAction.Default
- ),
- onEditCommand = {},
- onImeActionPerformed = {}
- )
-
- EditorInfo().let { info ->
- textInputService.createInputConnection(info)
- assertThat((EditorInfo.IME_ACTION_DONE and info.imeOptions) == 0).isFalse()
- assertThat((EditorInfo.IME_ACTION_UNSPECIFIED and info.imeOptions) == 0).isTrue()
- }
- }
-
- @Test
- fun test_fill_editor_info_multi_line_not_set_when_input_type_is_not_text() {
- textInputService.startInput(
- value = TextFieldValue(""),
- imeOptions = ImeOptions(
- singleLine = false,
- keyboardType = KeyboardType.Number,
- imeAction = ImeAction.Done
- ),
- onEditCommand = {},
- onImeActionPerformed = {}
- )
-
- EditorInfo().let { info ->
- textInputService.createInputConnection(info)
- assertThat((InputType.TYPE_TEXT_FLAG_MULTI_LINE and info.inputType) == 0).isTrue()
- assertThat((EditorInfo.IME_FLAG_NO_ENTER_ACTION and info.imeOptions) == 0).isTrue()
- }
- }
-
- @Test
- fun test_fill_editor_info_capitalization_none() {
- textInputService.startInput(
- value = TextFieldValue(""),
- imeOptions = ImeOptions(
- capitalization = KeyboardCapitalization.None,
- keyboardType = KeyboardType.Ascii,
- imeAction = ImeAction.Done
- ),
- onEditCommand = {},
- onImeActionPerformed = {}
- )
-
- EditorInfo().let { info ->
- textInputService.createInputConnection(info)
- assertThat((InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS and info.inputType) == 0).isTrue()
- assertThat((InputType.TYPE_TEXT_FLAG_CAP_WORDS and info.inputType) == 0).isTrue()
- assertThat((InputType.TYPE_TEXT_FLAG_CAP_SENTENCES and info.inputType) == 0).isTrue()
- }
- }
-
- @Test
- fun test_fill_editor_info_capitalization_characters() {
- textInputService.startInput(
- value = TextFieldValue(""),
- imeOptions = ImeOptions(
- capitalization = KeyboardCapitalization.Characters,
- keyboardType = KeyboardType.Ascii,
- imeAction = ImeAction.Done
- ),
- onEditCommand = {},
- onImeActionPerformed = {}
- )
-
- EditorInfo().let { info ->
- textInputService.createInputConnection(info)
- assertThat((InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS and info.inputType) == 0).isFalse()
- assertThat((InputType.TYPE_TEXT_FLAG_CAP_WORDS and info.inputType) == 0).isTrue()
- assertThat((InputType.TYPE_TEXT_FLAG_CAP_SENTENCES and info.inputType) == 0).isTrue()
- }
- }
-
- @Test
- fun test_fill_editor_info_capitalization_words() {
- textInputService.startInput(
- value = TextFieldValue(""),
- imeOptions = ImeOptions(
- capitalization = KeyboardCapitalization.Words,
- keyboardType = KeyboardType.Ascii,
- imeAction = ImeAction.Done
- ),
- onEditCommand = {},
- onImeActionPerformed = {}
- )
-
- EditorInfo().let { info ->
- textInputService.createInputConnection(info)
- assertThat((InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS and info.inputType) == 0).isTrue()
- assertThat((InputType.TYPE_TEXT_FLAG_CAP_WORDS and info.inputType) == 0).isFalse()
- assertThat((InputType.TYPE_TEXT_FLAG_CAP_SENTENCES and info.inputType) == 0).isTrue()
- }
- }
-
- @Test
- fun test_fill_editor_info_capitalization_sentences() {
- textInputService.startInput(
- value = TextFieldValue(""),
- imeOptions = ImeOptions(
- capitalization = KeyboardCapitalization.Sentences,
- keyboardType = KeyboardType.Ascii,
- imeAction = ImeAction.Done,
- ),
- onEditCommand = {},
- onImeActionPerformed = {}
- )
-
- EditorInfo().let { info ->
- textInputService.createInputConnection(info)
- assertThat((InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS and info.inputType) == 0).isTrue()
- assertThat((InputType.TYPE_TEXT_FLAG_CAP_WORDS and info.inputType) == 0).isTrue()
- assertThat((InputType.TYPE_TEXT_FLAG_CAP_SENTENCES and info.inputType) == 0).isFalse()
- }
- }
-
- @Test
- fun test_fill_editor_info_capitalization_not_added_when_input_type_is_not_text() {
- textInputService.startInput(
- value = TextFieldValue(""),
- imeOptions = ImeOptions(
- capitalization = KeyboardCapitalization.Sentences,
- keyboardType = KeyboardType.Number,
- imeAction = ImeAction.Done,
- ),
- onEditCommand = {},
- onImeActionPerformed = {}
- )
-
- EditorInfo().let { info ->
- textInputService.createInputConnection(info)
- assertThat((InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS and info.inputType) == 0).isTrue()
- assertThat((InputType.TYPE_TEXT_FLAG_CAP_WORDS and info.inputType) == 0).isTrue()
- assertThat((InputType.TYPE_TEXT_FLAG_CAP_SENTENCES and info.inputType) == 0).isTrue()
- }
- }
-
- @Test
- fun test_fill_editor_info_auto_correct_on() {
- textInputService.startInput(
- value = TextFieldValue(""),
- imeOptions = ImeOptions(
- autoCorrect = true,
- keyboardType = KeyboardType.Ascii,
- imeAction = ImeAction.Done
- ),
- onEditCommand = {},
- onImeActionPerformed = {}
- )
-
- EditorInfo().let { info ->
- textInputService.createInputConnection(info)
- assertThat((InputType.TYPE_TEXT_FLAG_AUTO_CORRECT and info.inputType) == 0).isFalse()
- }
- }
-
- @Test
- fun test_fill_editor_info_auto_correct_off() {
- textInputService.startInput(
- value = TextFieldValue(""),
- imeOptions = ImeOptions(
- autoCorrect = false,
- keyboardType = KeyboardType.Ascii,
- imeAction = ImeAction.Done
- ),
- onEditCommand = {},
- onImeActionPerformed = {}
- )
-
- EditorInfo().let { info ->
- textInputService.createInputConnection(info)
- assertThat((InputType.TYPE_TEXT_FLAG_AUTO_CORRECT and info.inputType) == 0).isTrue()
- }
- }
-
- @Test
- fun autocorrect_not_added_when_input_type_is_not_text() {
- textInputService.startInput(
- value = TextFieldValue(""),
- imeOptions = ImeOptions(
- autoCorrect = true,
- keyboardType = KeyboardType.Number,
- imeAction = ImeAction.Done
- ),
- onEditCommand = {},
- onImeActionPerformed = {}
- )
-
- EditorInfo().let { info ->
- textInputService.createInputConnection(info)
- assertThat((InputType.TYPE_TEXT_FLAG_AUTO_CORRECT and info.inputType) == 0).isTrue()
- }
- }
-
- @Test
- fun initial_default_selection_info_is_set() {
- textInputService.startInput(
- value = TextFieldValue(),
- imeOptions = ImeOptions.Default,
- onEditCommand = {},
- onImeActionPerformed = {}
- )
-
- EditorInfo().let { info ->
- textInputService.createInputConnection(info)
- assertThat(info.initialSelStart).isEqualTo(0)
- assertThat(info.initialSelEnd).isEqualTo(0)
- }
- }
-
- @Test
- fun initial_selection_info_is_set() {
- val selection = TextRange(1, 2)
- textInputService.startInput(
- value = TextFieldValue("abc", selection),
- imeOptions = ImeOptions.Default,
- onEditCommand = {},
- onImeActionPerformed = {}
- )
-
- EditorInfo().let { info ->
- textInputService.createInputConnection(info)
- assertThat(info.initialSelStart).isEqualTo(selection.start)
- assertThat(info.initialSelEnd).isEqualTo(selection.end)
- }
- }
-}
\ No newline at end of file
diff --git a/fragment/fragment-lint/build.gradle b/fragment/fragment-lint/build.gradle
index 446b4be..da2c726 100644
--- a/fragment/fragment-lint/build.gradle
+++ b/fragment/fragment-lint/build.gradle
@@ -25,13 +25,7 @@
}
dependencies {
- // compileOnly because we use lintChecks and it doesn't allow other types of deps
- // this ugly hack exists because of b/63873667
- if (rootProject.hasProperty("android.injected.invoked.from.ide")) {
- compileOnly(LINT_API_LATEST)
- } else {
- compileOnly(LINT_API_MIN)
- }
+ compileOnly(LINT_API_MIN)
compileOnly(KOTLIN_STDLIB)
testImplementation(KOTLIN_STDLIB)
diff --git a/fragment/fragment-testing-lint/build.gradle b/fragment/fragment-testing-lint/build.gradle
index e8edd4f..9242788 100644
--- a/fragment/fragment-testing-lint/build.gradle
+++ b/fragment/fragment-testing-lint/build.gradle
@@ -25,13 +25,7 @@
}
dependencies {
- // compileOnly because we use lintChecks and it doesn't allow other types of deps
- // this ugly hack exists because of b/63873667
- if (rootProject.hasProperty("android.injected.invoked.from.ide")) {
- compileOnly(LINT_API_LATEST)
- } else {
- compileOnly(LINT_API_MIN)
- }
+ compileOnly(LINT_API_MIN)
compileOnly(KOTLIN_STDLIB)
testImplementation(KOTLIN_STDLIB)
diff --git a/fragment/fragment/api/current.txt b/fragment/fragment/api/current.txt
index 83ddaab..0ab03c8 100644
--- a/fragment/fragment/api/current.txt
+++ b/fragment/fragment/api/current.txt
@@ -476,6 +476,7 @@
public static final class FragmentStrictMode.Policy.Builder {
ctor public FragmentStrictMode.Policy.Builder();
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder allowViolation(Class<? extends androidx.fragment.app.Fragment>, Class<? extends androidx.fragment.app.strictmode.Violation>);
method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy build();
method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectFragmentReuse();
method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectFragmentTagUsage();
diff --git a/fragment/fragment/api/public_plus_experimental_current.txt b/fragment/fragment/api/public_plus_experimental_current.txt
index 6cb05d1..dcbd3b6 100644
--- a/fragment/fragment/api/public_plus_experimental_current.txt
+++ b/fragment/fragment/api/public_plus_experimental_current.txt
@@ -482,6 +482,7 @@
public static final class FragmentStrictMode.Policy.Builder {
ctor public FragmentStrictMode.Policy.Builder();
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder allowViolation(Class<? extends androidx.fragment.app.Fragment>, Class<? extends androidx.fragment.app.strictmode.Violation>);
method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy build();
method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectFragmentReuse();
method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectFragmentTagUsage();
diff --git a/fragment/fragment/api/restricted_current.txt b/fragment/fragment/api/restricted_current.txt
index 680d7b8..9fe9aaa 100644
--- a/fragment/fragment/api/restricted_current.txt
+++ b/fragment/fragment/api/restricted_current.txt
@@ -512,6 +512,7 @@
public static final class FragmentStrictMode.Policy.Builder {
ctor public FragmentStrictMode.Policy.Builder();
+ method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder allowViolation(Class<? extends androidx.fragment.app.Fragment>, Class<? extends androidx.fragment.app.strictmode.Violation>);
method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy build();
method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectFragmentReuse();
method public androidx.fragment.app.strictmode.FragmentStrictMode.Policy.Builder detectFragmentTagUsage();
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/strictmode/FragmentStrictModeTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/strictmode/FragmentStrictModeTest.kt
index 87b0c07..44e58d5 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/strictmode/FragmentStrictModeTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/strictmode/FragmentStrictModeTest.kt
@@ -36,6 +36,8 @@
@MediumTest
@RunWith(AndroidJUnit4::class)
public class FragmentStrictModeTest {
+ private val fragmentClass = StrictFragment::class.java
+
private lateinit var originalPolicy: FragmentStrictMode.Policy
@Before
@@ -293,4 +295,38 @@
assertThat(violation).isInstanceOf(WrongFragmentContainerViolation::class.java)
}
}
+
+ @Test
+ public fun detectAllowedViolations() {
+ val violationClass1 = RetainInstanceUsageViolation::class.java
+ val violationClass2 = SetUserVisibleHintViolation::class.java
+ val violationClassList = listOf(violationClass1, violationClass2)
+
+ var violation: Violation? = null
+ var policyBuilder = FragmentStrictMode.Policy.Builder()
+ .detectRetainInstanceUsage()
+ .detectSetUserVisibleHint()
+ .penaltyListener { violation = it }
+ for (violationClass in violationClassList) {
+ policyBuilder = policyBuilder.allowViolation(fragmentClass, violationClass)
+ }
+ FragmentStrictMode.setDefaultPolicy(policyBuilder.build())
+
+ @Suppress("DEPRECATION")
+ StrictFragment().retainInstance = true
+ assertThat(violation).isNotInstanceOf(violationClass1)
+ assertThat(violation).isNotInstanceOf(violationClass2)
+
+ violation = null
+ @Suppress("DEPRECATION")
+ StrictFragment().retainInstance
+ assertThat(violation).isNotInstanceOf(violationClass1)
+ assertThat(violation).isNotInstanceOf(violationClass2)
+
+ violation = null
+ @Suppress("DEPRECATION")
+ StrictFragment().userVisibleHint = true
+ assertThat(violation).isNotInstanceOf(violationClass1)
+ assertThat(violation).isNotInstanceOf(violationClass2)
+ }
}
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
index 1c7ab0e..9f04df6 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
@@ -93,7 +93,10 @@
*/
public abstract class FragmentManager implements FragmentResultOwner {
private static boolean DEBUG = false;
- static final String TAG = "FragmentManager";
+
+ /** @hide */
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ public static final String TAG = "FragmentManager";
/**
* Control whether the framework's internal fragment manager debugging
@@ -108,7 +111,9 @@
FragmentManager.DEBUG = enabled;
}
- static boolean isLoggingEnabled(int level) {
+ /** @hide */
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ public static boolean isLoggingEnabled(int level) {
return DEBUG || Log.isLoggable(TAG, level);
}
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/FragmentStrictMode.java b/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/FragmentStrictMode.java
index 65ad631..4c4368a 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/FragmentStrictMode.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/strictmode/FragmentStrictMode.java
@@ -29,8 +29,9 @@
import androidx.fragment.app.FragmentContainerView;
import androidx.fragment.app.FragmentManager;
-import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
+import java.util.Map;
import java.util.Set;
/**
@@ -84,16 +85,30 @@
* different penalties for different detected actions.
*/
public static final class Policy {
- private final Set<Flag> flags;
- private final OnViolationListener listener;
+ private final Set<Flag> mFlags;
+ private final OnViolationListener mListener;
+ private final Map<Class<? extends Fragment>,
+ Set<Class<? extends Violation>>> mAllowedViolations;
/** The default, lax policy which doesn't catch anything. */
@NonNull
- public static final Policy LAX = new Policy(new HashSet<Flag>(), null);
+ public static final Policy LAX = new Policy(new HashSet<>(), null, new HashMap<>());
- private Policy(@NonNull Set<Flag> flags, @Nullable OnViolationListener listener) {
- this.flags = Collections.unmodifiableSet(flags);
- this.listener = listener;
+ private Policy(
+ @NonNull Set<Flag> flags,
+ @Nullable OnViolationListener listener,
+ @NonNull Map<Class<? extends Fragment>,
+ Set<Class<? extends Violation>>> allowedViolations) {
+ this.mFlags = new HashSet<>(flags);
+ this.mListener = listener;
+
+ Map<Class<? extends Fragment>, Set<Class<? extends Violation>>>
+ newAllowedViolationsMap = new HashMap<>();
+ for (Map.Entry<Class<? extends Fragment>,
+ Set<Class<? extends Violation>>> entry : allowedViolations.entrySet()) {
+ newAllowedViolationsMap.put(entry.getKey(), new HashSet<>(entry.getValue()));
+ }
+ this.mAllowedViolations = newAllowedViolationsMap;
}
/**
@@ -105,19 +120,22 @@
* order is insignificant: all penalties apply to all detected problems.
*/
public static final class Builder {
- private final Set<Flag> flags;
- private OnViolationListener listener;
+ private final Set<Flag> mFlags;
+ private OnViolationListener mListener;
+ private final Map<Class<? extends Fragment>,
+ Set<Class<? extends Violation>>> mAllowedViolations;
/** Create a Builder that detects nothing and has no violations. */
public Builder() {
- flags = new HashSet<>();
+ mFlags = new HashSet<>();
+ mAllowedViolations = new HashMap<>();
}
/** Log detected violations to the system log. */
@NonNull
@SuppressLint("BuilderSetStyle")
public Builder penaltyLog() {
- flags.add(Flag.PENALTY_LOG);
+ mFlags.add(Flag.PENALTY_LOG);
return this;
}
@@ -129,7 +147,7 @@
@NonNull
@SuppressLint("BuilderSetStyle")
public Builder penaltyDeath() {
- flags.add(Flag.PENALTY_DEATH);
+ mFlags.add(Flag.PENALTY_DEATH);
return this;
}
@@ -140,7 +158,7 @@
@NonNull
@SuppressLint("BuilderSetStyle")
public Builder penaltyListener(@NonNull OnViolationListener listener) {
- this.listener = listener;
+ this.mListener = listener;
return this;
}
@@ -151,7 +169,7 @@
@NonNull
@SuppressLint("BuilderSetStyle")
public Builder detectFragmentReuse() {
- flags.add(Flag.DETECT_FRAGMENT_REUSE);
+ mFlags.add(Flag.DETECT_FRAGMENT_REUSE);
return this;
}
@@ -159,7 +177,7 @@
@NonNull
@SuppressLint("BuilderSetStyle")
public Builder detectFragmentTagUsage() {
- flags.add(Flag.DETECT_FRAGMENT_TAG_USAGE);
+ mFlags.add(Flag.DETECT_FRAGMENT_TAG_USAGE);
return this;
}
@@ -170,7 +188,7 @@
@NonNull
@SuppressLint("BuilderSetStyle")
public Builder detectRetainInstanceUsage() {
- flags.add(Flag.DETECT_RETAIN_INSTANCE_USAGE);
+ mFlags.add(Flag.DETECT_RETAIN_INSTANCE_USAGE);
return this;
}
@@ -178,7 +196,7 @@
@NonNull
@SuppressLint("BuilderSetStyle")
public Builder detectSetUserVisibleHint() {
- flags.add(Flag.DETECT_SET_USER_VISIBLE_HINT);
+ mFlags.add(Flag.DETECT_SET_USER_VISIBLE_HINT);
return this;
}
@@ -189,7 +207,7 @@
@NonNull
@SuppressLint("BuilderSetStyle")
public Builder detectTargetFragmentUsage() {
- flags.add(Flag.DETECT_TARGET_FRAGMENT_USAGE);
+ mFlags.add(Flag.DETECT_TARGET_FRAGMENT_USAGE);
return this;
}
@@ -200,7 +218,29 @@
@NonNull
@SuppressLint("BuilderSetStyle")
public Builder detectWrongFragmentContainer() {
- flags.add(Flag.DETECT_WRONG_FRAGMENT_CONTAINER);
+ mFlags.add(Flag.DETECT_WRONG_FRAGMENT_CONTAINER);
+ return this;
+ }
+
+ /**
+ * Allow the specified {@link Fragment} class to bypass penalties for the
+ * specified {@link Violation}, if detected.
+ *
+ * By default, all {@link Fragment} classes will incur penalties for any
+ * detected {@link Violation}.
+ */
+ @NonNull
+ @SuppressLint("BuilderSetStyle")
+ public Builder allowViolation(
+ @NonNull Class<? extends Fragment> fragmentClass,
+ @NonNull Class<? extends Violation> violationClass) {
+ Set<Class<? extends Violation>> violationsToBypass =
+ mAllowedViolations.get(fragmentClass);
+ if (violationsToBypass == null) {
+ violationsToBypass = new HashSet<>();
+ }
+ violationsToBypass.add(violationClass);
+ mAllowedViolations.put(fragmentClass, violationsToBypass);
return this;
}
@@ -212,10 +252,10 @@
*/
@NonNull
public Policy build() {
- if (listener == null && !flags.contains(Flag.PENALTY_DEATH)) {
+ if (mListener == null && !mFlags.contains(Flag.PENALTY_DEATH)) {
penaltyLog();
}
- return new Policy(flags, listener);
+ return new Policy(mFlags, mListener, mAllowedViolations);
}
}
}
@@ -251,56 +291,109 @@
@RestrictTo(RestrictTo.Scope.LIBRARY)
public static void onFragmentReuse(@NonNull Fragment fragment) {
+ Violation violation = new FragmentReuseViolation();
+ logIfDebuggingEnabled(fragment.getClass().getName(), violation);
+
Policy policy = getNearestPolicy(fragment);
- if (policy.flags.contains(Flag.DETECT_FRAGMENT_REUSE)) {
- handlePolicyViolation(fragment, policy, new FragmentReuseViolation());
+ if (policy.mFlags.contains(Flag.DETECT_FRAGMENT_REUSE)
+ && shouldHandlePolicyViolation(
+ fragment.getClass(), policy, violation.getClass())) {
+ handlePolicyViolation(fragment, policy, violation);
}
}
@RestrictTo(RestrictTo.Scope.LIBRARY)
public static void onFragmentTagUsage(@NonNull Fragment fragment) {
+ Violation violation = new FragmentTagUsageViolation();
+ logIfDebuggingEnabled(fragment.getClass().getName(), violation);
+
Policy policy = getNearestPolicy(fragment);
- if (policy.flags.contains(Flag.DETECT_FRAGMENT_TAG_USAGE)) {
- handlePolicyViolation(fragment, policy, new FragmentTagUsageViolation());
+ if (policy.mFlags.contains(Flag.DETECT_FRAGMENT_TAG_USAGE)
+ && shouldHandlePolicyViolation(
+ fragment.getClass(), policy, violation.getClass())) {
+ handlePolicyViolation(fragment, policy, violation);
}
}
@RestrictTo(RestrictTo.Scope.LIBRARY)
public static void onRetainInstanceUsage(@NonNull Fragment fragment) {
+ Violation violation = new RetainInstanceUsageViolation();
+ logIfDebuggingEnabled(fragment.getClass().getName(), violation);
+
Policy policy = getNearestPolicy(fragment);
- if (policy.flags.contains(Flag.DETECT_RETAIN_INSTANCE_USAGE)) {
- handlePolicyViolation(fragment, policy, new RetainInstanceUsageViolation());
+ if (policy.mFlags.contains(Flag.DETECT_RETAIN_INSTANCE_USAGE)
+ && shouldHandlePolicyViolation(
+ fragment.getClass(), policy, violation.getClass())) {
+ handlePolicyViolation(fragment, policy, violation);
}
}
@RestrictTo(RestrictTo.Scope.LIBRARY)
public static void onSetUserVisibleHint(@NonNull Fragment fragment) {
+ Violation violation = new SetUserVisibleHintViolation();
+ logIfDebuggingEnabled(fragment.getClass().getName(), violation);
+
Policy policy = getNearestPolicy(fragment);
- if (policy.flags.contains(Flag.DETECT_SET_USER_VISIBLE_HINT)) {
- handlePolicyViolation(fragment, policy, new SetUserVisibleHintViolation());
+ if (policy.mFlags.contains(Flag.DETECT_SET_USER_VISIBLE_HINT)
+ && shouldHandlePolicyViolation(
+ fragment.getClass(), policy, violation.getClass())) {
+ handlePolicyViolation(fragment, policy, violation);
}
}
@RestrictTo(RestrictTo.Scope.LIBRARY)
public static void onTargetFragmentUsage(@NonNull Fragment fragment) {
+ Violation violation = new TargetFragmentUsageViolation();
+ logIfDebuggingEnabled(fragment.getClass().getName(), violation);
+
Policy policy = getNearestPolicy(fragment);
- if (policy.flags.contains(Flag.DETECT_TARGET_FRAGMENT_USAGE)) {
- handlePolicyViolation(fragment, policy, new TargetFragmentUsageViolation());
+ if (policy.mFlags.contains(Flag.DETECT_TARGET_FRAGMENT_USAGE)
+ && shouldHandlePolicyViolation(
+ fragment.getClass(), policy, violation.getClass())) {
+ handlePolicyViolation(fragment, policy, violation);
}
}
@RestrictTo(RestrictTo.Scope.LIBRARY)
public static void onWrongFragmentContainer(@NonNull Fragment fragment) {
+ Violation violation = new WrongFragmentContainerViolation();
+ logIfDebuggingEnabled(fragment.getClass().getName(), violation);
+
Policy policy = getNearestPolicy(fragment);
- if (policy.flags.contains(Flag.DETECT_WRONG_FRAGMENT_CONTAINER)) {
- handlePolicyViolation(fragment, policy, new WrongFragmentContainerViolation());
+ if (policy.mFlags.contains(Flag.DETECT_WRONG_FRAGMENT_CONTAINER)
+ && shouldHandlePolicyViolation(
+ fragment.getClass(), policy, violation.getClass())) {
+ handlePolicyViolation(fragment, policy, violation);
}
}
@VisibleForTesting
static void onPolicyViolation(@NonNull Fragment fragment, @NonNull Violation violation) {
+ logIfDebuggingEnabled(fragment.getClass().getName(), violation);
+
Policy policy = getNearestPolicy(fragment);
- handlePolicyViolation(fragment, policy, violation);
+ if (shouldHandlePolicyViolation(fragment.getClass(), policy, violation.getClass())) {
+ handlePolicyViolation(fragment, policy, violation);
+ }
+ }
+
+ private static void logIfDebuggingEnabled(
+ @NonNull String fragmentName,
+ @NonNull final Violation violation
+ ) {
+ if (FragmentManager.isLoggingEnabled(Log.DEBUG)) {
+ Log.d(FragmentManager.TAG, "StrictMode violation in " + fragmentName,
+ violation);
+ }
+ }
+
+ private static boolean shouldHandlePolicyViolation(
+ @NonNull Class<? extends Fragment> fragmentClass,
+ @NonNull final Policy policy,
+ @NonNull Class<? extends Violation> violationClass) {
+ Set<Class<? extends Violation>> violationsToBypass =
+ policy.mAllowedViolations.get(fragmentClass);
+ return violationsToBypass == null || !violationsToBypass.contains(violationClass);
}
private static void handlePolicyViolation(
@@ -310,20 +403,20 @@
) {
final String fragmentName = fragment.getClass().getName();
- if (policy.flags.contains(Flag.PENALTY_LOG)) {
+ if (policy.mFlags.contains(Flag.PENALTY_LOG)) {
Log.d(TAG, "Policy violation in " + fragmentName, violation);
}
- if (policy.listener != null) {
+ if (policy.mListener != null) {
runOnHostThread(fragment, new Runnable() {
@Override
public void run() {
- policy.listener.onViolation(violation);
+ policy.mListener.onViolation(violation);
}
});
}
- if (policy.flags.contains(Flag.PENALTY_DEATH)) {
+ if (policy.mFlags.contains(Flag.PENALTY_DEATH)) {
runOnHostThread(fragment, new Runnable() {
@Override
public void run() {
diff --git a/leanback/leanback/src/androidTest/java/androidx/leanback/app/DetailsFragmentTest.java b/leanback/leanback/src/androidTest/java/androidx/leanback/app/DetailsFragmentTest.java
index 3813ac5..2c342b4 100644
--- a/leanback/leanback/src/androidTest/java/androidx/leanback/app/DetailsFragmentTest.java
+++ b/leanback/leanback/src/androidTest/java/androidx/leanback/app/DetailsFragmentTest.java
@@ -1261,16 +1261,17 @@
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN),
1000);
- View fragmentView = activity.getTestFragment().getView();
- RowsFragment rowsSupportFragment =
- ((DetailsFragmentEntranceTransition) activity.getTestFragment())
- .getRowsFragment();
+ DetailsFragmentEntranceTransition detailsFragment =
+ (DetailsFragmentEntranceTransition) activity.getTestFragment();
+ View fragmentView = detailsFragment.getView();
+ RowsFragment rowsSupportFragment = detailsFragment.getRowsFragment();
VerticalGridView gridView = rowsSupportFragment.getVerticalGridView();
LeakDetector leakDetector = new LeakDetector();
leakDetector.observeObject(fragmentView);
// Note: RowsFragment is referred by childFragmentManager of details fragment.
leakDetector.observeObject(gridView);
leakDetector.observeObject(gridView.getRecycledViewPool());
+ leakDetector.observeObject(detailsFragment.mDetailsBackgroundController.mCoverBitmap);
for (int i = 0; i < gridView.getChildCount(); i++) {
leakDetector.observeObject(gridView.getChildAt(i));
}
@@ -1289,6 +1290,7 @@
return emptyFragment.isResumed();
}
});
+ assertTrue(detailsFragment.mBackgroundDrawable != null);
leakDetector.assertNoLeak();
}
}
diff --git a/leanback/leanback/src/androidTest/java/androidx/leanback/app/DetailsSupportFragmentTest.java b/leanback/leanback/src/androidTest/java/androidx/leanback/app/DetailsSupportFragmentTest.java
index 38a1f78..0f63d6d 100644
--- a/leanback/leanback/src/androidTest/java/androidx/leanback/app/DetailsSupportFragmentTest.java
+++ b/leanback/leanback/src/androidTest/java/androidx/leanback/app/DetailsSupportFragmentTest.java
@@ -1258,16 +1258,17 @@
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN),
1000);
- View fragmentView = activity.getTestFragment().getView();
- RowsSupportFragment rowsSupportFragment =
- ((DetailsSupportFragmentEntranceTransition) activity.getTestFragment())
- .getRowsSupportFragment();
+ DetailsSupportFragmentEntranceTransition detailsFragment =
+ (DetailsSupportFragmentEntranceTransition) activity.getTestFragment();
+ View fragmentView = detailsFragment.getView();
+ RowsSupportFragment rowsSupportFragment = detailsFragment.getRowsSupportFragment();
VerticalGridView gridView = rowsSupportFragment.getVerticalGridView();
LeakDetector leakDetector = new LeakDetector();
leakDetector.observeObject(fragmentView);
// Note: RowsSupportFragment is referred by childFragmentManager of details fragment.
leakDetector.observeObject(gridView);
leakDetector.observeObject(gridView.getRecycledViewPool());
+ leakDetector.observeObject(detailsFragment.mDetailsBackgroundController.mCoverBitmap);
for (int i = 0; i < gridView.getChildCount(); i++) {
leakDetector.observeObject(gridView.getChildAt(i));
}
@@ -1286,6 +1287,7 @@
return emptyFragment.isResumed();
}
});
+ assertTrue(detailsFragment.mBackgroundDrawable != null);
leakDetector.assertNoLeak();
}
}
diff --git a/leanback/leanback/src/main/java/androidx/leanback/app/DetailsFragment.java b/leanback/leanback/src/main/java/androidx/leanback/app/DetailsFragment.java
index 1d7a26ea..9cbfbfd 100644
--- a/leanback/leanback/src/main/java/androidx/leanback/app/DetailsFragment.java
+++ b/leanback/leanback/src/main/java/androidx/leanback/app/DetailsFragment.java
@@ -528,7 +528,6 @@
}
mRootView = null;
mBackgroundView = null;
- mBackgroundDrawable = null;
mRowsFragment = null;
mVideoFragment = null;
mSceneAfterEntranceTransition = null;
diff --git a/leanback/leanback/src/main/java/androidx/leanback/app/DetailsSupportFragment.java b/leanback/leanback/src/main/java/androidx/leanback/app/DetailsSupportFragment.java
index 4d56fe1..f189253 100644
--- a/leanback/leanback/src/main/java/androidx/leanback/app/DetailsSupportFragment.java
+++ b/leanback/leanback/src/main/java/androidx/leanback/app/DetailsSupportFragment.java
@@ -523,7 +523,6 @@
}
mRootView = null;
mBackgroundView = null;
- mBackgroundDrawable = null;
mRowsSupportFragment = null;
mVideoSupportFragment = null;
mSceneAfterEntranceTransition = null;
diff --git a/leanback/leanback/src/main/java/androidx/leanback/app/SearchFragment.java b/leanback/leanback/src/main/java/androidx/leanback/app/SearchFragment.java
index 38bcc52..dc49c69 100644
--- a/leanback/leanback/src/main/java/androidx/leanback/app/SearchFragment.java
+++ b/leanback/leanback/src/main/java/androidx/leanback/app/SearchFragment.java
@@ -389,7 +389,7 @@
});
if (!isSpeechRecognizerAvailable()) {
- if(mSearchBar.hasFocus()) {
+ if (mSearchBar.hasFocus()) {
mSearchBar.findViewById(R.id.lb_search_text_editor).requestFocus();
}
mSearchBar.findViewById(R.id.lb_search_bar_speech_orb).setFocusable(false);
@@ -419,8 +419,7 @@
mIsPaused = false;
if (mSpeechRecognitionCallback == null && null == mSpeechRecognizer
&& mSpeechRecognizerEnabled) {
- mSpeechRecognizer = SpeechRecognizer.createSpeechRecognizer(
- FragmentUtil.getContext(SearchFragment.this));
+ mSpeechRecognizer = SpeechRecognizer.createSpeechRecognizer(FragmentUtil.getContext(SearchFragment.this));
mSearchBar.setSpeechRecognizer(mSpeechRecognizer);
}
if (mPendingStartRecognitionWhenPaused) {
@@ -783,8 +782,7 @@
}
boolean isSpeechRecognizerAvailable() {
- return SpeechRecognizer.isRecognitionAvailable(
- FragmentUtil.getContext(SearchFragment.this));
+ return SpeechRecognizer.isRecognitionAvailable(FragmentUtil.getContext(SearchFragment.this));
}
static class ExternalQuery {
diff --git a/lifecycle/lifecycle-compiler/build.gradle b/lifecycle/lifecycle-compiler/build.gradle
index cde5b80..f64dfa1 100644
--- a/lifecycle/lifecycle-compiler/build.gradle
+++ b/lifecycle/lifecycle-compiler/build.gradle
@@ -25,13 +25,6 @@
id("kotlin")
}
-// Temporary hack to stop AS to adding two guavas into test's classpath
-configurations.all {
- resolutionStrategy {
- force GUAVA
- }
-}
-
dependencies {
implementation(project(":lifecycle:lifecycle-common"))
implementation(KOTLIN_STDLIB)
diff --git a/lifecycle/lifecycle-livedata-core-ktx-lint/build.gradle b/lifecycle/lifecycle-livedata-core-ktx-lint/build.gradle
index 0dc1113..193e1c7 100644
--- a/lifecycle/lifecycle-livedata-core-ktx-lint/build.gradle
+++ b/lifecycle/lifecycle-livedata-core-ktx-lint/build.gradle
@@ -26,15 +26,8 @@
}
dependencies {
- // compileOnly because we use lintChecks and it doesn't allow other types of deps
- // this ugly hack exists because of b/63873667
- if (rootProject.hasProperty("android.injected.invoked.from.ide")) {
- compileOnly(LINT_API_LATEST)
- compileOnly(LINT_CORE)
- } else {
- compileOnly(LINT_API_MIN)
- compileOnly("com.android.tools.lint:lint:$lintMinVersion")
- }
+ compileOnly(LINT_API_MIN)
+ compileOnly("com.android.tools.lint:lint:$lintMinVersion")
compileOnly(KOTLIN_STDLIB)
testImplementation(KOTLIN_STDLIB)
diff --git a/lifecycle/lifecycle-runtime-ktx-lint/build.gradle b/lifecycle/lifecycle-runtime-ktx-lint/build.gradle
index c7e05bd..6a17065 100644
--- a/lifecycle/lifecycle-runtime-ktx-lint/build.gradle
+++ b/lifecycle/lifecycle-runtime-ktx-lint/build.gradle
@@ -26,13 +26,7 @@
}
dependencies {
- // compileOnly because we use lintChecks and it doesn't allow other types of deps
- // this ugly hack exists because of b/63873667
- if (rootProject.hasProperty("android.injected.invoked.from.ide")) {
- compileOnly(LINT_API_LATEST)
- } else {
- compileOnly(LINT_API_MIN)
- }
+ compileOnly(LINT_API_MIN)
compileOnly(KOTLIN_STDLIB)
testImplementation(KOTLIN_STDLIB)
diff --git a/media2/media2-widget/src/main/res/values-eu/strings.xml b/media2/media2-widget/src/main/res/values-eu/strings.xml
index 4fde31a..5999909 100644
--- a/media2/media2-widget/src/main/res/values-eu/strings.xml
+++ b/media2/media2-widget/src/main/res/values-eu/strings.xml
@@ -20,7 +20,7 @@
<string name="MediaControlView_subtitle_off_text" msgid="3464220590351304587">"Desaktibatuta"</string>
<string name="MediaControlView_audio_track_text" msgid="3309135445007366582">"Audio-pista"</string>
<string name="MediaControlView_audio_track_none_text" msgid="2659752099246305694">"Bat ere ez"</string>
- <string name="MediaControlView_playback_speed_text" msgid="1481072528142380025">"Erreprodukzioaren abiadura"</string>
+ <string name="MediaControlView_playback_speed_text" msgid="1481072528142380025">"Erreprodukzio-abiadura"</string>
<string name="MediaControlView_playback_speed_normal" msgid="2029510260288453183">"Normala"</string>
<string name="MediaControlView_time_placeholder" msgid="6734584158942500617">"00:00:00"</string>
<string name="MediaControlView_subtitle_track_number_text" msgid="2241078077382492349">"<xliff:g id="TRACK_NUMBER">%1$d</xliff:g>. pista"</string>
diff --git a/recyclerview/recyclerview-lint/build.gradle b/recyclerview/recyclerview-lint/build.gradle
index 3a3b3ff..e976ce8 100644
--- a/recyclerview/recyclerview-lint/build.gradle
+++ b/recyclerview/recyclerview-lint/build.gradle
@@ -25,13 +25,7 @@
}
dependencies {
- // compileOnly because we use lintChecks and it doesn't allow other types of deps
- // this ugly hack exists because of b/63873667
- if (rootProject.hasProperty("android.injected.invoked.from.ide")) {
- compileOnly(LINT_API_LATEST)
- } else {
- compileOnly(LINT_API_MIN)
- }
+ compileOnly(LINT_API_MIN)
compileOnly(KOTLIN_STDLIB)
testImplementation(KOTLIN_STDLIB)
diff --git a/resourceinspection/resourceinspection-annotation/api/restricted_current.txt b/resourceinspection/resourceinspection-annotation/api/restricted_current.txt
index 2962da1..5be0bd5 100644
--- a/resourceinspection/resourceinspection-annotation/api/restricted_current.txt
+++ b/resourceinspection/resourceinspection-annotation/api/restricted_current.txt
@@ -1,6 +1,9 @@
// Signature format: 4.0
package androidx.resourceinspection.annotation {
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target(java.lang.annotation.ElementType.TYPE) public @interface AppCompatShadowedAttributes {
+ }
+
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target(java.lang.annotation.ElementType.METHOD) public @interface Attribute {
method public abstract androidx.resourceinspection.annotation.Attribute.IntMap[] intMapping() default {};
method public abstract String value();
diff --git a/resourceinspection/resourceinspection-annotation/src/main/java/androidx/resourceinspection/annotation/AppCompatShadowedAttributes.java b/resourceinspection/resourceinspection-annotation/src/main/java/androidx/resourceinspection/annotation/AppCompatShadowedAttributes.java
new file mode 100644
index 0000000..2ec1183
--- /dev/null
+++ b/resourceinspection/resourceinspection-annotation/src/main/java/androidx/resourceinspection/annotation/AppCompatShadowedAttributes.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.resourceinspection.annotation;
+
+import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import androidx.annotation.RestrictTo;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Marks a view within AppCompat that shadows platform attributes.
+ * <p>
+ * Many AppCompat views shadow a platform attribute in order to provide backwards compatibility
+ * for older API levels. This means that a developer might set
+ * {@code androidx.appcompat:backgroundTint} instead of {@code android:backgroundTint} to get a
+ * background tint with support for API < 21. On more recent versions of the platform, this has
+ * the effect of storing the resolution stack in the {@code androidx.appcompat} namespace but
+ * reading the set value from the platform inspection companion in the {@code android} namespace,
+ * causing the resolution stack to get lost in the inspector.
+ * <p>
+ * Ordinarily, this behavior could be overridden by an {@link Attribute} annotation on the getter,
+ * but it is infeasible to override a platform getter that doesn't exist on older supported API
+ * levels. It results in worse performance at load time on those devices.
+ * <p>
+ * This annotation instructs the processor to include a list of shadowed attributes with
+ * API-level appropriate accessors in the view's inspection companion. It infers the attributes
+ * to include from the interfaces on the annotated view. For example, a view that implements
+ * {@link androidx.core.view.TintableBackgroundView} will report
+ * {@code androidx.appcompat:backgroundTint} directly from the platform
+ * {@link android.view.View.getBackgroundTintList()} getter. This approach allows views within
+ * AppCompat to mix shadowed attributes and regular attribute annotations on the same view
+ * without hand-written inspection companions.
+ * <p>
+ * @hide
+ */
+@Target(TYPE)
+@Retention(SOURCE)
+@RestrictTo(LIBRARY_GROUP_PREFIX)
+public @interface AppCompatShadowedAttributes {
+}
diff --git a/resourceinspection/resourceinspection-processor/src/main/kotlin/androidx/resourceinspection/processor/InspectionCompanionGeneration.kt b/resourceinspection/resourceinspection-processor/src/main/kotlin/androidx/resourceinspection/processor/InspectionCompanionGeneration.kt
index a013fb7..77b6d72 100644
--- a/resourceinspection/resourceinspection-processor/src/main/kotlin/androidx/resourceinspection/processor/InspectionCompanionGeneration.kt
+++ b/resourceinspection/resourceinspection-processor/src/main/kotlin/androidx/resourceinspection/processor/InspectionCompanionGeneration.kt
@@ -31,7 +31,7 @@
/** Generates an inspection companion from a view using JavaPoet. */
internal fun generateInspectionCompanion(
- view: ViewIR,
+ view: View,
generatedAnnotation: AnnotationSpec?
): JavaFile {
val typeSpec = TypeSpec.classBuilder(
@@ -73,14 +73,14 @@
for (attribute in view.attributes) {
when (attribute.type) {
- AttributeTypeIR.INT_ENUM -> addStatement(
+ AttributeType.INT_ENUM -> addStatement(
"\$N = propertyMapper.mapIntEnum(\$S, \$L, \$L)",
attributeIdNames[attribute],
attribute.name,
attribute.attrReference,
intEnumLambda(attribute)
)
- AttributeTypeIR.INT_FLAG -> addStatement(
+ AttributeType.INT_FLAG -> addStatement(
"\$N = propertyMapper.mapIntFlag(\$S, \$L, \$L)",
attributeIdNames[attribute],
attribute.name,
@@ -117,11 +117,11 @@
for (attribute in view.attributes) {
addStatement(
- "propertyReader.read\$L(\$N, \$N.\$L())",
+ "propertyReader.read\$L(\$N, \$N.\$L)",
attribute.type.apiSuffix,
attributeIdNames[attribute],
viewParameter,
- attribute.getter.simpleName
+ attribute.invocation
)
}
}.build()
@@ -134,7 +134,7 @@
}
/** The `(Int) -> String` lambda for int enums, as an anonymous class for Java 7 compatibility. */
-private fun intEnumLambda(attribute: AttributeIR): TypeSpec {
+private fun intEnumLambda(attribute: Attribute): TypeSpec {
return TypeSpec.anonymousClassBuilder("").apply {
addSuperinterface(INT_FUNCTION.parameterized(STRING))
@@ -159,7 +159,7 @@
}
/** The `(Int) -> Set<String>` lambda for int flags, as an anonymous class. */
-private fun intFlagLambda(attribute: AttributeIR): TypeSpec {
+private fun intFlagLambda(attribute: Attribute): TypeSpec {
val stringSet = SET.parameterized(STRING)
val stringHashSet = HASH_SET.parameterized(STRING)
@@ -192,7 +192,7 @@
}
/** A [CodeBlock] of the `$namespace.R.attr.$name` attribute ID reference. */
-private val AttributeIR.attrReference: CodeBlock
+private val Attribute.attrReference: CodeBlock
get() = CodeBlock.of("\$T.attr.\$N", ClassName.get(namespace, "R"), name)
/** Kotlin wrapper for [ClassName.get] to avoid platform types. */
diff --git a/resourceinspection/resourceinspection-processor/src/main/kotlin/androidx/resourceinspection/processor/LayoutInspectionStep.kt b/resourceinspection/resourceinspection-processor/src/main/kotlin/androidx/resourceinspection/processor/LayoutInspectionStep.kt
index 6038e6e..a9d59b3 100644
--- a/resourceinspection/resourceinspection-processor/src/main/kotlin/androidx/resourceinspection/processor/LayoutInspectionStep.kt
+++ b/resourceinspection/resourceinspection-processor/src/main/kotlin/androidx/resourceinspection/processor/LayoutInspectionStep.kt
@@ -67,8 +67,8 @@
return emptySet()
}
- /** Parse the annotated getters of a view class into a [ViewIR]. */
- private fun parseView(type: TypeElement, getters: Iterable<ExecutableElement>): ViewIR? {
+ /** Parse the annotated getters of a view class into a [View]. */
+ private fun parseView(type: TypeElement, getters: Iterable<ExecutableElement>): View? {
if (!type.asType().isAssignableTo("android.view.View")) {
getters.forEach { getter ->
printError(
@@ -93,13 +93,13 @@
duplicates.forEach { attribute ->
val qualifiedName = attribute.qualifiedName
val otherGetters = duplicates
- .filter { it.getter != attribute.getter }
- .joinToString { it.getter.toString() }
+ .filter { it.invocation != attribute.invocation }
+ .joinToString { it.invocation }
printError(
"Duplicate attribute $qualifiedName is also present on $otherGetters",
- attribute.getter,
- attribute.annotation
+ (attribute as? GetterAttribute)?.getter,
+ (attribute as? GetterAttribute)?.annotation
)
}
}
@@ -110,11 +110,11 @@
return null
}
- return ViewIR(type, attributes = attributes.filterNotNull().sortedBy { it.qualifiedName })
+ return View(type, attributes = attributes.filterNotNull().sortedBy { it.qualifiedName })
}
- /** Get an [AttributeIR] from a method known to have an `Attribute` annotation. */
- private fun parseAttribute(getter: ExecutableElement): AttributeIR? {
+ /** Get an [Attribute] from a method known to have an `Attribute` annotation. */
+ private fun parseAttribute(getter: ExecutableElement): Attribute? {
val annotation = getter.getAnnotationMirror(ATTRIBUTE)!!
val annotationValue = getAnnotationValue(annotation, "value")
val value = annotationValue.value as String
@@ -139,7 +139,7 @@
// TODO(b/180041203): Verify attribute ID or at least existence of R files
// TODO(b/180041633): Validate consistency of int mapping
- AttributeIR(getter, annotation, namespace, name, type, intMapping)
+ GetterAttribute(getter, annotation, namespace, name, type, intMapping)
} else if (!value.contains(':')) {
printError("Attribute name must include namespace", getter, annotation, annotationValue)
null
@@ -150,11 +150,11 @@
}
/** Parse `Attribute.intMapping`. */
- private fun parseIntMapping(annotation: AnnotationMirror): List<IntMapIR> {
+ private fun parseIntMapping(annotation: AnnotationMirror): List<IntMap> {
return (getAnnotationValue(annotation, "intMapping").value as List<*>).map { entry ->
val intMapAnnotation = (entry as AnnotationValue).value as AnnotationMirror
- IntMapIR(
+ IntMap(
name = getAnnotationValue(intMapAnnotation, "name").value as String,
value = getAnnotationValue(intMapAnnotation, "value").value as Int,
mask = getAnnotationValue(intMapAnnotation, "mask").value as Int,
@@ -165,35 +165,35 @@
/** Map the getter's annotations and return type to the internal attribute type. */
private fun inferAttributeType(
getter: ExecutableElement,
- intMapping: List<IntMapIR>
- ): AttributeTypeIR {
+ intMapping: List<IntMap>
+ ): AttributeType {
return when (getter.returnType.kind) {
- TypeKind.BOOLEAN -> AttributeTypeIR.BOOLEAN
- TypeKind.BYTE -> AttributeTypeIR.BYTE
- TypeKind.CHAR -> AttributeTypeIR.CHAR
- TypeKind.DOUBLE -> AttributeTypeIR.DOUBLE
- TypeKind.FLOAT -> AttributeTypeIR.FLOAT
- TypeKind.SHORT -> AttributeTypeIR.SHORT
+ TypeKind.BOOLEAN -> AttributeType.BOOLEAN
+ TypeKind.BYTE -> AttributeType.BYTE
+ TypeKind.CHAR -> AttributeType.CHAR
+ TypeKind.DOUBLE -> AttributeType.DOUBLE
+ TypeKind.FLOAT -> AttributeType.FLOAT
+ TypeKind.SHORT -> AttributeType.SHORT
TypeKind.INT -> when {
- getter.isAnnotationPresent(COLOR_INT) -> AttributeTypeIR.COLOR
- getter.isAnnotationPresent(GRAVITY_INT) -> AttributeTypeIR.GRAVITY
- getter.hasResourceIdAnnotation() -> AttributeTypeIR.RESOURCE_ID
- intMapping.any { it.mask != 0 } -> AttributeTypeIR.INT_FLAG
- intMapping.isNotEmpty() -> AttributeTypeIR.INT_ENUM
- else -> AttributeTypeIR.INT
+ getter.isAnnotationPresent(COLOR_INT) -> AttributeType.COLOR
+ getter.isAnnotationPresent(GRAVITY_INT) -> AttributeType.GRAVITY
+ getter.hasResourceIdAnnotation() -> AttributeType.RESOURCE_ID
+ intMapping.any { it.mask != 0 } -> AttributeType.INT_FLAG
+ intMapping.isNotEmpty() -> AttributeType.INT_ENUM
+ else -> AttributeType.INT
}
TypeKind.LONG ->
if (getter.isAnnotationPresent(COLOR_LONG)) {
- AttributeTypeIR.COLOR
+ AttributeType.COLOR
} else {
- AttributeTypeIR.LONG
+ AttributeType.LONG
}
TypeKind.DECLARED, TypeKind.ARRAY ->
if (getter.returnType.isAssignableTo("android.graphics.Color")) {
- AttributeTypeIR.COLOR
+ AttributeType.COLOR
} else {
// TODO(b/180041034): Validate object types and unbox primitives
- AttributeTypeIR.OBJECT
+ AttributeType.OBJECT
}
else -> throw IllegalArgumentException("Unexpected attribute type")
}
@@ -215,7 +215,7 @@
/** Convenience wrapper for [javax.annotation.processing.Messager.printMessage]. */
private fun printError(
message: String,
- element: Element,
+ element: Element?,
annotation: AnnotationMirror? = null,
value: AnnotationValue? = null
) {
diff --git a/resourceinspection/resourceinspection-processor/src/main/kotlin/androidx/resourceinspection/processor/InternalRepresentations.kt b/resourceinspection/resourceinspection-processor/src/main/kotlin/androidx/resourceinspection/processor/Models.kt
similarity index 69%
rename from resourceinspection/resourceinspection-processor/src/main/kotlin/androidx/resourceinspection/processor/InternalRepresentations.kt
rename to resourceinspection/resourceinspection-processor/src/main/kotlin/androidx/resourceinspection/processor/Models.kt
index 8254250..ad46352 100644
--- a/resourceinspection/resourceinspection-processor/src/main/kotlin/androidx/resourceinspection/processor/InternalRepresentations.kt
+++ b/resourceinspection/resourceinspection-processor/src/main/kotlin/androidx/resourceinspection/processor/Models.kt
@@ -22,34 +22,46 @@
import javax.lang.model.element.TypeElement
/** Represents a view with annotated attributes, mostly a convenience class. */
-internal data class ViewIR(
+internal data class View(
val type: TypeElement,
- val attributes: List<AttributeIR>
+ val attributes: List<Attribute>
) {
val className: ClassName = ClassName.get(type)
}
+internal interface Attribute {
+ val name: String
+ val namespace: String
+ val type: AttributeType
+ val intMapping: List<IntMap>
+ val invocation: String
+
+ val qualifiedName: String
+ get() = "$namespace:$name"
+}
+
/** Represents an `Attribute` with its getter. */
-internal data class AttributeIR(
+internal data class GetterAttribute(
val getter: ExecutableElement,
val annotation: AnnotationMirror,
- val namespace: String,
- val name: String,
- val type: AttributeTypeIR,
- val intMapping: List<IntMapIR>
-) {
- val qualifiedName: String = "$namespace:$name"
+ override val namespace: String,
+ override val name: String,
+ override val type: AttributeType,
+ override val intMapping: List<IntMap> = emptyList()
+) : Attribute {
+ override val invocation: String
+ get() = "${getter.simpleName}()"
}
/** Represents an `Attribute.IntMap` entry. */
-internal data class IntMapIR(
+internal data class IntMap(
val name: String,
val value: Int,
- val mask: Int
+ val mask: Int = 0
)
/** Represents the type of the attribute, determined from context and the annotation itself. */
-internal enum class AttributeTypeIR(val apiSuffix: String) {
+internal enum class AttributeType(val apiSuffix: String) {
BOOLEAN("Boolean"),
BYTE("Byte"),
CHAR("Char"),
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/XElement.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/XElement.kt
index b06d8c9..497b194 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/XElement.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/XElement.kt
@@ -40,6 +40,11 @@
* message. Without this information, developer gets no clue on where the error is.
*/
val fallbackLocationText: String
+
+ /**
+ * The documentation comment of the element, or null if there is none.
+ */
+ val docComment: String?
}
/**
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/XMessager.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/XMessager.kt
index 16f1bba..65561ab 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/XMessager.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/XMessager.kt
@@ -37,7 +37,11 @@
onPrintMessage(kind, msg, element)
}
- abstract fun onPrintMessage(kind: Diagnostic.Kind, msg: String, element: XElement? = null)
+ protected abstract fun onPrintMessage(
+ kind: Diagnostic.Kind,
+ msg: String,
+ element: XElement? = null
+ )
fun addMessageWatcher(watcher: XMessager) {
watchers.add(watcher)
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/XTypeElement.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/XTypeElement.kt
index 5109aff..aa692e1 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/XTypeElement.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/XTypeElement.kt
@@ -65,11 +65,49 @@
fun isInterface(): Boolean
/**
- * Returns `true` if this [XTypeElement] is declared as a Kotlin `object`
+ * Returns `true` if this [XTypeElement] represents a Kotlin functional interface,
+ * i.e. marked with the keyword `fun`
+ */
+ fun isFunctionalInterface(): Boolean
+
+ /**
+ * Returns `true` if this [XTypeElement] represents an ordinary class. ie not an enum, object,
+ * annotation, interface, or other type of specialty class.
+ */
+ fun isClass(): Boolean
+
+ /**
+ * Returns `true` if this [XTypeElement] represents a Kotlin data class
+ */
+ fun isDataClass(): Boolean
+
+ /**
+ * Returns `true` if this [XTypeElement] represents a Kotlin value class
+ */
+ fun isValueClass(): Boolean
+
+ /**
+ * Returns `true` if this [XTypeElement] represents a class with the Kotlin `expect` keyword
+ */
+ fun isExpect(): Boolean
+
+ /**
+ * Returns `true` if this [XTypeElement] represents a Kotlin annotation class or a Java
+ * annotation type.
+ */
+ fun isAnnotationClass(): Boolean
+
+ /**
+ * Returns `true` if this [XTypeElement] is a non-companion `object` in Kotlin
*/
fun isKotlinObject(): Boolean
/**
+ * Returns `true` if this [XTypeElement] is declared as a Kotlin `companion object`
+ */
+ fun isCompanionObject(): Boolean
+
+ /**
* All fields, including private supers.
* Room only ever reads fields this way.
*/
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacElement.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacElement.kt
index 47eb36b..497a809 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacElement.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacElement.kt
@@ -86,4 +86,8 @@
MoreElements.getPackage(it.annotationType.asElement()).toString() == pkg
}
}
+
+ override val docComment: String? by lazy {
+ env.elementUtils.getDocComment(element)
+ }
}
\ No newline at end of file
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacTypeElement.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacTypeElement.kt
index acaffb7..1266ef0 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacTypeElement.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacTypeElement.kt
@@ -21,7 +21,6 @@
import androidx.room.compiler.processing.XHasModifiers
import androidx.room.compiler.processing.XMethodElement
import androidx.room.compiler.processing.XTypeElement
-import androidx.room.compiler.processing.javac.JavacTypeElement.JavacEnumTypeElement
import androidx.room.compiler.processing.javac.kotlin.KotlinMetadataElement
import com.google.auto.common.MoreElements
import com.google.auto.common.MoreTypes
@@ -58,8 +57,6 @@
element.enclosingType(env)
}
- override fun isInterface() = element.kind == ElementKind.INTERFACE
-
private val _allFieldsIncludingPrivateSupers by lazy {
element.getAllFieldsIncludingPrivateSupers(
env.elementUtils
@@ -77,6 +74,24 @@
}
override fun isKotlinObject() = kotlinMetadata?.isObject() == true
+ override fun isCompanionObject() = kotlinMetadata?.isCompanionObject() == true
+ override fun isDataClass() = kotlinMetadata?.isDataClass() == true
+ override fun isValueClass() = kotlinMetadata?.isValueClass() == true
+ override fun isFunctionalInterface() = kotlinMetadata?.isFunctionalInterface() == true
+ override fun isExpect() = kotlinMetadata?.isExpect() == true
+
+ override fun isAnnotationClass(): Boolean {
+ return kotlinMetadata?.isAnnotationClass()
+ ?: (element.kind == ElementKind.ANNOTATION_TYPE)
+ }
+
+ override fun isClass(): Boolean {
+ return kotlinMetadata?.isClass() ?: (element.kind == ElementKind.CLASS)
+ }
+
+ override fun isInterface(): Boolean {
+ return kotlinMetadata?.isInterface() ?: (element.kind == ElementKind.INTERFACE)
+ }
override fun findPrimaryConstructor(): JavacConstructorElement? {
val primarySignature = kotlinMetadata?.findPrimaryConstructorSignature() ?: return null
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/kotlin/KotlinClassMetadataUtils.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/kotlin/KotlinClassMetadataUtils.kt
index 2ae8d57..2b3fc32 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/kotlin/KotlinClassMetadataUtils.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/kotlin/KotlinClassMetadataUtils.kt
@@ -39,6 +39,7 @@
internal interface KmExecutable {
val parameters: List<KmValueParameter>
}
+
/**
* Represents the kotlin metadata of a function
*/
@@ -68,6 +69,7 @@
) {
val typeParameters
get() = type.typeArguments
+
fun isNullable() = Flag.Type.IS_NULLABLE(type.flags)
}
@@ -188,25 +190,42 @@
}
}
-internal fun KotlinClassMetadata.Class.isObject(): Boolean = ObjectReader().let {
- this@isObject.accept(it)
- it.isObject
+internal class KotlinMetadataClassFlags(val classMetadata: KotlinClassMetadata.Class) {
+
+ private val flags: Flags by lazy {
+ var theFlags: Flags = 0
+ classMetadata.accept(object : KmClassVisitor() {
+ override fun visit(flags: Flags, name: ClassName) {
+ theFlags = flags
+ super.visit(flags, name)
+ }
+ })
+ return@lazy theFlags
+ }
+
+ fun isObject(): Boolean = Flag.Class.IS_OBJECT(flags)
+
+ fun isCompanionObject(): Boolean = Flag.Class.IS_COMPANION_OBJECT(flags)
+
+ fun isAnnotationClass(): Boolean = Flag.Class.IS_ANNOTATION_CLASS(flags)
+
+ fun isInterface(): Boolean = Flag.Class.IS_INTERFACE(flags)
+
+ fun isClass(): Boolean = Flag.Class.IS_CLASS(flags)
+
+ fun isDataClass(): Boolean = Flag.Class.IS_DATA(flags)
+
+ fun isValueClass(): Boolean = Flag.Class.IS_INLINE(flags)
+
+ fun isFunctionalInterface(): Boolean = Flag.Class.IS_FUN(flags)
+
+ fun isExpect(): Boolean = Flag.Class.IS_EXPECT(flags)
}
internal fun KotlinClassMetadata.Class.readProperties(): List<KmProperty> =
mutableListOf<KmProperty>().apply { accept(PropertyReader(this)) }
/**
- * Reads whether the given class is a kotlin object
- */
-private class ObjectReader : KmClassVisitor() {
- var isObject: Boolean = false
- override fun visit(flags: Flags, name: ClassName) {
- isObject = Flag.Class.IS_OBJECT(flags)
- }
-}
-
-/**
* Reads the properties of a class declaration
*/
private class PropertyReader(
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/kotlin/KotlinMetadataElement.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/kotlin/KotlinMetadataElement.kt
index 963c20a..cf281c4 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/kotlin/KotlinMetadataElement.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/javac/kotlin/KotlinMetadataElement.kt
@@ -45,6 +45,9 @@
private val functionList: List<KmFunction> by lazy { classMetadata.readFunctions() }
private val constructorList: List<KmConstructor> by lazy { classMetadata.readConstructors() }
private val propertyList: List<KmProperty> by lazy { classMetadata.readProperties() }
+ private val classFlags: KotlinMetadataClassFlags by lazy {
+ KotlinMetadataClassFlags(classMetadata)
+ }
private val ExecutableElement.descriptor: String
get() = descriptor()
@@ -53,7 +56,15 @@
it.isPrimary()
}?.descriptor
- fun isObject(): Boolean = classMetadata.isObject()
+ fun isObject(): Boolean = classFlags.isObject()
+ fun isCompanionObject(): Boolean = classFlags.isCompanionObject()
+ fun isAnnotationClass(): Boolean = classFlags.isAnnotationClass()
+ fun isClass(): Boolean = classFlags.isClass()
+ fun isInterface(): Boolean = classFlags.isInterface()
+ fun isDataClass(): Boolean = classFlags.isDataClass()
+ fun isValueClass(): Boolean = classFlags.isValueClass()
+ fun isFunctionalInterface(): Boolean = classFlags.isFunctionalInterface()
+ fun isExpect(): Boolean = classFlags.isExpect()
fun getFunctionMetadata(method: ExecutableElement): KmFunction? {
check(method.kind == ElementKind.METHOD) {
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspElement.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspElement.kt
index 22fa748..37ef859 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspElement.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspElement.kt
@@ -61,4 +61,10 @@
KSFileAsOriginatingElement(it)
}
}
+
+ override val docComment: String? by lazy {
+ // TODO: Not yet implemented in KSP.
+ // https://github.com/google/ksp/issues/392
+ null
+ }
}
\ No newline at end of file
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspFileMemberContainer.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspFileMemberContainer.kt
index 9d91bb4..60df4da 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspFileMemberContainer.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspFileMemberContainer.kt
@@ -58,6 +58,12 @@
override val fallbackLocationText: String = ksFile.filePath
+ override val docComment: String? by lazy {
+ // TODO: Not yet implemented in KSP.
+ // https://github.com/google/ksp/issues/392
+ null
+ }
+
companion object {
private fun KSFile.findClassName(): String {
return annotations.firstOrNull {
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt
index c7f17c6..404c5f5 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt
@@ -40,6 +40,7 @@
import com.google.devtools.ksp.symbol.Modifier
import com.google.devtools.ksp.symbol.Origin
import com.squareup.javapoet.ClassName
+import javax.tools.Diagnostic
internal sealed class KspTypeElement(
env: KspProcessingEnv,
@@ -253,6 +254,40 @@
return declaration.classKind == ClassKind.OBJECT
}
+ override fun isCompanionObject(): Boolean {
+ return declaration.isCompanionObject
+ }
+
+ override fun isAnnotationClass(): Boolean {
+ return declaration.classKind == ClassKind.ANNOTATION_CLASS
+ }
+
+ override fun isClass(): Boolean {
+ return declaration.classKind == ClassKind.CLASS
+ }
+
+ override fun isDataClass(): Boolean {
+ return Modifier.DATA in declaration.modifiers
+ }
+
+ override fun isValueClass(): Boolean {
+ return Modifier.INLINE in declaration.modifiers
+ }
+
+ override fun isFunctionalInterface(): Boolean {
+ // TODO: Update this once KSP supports it
+ // https://github.com/google/ksp/issues/393
+ env.messager.printMessage(
+ Diagnostic.Kind.WARNING,
+ "XProcessing does not yet support checking for functional interfaces in KSP."
+ )
+ return false
+ }
+
+ override fun isExpect(): Boolean {
+ return Modifier.EXPECT in declaration.modifiers
+ }
+
override fun isFinal(): Boolean {
// workaround for https://github.com/android/kotlin/issues/128
return !isInterface() && !declaration.isOpen()
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticContinuationParameterElement.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticContinuationParameterElement.kt
index aad60a8..cab0ab2 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticContinuationParameterElement.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticContinuationParameterElement.kt
@@ -82,6 +82,9 @@
override val fallbackLocationText: String
get() = "return type of ${containing.fallbackLocationText}"
+ // Not applicable
+ override val docComment: String? get() = null
+
override fun asMemberOf(other: XType): XType {
check(other is KspType)
val continuation = env.resolver.requireContinuationClass()
diff --git a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticPropertyMethodElement.kt b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticPropertyMethodElement.kt
index 7ad376f..3c35af4 100644
--- a/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticPropertyMethodElement.kt
+++ b/room/compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/synthetic/KspSyntheticPropertyMethodElement.kt
@@ -80,6 +80,12 @@
)
}
+ override val docComment: String? by lazy {
+ // Not yet implemented in KSP.
+ // https://github.com/google/ksp/issues/392
+ null
+ }
+
final override fun asMemberOf(other: XType): XMethodType {
return KspSyntheticPropertyMethodType.create(
element = this,
@@ -226,6 +232,12 @@
return origin.field.asMemberOf(other)
}
+ override val docComment: String? by lazy {
+ // Not yet implemented in KSP.
+ // https://github.com/google/ksp/issues/392
+ null
+ }
+
override fun kindName(): String {
return "method parameter"
}
diff --git a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt
index faded70..b8727eb 100644
--- a/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt
+++ b/room/compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt
@@ -202,6 +202,12 @@
inner class InnerKotlinClass
class NestedKotlinClass
}
+ annotation class KotlinAnnotation
+ data class DataClass(val foo: Int)
+ inline class InlineClass(val foo: Int)
+ fun interface FunInterface {
+ fun foo()
+ }
""".trimIndent()
)
val javaSrc = Source.java(
@@ -213,8 +219,15 @@
}
""".trimIndent()
)
+ val javaAnnotationSrc = Source.java(
+ "JavaAnnotation",
+ """
+ public @interface JavaAnnotation {
+ }
+ """.trimIndent()
+ )
runProcessorTest(
- sources = listOf(kotlinSrc, javaSrc)
+ sources = listOf(kotlinSrc, javaSrc, javaAnnotationSrc)
) { invocation ->
fun getModifiers(element: XTypeElement): Set<String> {
val result = mutableSetOf<String>()
@@ -224,8 +237,15 @@
if (element.isProtected()) result.add("protected")
if (element.isPublic()) result.add("public")
if (element.isKotlinObject()) result.add("object")
+ if (element.isCompanionObject()) result.add("companion")
+ if (element.isFunctionalInterface()) result.add("fun")
+ if (element.isClass()) result.add("class")
+ if (element.isDataClass()) result.add("data")
+ if (element.isValueClass()) result.add("value")
+ if (element.isExpect()) result.add("expect")
if (element.isInterface()) result.add("interface")
if (element.isStatic()) result.add("static")
+ if (element.isAnnotationClass()) result.add("annotation")
return result
}
@@ -235,32 +255,54 @@
)
assertThat(getModifiers("OpenClass"))
- .containsExactly("public")
+ .containsExactly("public", "class")
assertThat(getModifiers("AbstractClass"))
- .containsExactly("abstract", "public")
+ .containsExactly("abstract", "public", "class")
assertThat(getModifiers("MyObject"))
.containsExactly("final", "public", "object")
assertThat(getModifiers("MyInterface"))
.containsExactly("abstract", "interface", "public")
assertThat(getModifiers("Final"))
- .containsExactly("final", "public")
+ .containsExactly("final", "public", "class")
assertThat(getModifiers("PrivateClass"))
.containsExactlyElementsIn(
if (invocation.isKsp) {
- listOf("private", "final")
+ listOf("private", "final", "class")
} else {
// java does not support top level private classes.
- listOf("final")
+ listOf("final", "class")
}
)
assertThat(getModifiers("OuterKotlinClass.InnerKotlinClass"))
- .containsExactly("final", "public")
+ .containsExactly("final", "public", "class")
assertThat(getModifiers("OuterKotlinClass.NestedKotlinClass"))
- .containsExactly("final", "public", "static")
+ .containsExactly("final", "public", "static", "class")
assertThat(getModifiers("OuterJavaClass.InnerJavaClass"))
- .containsExactly("public")
+ .containsExactly("public", "class")
assertThat(getModifiers("OuterJavaClass.NestedJavaClass"))
- .containsExactly("public", "static")
+ .containsExactly("public", "static", "class")
+ assertThat(getModifiers("JavaAnnotation"))
+ .containsExactly("abstract", "public", "annotation")
+ assertThat(getModifiers("KotlinAnnotation")).apply {
+ // KSP vs KAPT metadata have a difference in final vs abstract modifiers
+ // for annotation types.
+ if (invocation.isKsp) {
+ containsExactly("final", "public", "annotation")
+ } else {
+ containsExactly("abstract", "public", "annotation")
+ }
+ }
+ assertThat(getModifiers("DataClass"))
+ .containsExactly("public", "final", "class", "data")
+ assertThat(getModifiers("InlineClass"))
+ .containsExactly("public", "final", "class", "value")
+
+ if (!invocation.isKsp) {
+ // TODO: Enable for ksp too once it supports fun interfaces
+ // https://github.com/google/ksp/issues/393
+ assertThat(getModifiers("FunInterface"))
+ .containsExactly("public", "abstract", "interface", "fun")
+ }
}
}
diff --git a/room/compiler/build.gradle b/room/compiler/build.gradle
index 26115ea..7e73bf4b 100644
--- a/room/compiler/build.gradle
+++ b/room/compiler/build.gradle
@@ -36,13 +36,6 @@
main.java.srcDirs += antlrOut
}
-// Temporary hack to stop AS to adding two guavas into test's classpath
-configurations.all {
- resolutionStrategy {
- force GUAVA
- }
-}
-
configurations {
/**
* shadowed is used for dependencies which we jarjar into the library jar instead of adding it
diff --git a/security/crypto/build.gradle b/security/crypto/build.gradle
index 3fa3338..665ee42 100644
--- a/security/crypto/build.gradle
+++ b/security/crypto/build.gradle
@@ -31,11 +31,11 @@
implementation("com.google.crypto.tink:tink-android:1.5.0")
implementation("androidx.collection:collection:1.1.0")
- androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
- androidTestImplementation(ANDROIDX_TEST_CORE)
- androidTestImplementation(ANDROIDX_TEST_RUNNER)
- androidTestImplementation(ANDROIDX_TEST_RULES)
- androidTestImplementation(MOCKITO_CORE)
+ androidTestImplementation(libs.testExtJunit)
+ androidTestImplementation(libs.testCore)
+ androidTestImplementation(libs.testRunner)
+ androidTestImplementation(libs.testRules)
+ androidTestImplementation(libs.mockitoCore)
}
diff --git a/security/security-crypto-ktx/build.gradle b/security/security-crypto-ktx/build.gradle
index 12aaa02..f977990 100644
--- a/security/security-crypto-ktx/build.gradle
+++ b/security/security-crypto-ktx/build.gradle
@@ -28,13 +28,13 @@
dependencies {
api("androidx.security:security-crypto:1.1.0-alpha03")
- api(KOTLIN_STDLIB)
- androidTestImplementation(JUNIT)
- androidTestImplementation(TRUTH)
- androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
- androidTestImplementation(ANDROIDX_TEST_CORE)
- androidTestImplementation(ANDROIDX_TEST_RUNNER)
- androidTestImplementation(ANDROIDX_TEST_RULES)
+ api(libs.kotlinStdlib)
+ androidTestImplementation(libs.junit)
+ androidTestImplementation(libs.truth)
+ androidTestImplementation(libs.testExtJunit)
+ androidTestImplementation(libs.testCore)
+ androidTestImplementation(libs.testRunner)
+ androidTestImplementation(libs.testRules)
}
android {
diff --git a/slices/core/src/main/res/values-af/strings.xml b/slices/core/src/main/res/values-af/strings.xml
index c6a4763..cea048b 100644
--- a/slices/core/src/main/res/values-af/strings.xml
+++ b/slices/core/src/main/res/values-af/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Laat <xliff:g id="APP_0">%1$s</xliff:g> toe om <xliff:g id="APP_2">%2$s</xliff:g>-skyfies te wys?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"– Dit kan inligting in <xliff:g id="APP">%1$s</xliff:g> lees"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"– Dit kan handelinge binne <xliff:g id="APP">%1$s</xliff:g> uitvoer"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Laat toe"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Weier"</string>
</resources>
diff --git a/slices/core/src/main/res/values-am/strings.xml b/slices/core/src/main/res/values-am/strings.xml
index 3fc492a..96be387 100644
--- a/slices/core/src/main/res/values-am/strings.xml
+++ b/slices/core/src/main/res/values-am/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"<xliff:g id="APP_0">%1$s</xliff:g> የ<xliff:g id="APP_2">%2$s</xliff:g> ቁራጮችን እንዲያሳይ ይፈቀድለት?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- ከ<xliff:g id="APP">%1$s</xliff:g> የመጣ መረጃን ማንበብ ይችላል"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- በ<xliff:g id="APP">%1$s</xliff:g> ውስጥ እርምጃዎችን መውሰድ ይችላል"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"ፍቀድ"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"ከልክል"</string>
</resources>
diff --git a/slices/core/src/main/res/values-ar/strings.xml b/slices/core/src/main/res/values-ar/strings.xml
index 2d76a8d..c195f16 100644
--- a/slices/core/src/main/res/values-ar/strings.xml
+++ b/slices/core/src/main/res/values-ar/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"هل تريد السماح لتطبيق <xliff:g id="APP_0">%1$s</xliff:g> بعرض شرائح <xliff:g id="APP_2">%2$s</xliff:g>؟"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- إمكانية قراءة المعلومات من <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- إمكانية اتخاذ إجراءات داخل <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"سماح"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"رفض"</string>
</resources>
diff --git a/slices/core/src/main/res/values-as/strings.xml b/slices/core/src/main/res/values-as/strings.xml
index ea2a877..e8c88e0 100644
--- a/slices/core/src/main/res/values-as/strings.xml
+++ b/slices/core/src/main/res/values-as/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"<xliff:g id="APP_0">%1$s</xliff:g>ক <xliff:g id="APP_2">%2$s</xliff:g>ৰ অংশ দেখুওৱাবলৈ অনুমতি দিবনে?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- ই <xliff:g id="APP">%1$s</xliff:g>ৰ তথ্য পঢ়িব পাৰে"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- ই <xliff:g id="APP">%1$s</xliff:g>ৰ ভিতৰত কাৰ্য কৰিব পাৰে"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"অনুমতি দিয়ক"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"অস্বীকাৰ কৰক"</string>
</resources>
diff --git a/slices/core/src/main/res/values-az/strings.xml b/slices/core/src/main/res/values-az/strings.xml
index 2f1bce4..bd71cb1 100644
--- a/slices/core/src/main/res/values-az/strings.xml
+++ b/slices/core/src/main/res/values-az/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"<xliff:g id="APP_0">%1$s</xliff:g> tətbiqinə <xliff:g id="APP_2">%2$s</xliff:g> hissələrini göstərmək üçün icazə verilsin?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- <xliff:g id="APP">%1$s</xliff:g> tətbiqindən məlumat oxuya bilər"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- <xliff:g id="APP">%1$s</xliff:g> daxilində əməliyyatlar edə bilər"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"İcazə verin"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Rədd edin"</string>
</resources>
diff --git a/slices/core/src/main/res/values-b+sr+Latn/strings.xml b/slices/core/src/main/res/values-b+sr+Latn/strings.xml
index 3e20b9e..476441d 100644
--- a/slices/core/src/main/res/values-b+sr+Latn/strings.xml
+++ b/slices/core/src/main/res/values-b+sr+Latn/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Želite li da dozvolite aplikaciji <xliff:g id="APP_0">%1$s</xliff:g> da prikazuje isečke iz aplikacije <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"– Može da čita podatke iz aplikacije <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"– Može da obavlja radnje u aplikaciji <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Dozvoli"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Odbij"</string>
</resources>
diff --git a/slices/core/src/main/res/values-be/strings.xml b/slices/core/src/main/res/values-be/strings.xml
index 84cd4a22..85988a8 100644
--- a/slices/core/src/main/res/values-be/strings.xml
+++ b/slices/core/src/main/res/values-be/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Дазволіць праграме <xliff:g id="APP_0">%1$s</xliff:g> паказваць фрагменты праграмы <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- Можа счытваць інфармацыю з праграмы <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- Можа выконваць дзеянні ў праграме <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Дазволіць"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Адмовіць"</string>
</resources>
diff --git a/slices/core/src/main/res/values-bg/strings.xml b/slices/core/src/main/res/values-bg/strings.xml
index dcd2606..1ff6667 100644
--- a/slices/core/src/main/res/values-bg/strings.xml
+++ b/slices/core/src/main/res/values-bg/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Ще разрешите ли на <xliff:g id="APP_0">%1$s</xliff:g> да показва части от <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"– Може да чете информация от <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"– Може да предприема действия в/ъв <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Разрешаване"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Отказ"</string>
</resources>
diff --git a/slices/core/src/main/res/values-bn/strings.xml b/slices/core/src/main/res/values-bn/strings.xml
index 4b46f9b..c9556dd 100644
--- a/slices/core/src/main/res/values-bn/strings.xml
+++ b/slices/core/src/main/res/values-bn/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"<xliff:g id="APP_0">%1$s</xliff:g> অ্যাপটিকে <xliff:g id="APP_2">%2$s</xliff:g> এর অংশ দেখানোর অনুমতি দেবেন?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- এটি <xliff:g id="APP">%1$s</xliff:g> এর তথ্য অ্যাক্সেস করতে পারবে"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- এটি <xliff:g id="APP">%1$s</xliff:g> এর মধ্যে কাজ করতে পারবে"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"অনুমতি দিন"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"খারিজ করুন"</string>
</resources>
diff --git a/slices/core/src/main/res/values-bs/strings.xml b/slices/core/src/main/res/values-bs/strings.xml
index b203a79..ed3104d5 100644
--- a/slices/core/src/main/res/values-bs/strings.xml
+++ b/slices/core/src/main/res/values-bs/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Dozvoliti aplikaciji <xliff:g id="APP_0">%1$s</xliff:g> prikazivanje isječaka aplikacije <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- Može čitati informacije iz aplikacije <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- Može poduzeti radnje u aplikaciji <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Dozvoli"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Odbij"</string>
</resources>
diff --git a/slices/core/src/main/res/values-ca/strings.xml b/slices/core/src/main/res/values-ca/strings.xml
index ac93488..5bff344 100644
--- a/slices/core/src/main/res/values-ca/strings.xml
+++ b/slices/core/src/main/res/values-ca/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Vols permetre que <xliff:g id="APP_0">%1$s</xliff:g> mostri porcions de l\'aplicació <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- Pot llegir informació de l\'aplicació <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- Pot dur a terme accions dins de l\'aplicació <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Permet"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Denega"</string>
</resources>
diff --git a/slices/core/src/main/res/values-cs/strings.xml b/slices/core/src/main/res/values-cs/strings.xml
index da1053b..432ed94 100644
--- a/slices/core/src/main/res/values-cs/strings.xml
+++ b/slices/core/src/main/res/values-cs/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Povolit aplikaci <xliff:g id="APP_0">%1$s</xliff:g> zobrazovat ukázky z aplikace <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"– Může číst informace z aplikace <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"– Může provádět akce v aplikaci <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Povolit"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Zamítnout"</string>
</resources>
diff --git a/slices/core/src/main/res/values-da/strings.xml b/slices/core/src/main/res/values-da/strings.xml
index 67a7ed0..eb148eb 100644
--- a/slices/core/src/main/res/values-da/strings.xml
+++ b/slices/core/src/main/res/values-da/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Vil du give <xliff:g id="APP_0">%1$s</xliff:g> tilladelse til at vise eksempler fra <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- Den kan læse oplysninger fra <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- Den kan foretage handlinger i <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Tillad"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Afvis"</string>
</resources>
diff --git a/slices/core/src/main/res/values-de/strings.xml b/slices/core/src/main/res/values-de/strings.xml
index 419fa20..b14bdb3 100644
--- a/slices/core/src/main/res/values-de/strings.xml
+++ b/slices/core/src/main/res/values-de/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"<xliff:g id="APP_0">%1$s</xliff:g> erlauben, Teile von <xliff:g id="APP_2">%2$s</xliff:g> anzuzeigen?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"– Darf Informationen aus <xliff:g id="APP">%1$s</xliff:g> lesen"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"– Darf Aktionen in <xliff:g id="APP">%1$s</xliff:g> ausführen"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Zulassen"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Ablehnen"</string>
</resources>
diff --git a/slices/core/src/main/res/values-el/strings.xml b/slices/core/src/main/res/values-el/strings.xml
index 898d2ea..8ae97bf 100644
--- a/slices/core/src/main/res/values-el/strings.xml
+++ b/slices/core/src/main/res/values-el/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Να επιτρέπεται στην εφαρμογή <xliff:g id="APP_0">%1$s</xliff:g> να εμφανίζει τμήματα της εφαρμογής <xliff:g id="APP_2">%2$s</xliff:g>;"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- Μπορεί να διαβάζει πληροφορίες από την εφαρμογή <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- Μπορεί να εκτελεί ενέργειες εντός της εφαρμογής <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Να επιτρέπεται"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Να μην επιτρέπεται"</string>
</resources>
diff --git a/slices/core/src/main/res/values-en-rAU/strings.xml b/slices/core/src/main/res/values-en-rAU/strings.xml
index b4c1f11..753ea3e 100644
--- a/slices/core/src/main/res/values-en-rAU/strings.xml
+++ b/slices/core/src/main/res/values-en-rAU/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Allow <xliff:g id="APP_0">%1$s</xliff:g> to show <xliff:g id="APP_2">%2$s</xliff:g> slices?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"– It can read information from <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"– It can take actions inside <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Allow"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Deny"</string>
</resources>
diff --git a/slices/core/src/main/res/values-en-rCA/strings.xml b/slices/core/src/main/res/values-en-rCA/strings.xml
index b4c1f11..753ea3e 100644
--- a/slices/core/src/main/res/values-en-rCA/strings.xml
+++ b/slices/core/src/main/res/values-en-rCA/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Allow <xliff:g id="APP_0">%1$s</xliff:g> to show <xliff:g id="APP_2">%2$s</xliff:g> slices?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"– It can read information from <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"– It can take actions inside <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Allow"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Deny"</string>
</resources>
diff --git a/slices/core/src/main/res/values-en-rGB/strings.xml b/slices/core/src/main/res/values-en-rGB/strings.xml
index b4c1f11..753ea3e 100644
--- a/slices/core/src/main/res/values-en-rGB/strings.xml
+++ b/slices/core/src/main/res/values-en-rGB/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Allow <xliff:g id="APP_0">%1$s</xliff:g> to show <xliff:g id="APP_2">%2$s</xliff:g> slices?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"– It can read information from <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"– It can take actions inside <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Allow"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Deny"</string>
</resources>
diff --git a/slices/core/src/main/res/values-en-rIN/strings.xml b/slices/core/src/main/res/values-en-rIN/strings.xml
index b4c1f11..753ea3e 100644
--- a/slices/core/src/main/res/values-en-rIN/strings.xml
+++ b/slices/core/src/main/res/values-en-rIN/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Allow <xliff:g id="APP_0">%1$s</xliff:g> to show <xliff:g id="APP_2">%2$s</xliff:g> slices?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"– It can read information from <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"– It can take actions inside <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Allow"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Deny"</string>
</resources>
diff --git a/slices/core/src/main/res/values-en-rXC/strings.xml b/slices/core/src/main/res/values-en-rXC/strings.xml
index efb1d5f..22c86bc 100644
--- a/slices/core/src/main/res/values-en-rXC/strings.xml
+++ b/slices/core/src/main/res/values-en-rXC/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Allow <xliff:g id="APP_0">%1$s</xliff:g> to show <xliff:g id="APP_2">%2$s</xliff:g> slices?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- It can read information from <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- It can take actions inside <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Allow"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Deny"</string>
</resources>
diff --git a/slices/core/src/main/res/values-es-rUS/strings.xml b/slices/core/src/main/res/values-es-rUS/strings.xml
index 31746f0..d004921 100644
--- a/slices/core/src/main/res/values-es-rUS/strings.xml
+++ b/slices/core/src/main/res/values-es-rUS/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"¿Permitir que <xliff:g id="APP_0">%1$s</xliff:g> muestre fragmentos de <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- Puede leer información de <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- Puede realizar acciones en <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Permitir"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Rechazar"</string>
</resources>
diff --git a/slices/core/src/main/res/values-es/strings.xml b/slices/core/src/main/res/values-es/strings.xml
index 3a65289..cdd8514 100644
--- a/slices/core/src/main/res/values-es/strings.xml
+++ b/slices/core/src/main/res/values-es/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"¿Permitir que <xliff:g id="APP_0">%1$s</xliff:g> muestre fragmentos de <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- Puede leer información de <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- Puede realizar acciones en <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Permitir"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Denegar"</string>
</resources>
diff --git a/slices/core/src/main/res/values-et/strings.xml b/slices/core/src/main/res/values-et/strings.xml
index 5462dd7..7d92095 100644
--- a/slices/core/src/main/res/values-et/strings.xml
+++ b/slices/core/src/main/res/values-et/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Kas lubada rakendusel <xliff:g id="APP_0">%1$s</xliff:g> näidata rakenduse <xliff:g id="APP_2">%2$s</xliff:g> lõike?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- See saab lugeda teavet rakendusest <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- See saab rakenduses <xliff:g id="APP">%1$s</xliff:g> toiminguid teha"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Lubamine"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Keelamine"</string>
</resources>
diff --git a/slices/core/src/main/res/values-eu/strings.xml b/slices/core/src/main/res/values-eu/strings.xml
index 8776fec..bea035d 100644
--- a/slices/core/src/main/res/values-eu/strings.xml
+++ b/slices/core/src/main/res/values-eu/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"<xliff:g id="APP_2">%2$s</xliff:g> aplikazioaren zatiak erakusteko baimena eman nahi diozu <xliff:g id="APP_0">%1$s</xliff:g> aplikazioari?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- <xliff:g id="APP">%1$s</xliff:g> aplikazioaren informazioa irakur dezake."</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- <xliff:g id="APP">%1$s</xliff:g> aplikazioan ekintzak gauza ditzake."</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Baimendu"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Ukatu"</string>
</resources>
diff --git a/slices/core/src/main/res/values-fa/strings.xml b/slices/core/src/main/res/values-fa/strings.xml
index d5e0416..2abd384 100644
--- a/slices/core/src/main/res/values-fa/strings.xml
+++ b/slices/core/src/main/res/values-fa/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"به <xliff:g id="APP_0">%1$s</xliff:g> اجازه داده شود تکههای <xliff:g id="APP_2">%2$s</xliff:g> را نشان دهد؟"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- میتواند اطلاعات <xliff:g id="APP">%1$s</xliff:g> را بخواند"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- میتواند در <xliff:g id="APP">%1$s</xliff:g> اقدام انجام دهد"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"مجاز بودن"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"مجاز نبودن"</string>
</resources>
diff --git a/slices/core/src/main/res/values-fi/strings.xml b/slices/core/src/main/res/values-fi/strings.xml
index d682f1a..b826e98 100644
--- a/slices/core/src/main/res/values-fi/strings.xml
+++ b/slices/core/src/main/res/values-fi/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Saako <xliff:g id="APP_0">%1$s</xliff:g> näyttää osia sovelluksesta <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"– Se voi lukea tietoja sovelluksesta <xliff:g id="APP">%1$s</xliff:g>."</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"– Se voi suorittaa toimintoja sovelluksessa <xliff:g id="APP">%1$s</xliff:g>."</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Salli"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Estä"</string>
</resources>
diff --git a/slices/core/src/main/res/values-fr-rCA/strings.xml b/slices/core/src/main/res/values-fr-rCA/strings.xml
index a0e55d3..a5d3fd0 100644
--- a/slices/core/src/main/res/values-fr-rCA/strings.xml
+++ b/slices/core/src/main/res/values-fr-rCA/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Autoriser <xliff:g id="APP_0">%1$s</xliff:g> à afficher <xliff:g id="APP_2">%2$s</xliff:g> tranches?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- Il peut lire de l\'information de <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- Il peut effectuer des actions dans <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Autoriser"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Refuser"</string>
</resources>
diff --git a/slices/core/src/main/res/values-fr/strings.xml b/slices/core/src/main/res/values-fr/strings.xml
index 65f06bc..724da28 100644
--- a/slices/core/src/main/res/values-fr/strings.xml
+++ b/slices/core/src/main/res/values-fr/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Autoriser <xliff:g id="APP_0">%1$s</xliff:g> à afficher des éléments de <xliff:g id="APP_2">%2$s</xliff:g> ?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- Accès aux informations de <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- Capacité d\'action dans <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Autoriser"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Refuser"</string>
</resources>
diff --git a/slices/core/src/main/res/values-gl/strings.xml b/slices/core/src/main/res/values-gl/strings.xml
index 50befe5..5ef1215 100644
--- a/slices/core/src/main/res/values-gl/strings.xml
+++ b/slices/core/src/main/res/values-gl/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Queres permitir que <xliff:g id="APP_0">%1$s</xliff:g> mostre fragmentos de aplicación de <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- Pode ler información da aplicación <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- Pode levar a cabo accións dentro da aplicación <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Permitir"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Denegar"</string>
</resources>
diff --git a/slices/core/src/main/res/values-gu/strings.xml b/slices/core/src/main/res/values-gu/strings.xml
index 804b774..9b8f733 100644
--- a/slices/core/src/main/res/values-gu/strings.xml
+++ b/slices/core/src/main/res/values-gu/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"<xliff:g id="APP_0">%1$s</xliff:g>ને <xliff:g id="APP_2">%2$s</xliff:g> સ્લાઇસ બતાવવાની મંજૂરી આપીએ?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- તે <xliff:g id="APP">%1$s</xliff:g>માંથી માહિતી વાંચી શકે છે"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- તે <xliff:g id="APP">%1$s</xliff:g>ની અંદર ક્રિયાઓ કરી શકે છે"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"મંજૂરી આપો"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"નકારો"</string>
</resources>
diff --git a/slices/core/src/main/res/values-hi/strings.xml b/slices/core/src/main/res/values-hi/strings.xml
index 9dfe2b0..f5215cb 100644
--- a/slices/core/src/main/res/values-hi/strings.xml
+++ b/slices/core/src/main/res/values-hi/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"<xliff:g id="APP_0">%1$s</xliff:g> को <xliff:g id="APP_2">%2$s</xliff:g> के हिस्से (स्लाइस) दिखाने की मंज़ूरी दें?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- यह <xliff:g id="APP">%1$s</xliff:g> से जानकारी पा सकता है"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- यह <xliff:g id="APP">%1$s</xliff:g> में कार्रवाई कर सकता है"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"अनुमति दें"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"अनुमति न दें"</string>
</resources>
diff --git a/slices/core/src/main/res/values-hr/strings.xml b/slices/core/src/main/res/values-hr/strings.xml
index 9e614b5..b7980bd 100644
--- a/slices/core/src/main/res/values-hr/strings.xml
+++ b/slices/core/src/main/res/values-hr/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Želite li dopustiti aplikaciji <xliff:g id="APP_0">%1$s</xliff:g> da prikazuje isječke aplikacije <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"– može čitati informacije aplikacije <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"– može vršiti radnje u aplikaciji <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Dopusti"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Odbij"</string>
</resources>
diff --git a/slices/core/src/main/res/values-hu/strings.xml b/slices/core/src/main/res/values-hu/strings.xml
index 004829f..89b800c 100644
--- a/slices/core/src/main/res/values-hu/strings.xml
+++ b/slices/core/src/main/res/values-hu/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Engedélyezi a(z) <xliff:g id="APP_0">%1$s</xliff:g> alkalmazásnak, hogy részleteket mutasson a(z) <xliff:g id="APP_2">%2$s</xliff:g> alkalmazásból?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"– Információkat olvashat a(z) <xliff:g id="APP">%1$s</xliff:g> alkalmazásból"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"– Műveleteket végezhet a(z) <xliff:g id="APP">%1$s</xliff:g> alkalmazáson belül"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Engedélyezés"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Elutasítás"</string>
</resources>
diff --git a/slices/core/src/main/res/values-hy/strings.xml b/slices/core/src/main/res/values-hy/strings.xml
index 1d716d7..2512ee9 100644
--- a/slices/core/src/main/res/values-hy/strings.xml
+++ b/slices/core/src/main/res/values-hy/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Թույլատրե՞լ <xliff:g id="APP_0">%1$s</xliff:g> հավելվածին ցուցադրել հատվածներ <xliff:g id="APP_2">%2$s</xliff:g> հավելվածից"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"– Կարող է կարդալ տեղեկություններ <xliff:g id="APP">%1$s</xliff:g> հավելվածից"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"– Կարող է կատարել գործողություններ <xliff:g id="APP">%1$s</xliff:g> հավելվածում"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Թույլատրել"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Մերժել"</string>
</resources>
diff --git a/slices/core/src/main/res/values-in/strings.xml b/slices/core/src/main/res/values-in/strings.xml
index 860ed1c..87afa02 100644
--- a/slices/core/src/main/res/values-in/strings.xml
+++ b/slices/core/src/main/res/values-in/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Izinkan <xliff:g id="APP_0">%1$s</xliff:g> menampilkan potongan <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- Dapat membaca informasi dari <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- Dapat mengambil tindakan di dalam <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Izinkan"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Tolak"</string>
</resources>
diff --git a/slices/core/src/main/res/values-is/strings.xml b/slices/core/src/main/res/values-is/strings.xml
index da79a76..5845ff1 100644
--- a/slices/core/src/main/res/values-is/strings.xml
+++ b/slices/core/src/main/res/values-is/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Viltu leyfa <xliff:g id="APP_0">%1$s</xliff:g> að sýna sneiðar úr <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- Það getur lesið upplýsingar úr <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- Það getur gripið til aðgerða í <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Leyfa"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Hafna"</string>
</resources>
diff --git a/slices/core/src/main/res/values-it/strings.xml b/slices/core/src/main/res/values-it/strings.xml
index 5518d66..58509f9 100644
--- a/slices/core/src/main/res/values-it/strings.xml
+++ b/slices/core/src/main/res/values-it/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Vuoi consentire all\'app <xliff:g id="APP_0">%1$s</xliff:g> di mostrare porzioni dell\'app <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- Può leggere informazioni dell\'app <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- Può compiere azioni nell\'app <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Consenti"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Rifiuta"</string>
</resources>
diff --git a/slices/core/src/main/res/values-iw/strings.xml b/slices/core/src/main/res/values-iw/strings.xml
index 2151548..4a6791f 100644
--- a/slices/core/src/main/res/values-iw/strings.xml
+++ b/slices/core/src/main/res/values-iw/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"האם לאפשר ל-<xliff:g id="APP_0">%1$s</xliff:g> להציג חלקים מ-<xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- תהיה לה אפשרות לקרוא מידע מהאפליקציה <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- תהיה לה יכולת לנקוט פעולה בתוך <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"אישור"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"אני לא מרשה"</string>
</resources>
diff --git a/slices/core/src/main/res/values-ja/strings.xml b/slices/core/src/main/res/values-ja/strings.xml
index 1c28f4c..e619da5 100644
--- a/slices/core/src/main/res/values-ja/strings.xml
+++ b/slices/core/src/main/res/values-ja/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"<xliff:g id="APP_2">%2$s</xliff:g> のスライスの表示を <xliff:g id="APP_0">%1$s</xliff:g> に許可しますか?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- <xliff:g id="APP">%1$s</xliff:g> からの情報を読み取ることができます"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- <xliff:g id="APP">%1$s</xliff:g> 内部で操作することがあります"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"許可"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"拒否"</string>
</resources>
diff --git a/slices/core/src/main/res/values-ka/strings.xml b/slices/core/src/main/res/values-ka/strings.xml
index b78206c..2558632 100644
--- a/slices/core/src/main/res/values-ka/strings.xml
+++ b/slices/core/src/main/res/values-ka/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"ანიჭებთ ნებართვას <xliff:g id="APP_0">%1$s</xliff:g>-ს, აჩვენოს <xliff:g id="APP_2">%2$s</xliff:g>-ის ფრაგმენტები?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- მას შეუძლია ინფორმაციის <xliff:g id="APP">%1$s</xliff:g>-დან წაკითხვა"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- მას შეუძლია ქმედებების <xliff:g id="APP">%1$s</xliff:g>-ში განხორციელება"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"დაშვება"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"უარყოფა"</string>
</resources>
diff --git a/slices/core/src/main/res/values-kk/strings.xml b/slices/core/src/main/res/values-kk/strings.xml
index fcb0509..21f8465 100644
--- a/slices/core/src/main/res/values-kk/strings.xml
+++ b/slices/core/src/main/res/values-kk/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"<xliff:g id="APP_0">%1$s</xliff:g> қолданбасына <xliff:g id="APP_2">%2$s</xliff:g> қолданбасының үзінділерін көрсетуге рұқсат берілсін бе?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- <xliff:g id="APP">%1$s</xliff:g> қолданбасындағы ақпаратты оқи алады"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- <xliff:g id="APP">%1$s</xliff:g> қолданбасында әрекет ете алады"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Рұқсат беру"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Тыйым салу"</string>
</resources>
diff --git a/slices/core/src/main/res/values-km/strings.xml b/slices/core/src/main/res/values-km/strings.xml
index 5d0a988..1a5755b 100644
--- a/slices/core/src/main/res/values-km/strings.xml
+++ b/slices/core/src/main/res/values-km/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"អនុញ្ញាតឱ្យ <xliff:g id="APP_0">%1$s</xliff:g> បង្ហាញស្ថិតិប្រើប្រាស់របស់ <xliff:g id="APP_2">%2$s</xliff:g> ?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- វាអាចអានព័ត៌មានពី <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- វាអាចធ្វើសកម្មភាពនៅក្នុង <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"អនុញ្ញាត"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"បដិសេធ"</string>
</resources>
diff --git a/slices/core/src/main/res/values-kn/strings.xml b/slices/core/src/main/res/values-kn/strings.xml
index 7fe32e0..0f14a36 100644
--- a/slices/core/src/main/res/values-kn/strings.xml
+++ b/slices/core/src/main/res/values-kn/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"<xliff:g id="APP_2">%2$s</xliff:g> ಸ್ಲೈಸ್ಗಳನ್ನು ತೋರಿಸಲು <xliff:g id="APP_0">%1$s</xliff:g> ಅನ್ನು ಅನುಮತಿಸುವುದೇ?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- ಇದು <xliff:g id="APP">%1$s</xliff:g> ನಿಂದ ಮಾಹಿತಿಯನ್ನು ಓದಬಹುದು"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- ಇದು <xliff:g id="APP">%1$s</xliff:g> ಒಳಗಡೆ ಕ್ರಿಯೆಗಳನ್ನು ತೆಗೆದುಕೊಳ್ಳಬಹುದು"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"ಅನುಮತಿಸಿ"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"ನಿರಾಕರಿಸಿ"</string>
</resources>
diff --git a/slices/core/src/main/res/values-ko/strings.xml b/slices/core/src/main/res/values-ko/strings.xml
index 93c62f0..81a6707 100644
--- a/slices/core/src/main/res/values-ko/strings.xml
+++ b/slices/core/src/main/res/values-ko/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"<xliff:g id="APP_0">%1$s</xliff:g>에서 <xliff:g id="APP_2">%2$s</xliff:g>의 슬라이스를 표시하도록 허용하시겠습니까?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- <xliff:g id="APP">%1$s</xliff:g>의 정보를 읽을 수 있음"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- <xliff:g id="APP">%1$s</xliff:g>에서 작업할 수 있음"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"허용"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"거부"</string>
</resources>
diff --git a/slices/core/src/main/res/values-ky/strings.xml b/slices/core/src/main/res/values-ky/strings.xml
index 9615740..9a26322 100644
--- a/slices/core/src/main/res/values-ky/strings.xml
+++ b/slices/core/src/main/res/values-ky/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"<xliff:g id="APP_0">%1$s</xliff:g> колдонмосуна <xliff:g id="APP_2">%2$s</xliff:g> үлгүлөрүн көрсөтүүгө уруксат берилсинби?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- <xliff:g id="APP">%1$s</xliff:g> колдонмосунун маалыматын окуйт"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- <xliff:g id="APP">%1$s</xliff:g> колдонмосунда аракеттерди аткарат"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Уруксат берүү"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Тыюу салынат"</string>
</resources>
diff --git a/slices/core/src/main/res/values-lo/strings.xml b/slices/core/src/main/res/values-lo/strings.xml
index 9b18f5b..aeeedc9 100644
--- a/slices/core/src/main/res/values-lo/strings.xml
+++ b/slices/core/src/main/res/values-lo/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"ອະນຸຍາດ <xliff:g id="APP_0">%1$s</xliff:g> ໃຫ້ສະແດງ <xliff:g id="APP_2">%2$s</xliff:g> ສະໄລ້ບໍ?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- ມັນສາມາດອ່ານຂໍ້ມູນຈາກ <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- ມັນສາມາດໃຊ້ຄຳສັ່ງພາຍໃນ <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"ອະນຸຍາດ"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"ປະຕິເສດ"</string>
</resources>
diff --git a/slices/core/src/main/res/values-lt/strings.xml b/slices/core/src/main/res/values-lt/strings.xml
index 1e88cd6..604ee1d 100644
--- a/slices/core/src/main/res/values-lt/strings.xml
+++ b/slices/core/src/main/res/values-lt/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Leisti „<xliff:g id="APP_0">%1$s</xliff:g>“ rodyti „<xliff:g id="APP_2">%2$s</xliff:g>“ fragmentus?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"– Gali nuskaityti informaciją iš „<xliff:g id="APP">%1$s</xliff:g>“"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"– Gali imtis veiksmų programoje „<xliff:g id="APP">%1$s</xliff:g>“"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Leisti"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Atmesti"</string>
</resources>
diff --git a/slices/core/src/main/res/values-lv/strings.xml b/slices/core/src/main/res/values-lv/strings.xml
index 1f3ccde..60dbac8 100644
--- a/slices/core/src/main/res/values-lv/strings.xml
+++ b/slices/core/src/main/res/values-lv/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Vai atļaut lietotnei <xliff:g id="APP_0">%1$s</xliff:g> rādīt lietotnes <xliff:g id="APP_2">%2$s</xliff:g> sadaļas?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- Var lasīt informāciju no lietotnes <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- Var veikt darbības lietotnē <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Atļaut"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Neatļaut"</string>
</resources>
diff --git a/slices/core/src/main/res/values-mk/strings.xml b/slices/core/src/main/res/values-mk/strings.xml
index a5ccc53..fd0a8d2 100644
--- a/slices/core/src/main/res/values-mk/strings.xml
+++ b/slices/core/src/main/res/values-mk/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Да се дозволи <xliff:g id="APP_0">%1$s</xliff:g> да прикажува делови од <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- Може да чита информации од <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- Може да презема дејства во <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Дозволете"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Одбијте"</string>
</resources>
diff --git a/slices/core/src/main/res/values-ml/strings.xml b/slices/core/src/main/res/values-ml/strings.xml
index 21802b7..bb565fa 100644
--- a/slices/core/src/main/res/values-ml/strings.xml
+++ b/slices/core/src/main/res/values-ml/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"<xliff:g id="APP_2">%2$s</xliff:g> സ്ലൈസുകൾ കാണിക്കാൻ <xliff:g id="APP_0">%1$s</xliff:g>-നെ അനുവദിക്കണോ?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- ഇതിന് <xliff:g id="APP">%1$s</xliff:g>-ൽ നിന്ന് വിവരങ്ങൾ വായിക്കാനാകും"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- ഇതിന് <xliff:g id="APP">%1$s</xliff:g>-നുള്ളിൽ പ്രവർത്തനങ്ങൾ ചെയ്യാനാകും"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"അനുവദിക്കുക"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"നിരസിക്കുക"</string>
</resources>
diff --git a/slices/core/src/main/res/values-mn/strings.xml b/slices/core/src/main/res/values-mn/strings.xml
index 8e0661a..ce2b5b9 100644
--- a/slices/core/src/main/res/values-mn/strings.xml
+++ b/slices/core/src/main/res/values-mn/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"<xliff:g id="APP_0">%1$s</xliff:g>-д <xliff:g id="APP_2">%2$s</xliff:g>-н хэсгүүдийг харуулахыг зөвшөөрөх үү?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- Энэ <xliff:g id="APP">%1$s</xliff:g>-с мэдээлэл унших боломжтой"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- Энэ <xliff:g id="APP">%1$s</xliff:g> дотор үйлдэл хийх боломжтой"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Зөвшөөрөх"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Татгалзах"</string>
</resources>
diff --git a/slices/core/src/main/res/values-mr/strings.xml b/slices/core/src/main/res/values-mr/strings.xml
index ab48521..7ed173d 100644
--- a/slices/core/src/main/res/values-mr/strings.xml
+++ b/slices/core/src/main/res/values-mr/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"<xliff:g id="APP_0">%1$s</xliff:g> ला <xliff:g id="APP_2">%2$s</xliff:g> चे तुकडे दाखवण्याची अनुमती द्यायची का?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- ते <xliff:g id="APP">%1$s</xliff:g> ची माहिती वाचू शकते"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- ते <xliff:g id="APP">%1$s</xliff:g> मध्ये कृती करू शकते"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"अनुमती द्या"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"नकार द्या"</string>
</resources>
diff --git a/slices/core/src/main/res/values-ms/strings.xml b/slices/core/src/main/res/values-ms/strings.xml
index 4b20a63..e4dc766 100644
--- a/slices/core/src/main/res/values-ms/strings.xml
+++ b/slices/core/src/main/res/values-ms/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Benarkan <xliff:g id="APP_0">%1$s</xliff:g> menunjukkan hirisan <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- Hos hirisan boleh membaca maklumat daripada <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- Hos hirisan boleh mengambil tindakan dalam <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Benarkan"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Tolak"</string>
</resources>
diff --git a/slices/core/src/main/res/values-my/strings.xml b/slices/core/src/main/res/values-my/strings.xml
index 666640ef..109f680 100644
--- a/slices/core/src/main/res/values-my/strings.xml
+++ b/slices/core/src/main/res/values-my/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"<xliff:g id="APP_0">%1$s</xliff:g> အား <xliff:g id="APP_2">%2$s</xliff:g> ၏အချပ်များ ပြသခွင့်ပြုပါသလား။"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- ၎င်းသည် <xliff:g id="APP">%1$s</xliff:g> မှ အချက်အလက်ကို ဖတ်နိုင်သည်"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- ၎င်းသည် <xliff:g id="APP">%1$s</xliff:g> အတွင်း လုပ်ဆောင်ချက်များ ပြုလုပ်နိုင်သည်"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"ခွင့်ပြုရန်"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"ငြင်းပယ်ရန်"</string>
</resources>
diff --git a/slices/core/src/main/res/values-nb/strings.xml b/slices/core/src/main/res/values-nb/strings.xml
index 9448d03..d3246d5 100644
--- a/slices/core/src/main/res/values-nb/strings.xml
+++ b/slices/core/src/main/res/values-nb/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Vil du tillate at <xliff:g id="APP_0">%1$s</xliff:g> viser <xliff:g id="APP_2">%2$s</xliff:g>-utsnitt?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"– Den kan lese informasjon fra <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"– Den kan utføre handlinger i <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Tillat"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Ikke tillat"</string>
</resources>
diff --git a/slices/core/src/main/res/values-ne/strings.xml b/slices/core/src/main/res/values-ne/strings.xml
index bfa42445..baba49e 100644
--- a/slices/core/src/main/res/values-ne/strings.xml
+++ b/slices/core/src/main/res/values-ne/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"<xliff:g id="APP_0">%1$s</xliff:g> लाई <xliff:g id="APP_2">%2$s</xliff:g> का स्लाइसहरू देखाउन अनुमति दिने हो?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- यसले <xliff:g id="APP">%1$s</xliff:g> को जानकारी पढ्न सक्छ"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- यसले <xliff:g id="APP">%1$s</xliff:g> भित्र कारबाही गर्न सक्छ"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"अनुमति दिनुहोस्"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"नदिने"</string>
</resources>
diff --git a/slices/core/src/main/res/values-nl/strings.xml b/slices/core/src/main/res/values-nl/strings.xml
index 4d8006e..ffbb174 100644
--- a/slices/core/src/main/res/values-nl/strings.xml
+++ b/slices/core/src/main/res/values-nl/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"<xliff:g id="APP_0">%1$s</xliff:g> toestaan om segmenten van <xliff:g id="APP_2">%2$s</xliff:g> te tonen?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- Deze kan informatie lezen van <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- Deze kan acties uitvoeren in <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Toestaan"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Weigeren"</string>
</resources>
diff --git a/slices/core/src/main/res/values-or/strings.xml b/slices/core/src/main/res/values-or/strings.xml
index a757613..672f51c 100644
--- a/slices/core/src/main/res/values-or/strings.xml
+++ b/slices/core/src/main/res/values-or/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"<xliff:g id="APP_2">%2$s</xliff:g> ସ୍ଲାଇସ୍କୁ ଦେଖାଇବା ପାଇଁ <xliff:g id="APP_0">%1$s</xliff:g>କୁ ଅନୁମତି ଦେବେ?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- ଏହା <xliff:g id="APP">%1$s</xliff:g>ରୁ ସୂଚନାକୁ ପଢ଼ିପାରିବ"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- ଏହା <xliff:g id="APP">%1$s</xliff:g> ଭିତରେ କାମ କରିପାରିବ"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"ଅନୁମତି ଦିଅନ୍ତୁ"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"ଅଗ୍ରାହ୍ୟ କରନ୍ତୁ"</string>
</resources>
diff --git a/slices/core/src/main/res/values-pa/strings.xml b/slices/core/src/main/res/values-pa/strings.xml
index d68cc39..03c926e 100644
--- a/slices/core/src/main/res/values-pa/strings.xml
+++ b/slices/core/src/main/res/values-pa/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"ਕੀ <xliff:g id="APP_0">%1$s</xliff:g> ਨੂੰ <xliff:g id="APP_2">%2$s</xliff:g> ਦੇ ਹਿੱਸੇ ਦਿਖਾਉਣ ਦੇਣੇ ਹਨ?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- ਇਹ <xliff:g id="APP">%1$s</xliff:g> ਵਿੱਚੋਂ ਜਾਣਕਾਰੀ ਪੜ੍ਹ ਸਕਦਾ ਹੈ"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- ਇਸ <xliff:g id="APP">%1$s</xliff:g> ਦੇ ਅੰਦਰ ਕਾਰਵਾਈਆਂ ਕਰ ਸਕਦਾ ਹੈ"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"ਕਰਨ ਦਿਓ"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"ਅਸਵੀਕਾਰ ਕਰੋ"</string>
</resources>
diff --git a/slices/core/src/main/res/values-pl/strings.xml b/slices/core/src/main/res/values-pl/strings.xml
index ea32902..7ef7e74 100644
--- a/slices/core/src/main/res/values-pl/strings.xml
+++ b/slices/core/src/main/res/values-pl/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Zezwolić aplikacji <xliff:g id="APP_0">%1$s</xliff:g> na pokazywanie wycinków z aplikacji <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- Może odczytywać informacje z aplikacji <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- Może wykonywać działania w aplikacji <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Zezwól"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Odmów"</string>
</resources>
diff --git a/slices/core/src/main/res/values-pt-rBR/strings.xml b/slices/core/src/main/res/values-pt-rBR/strings.xml
index b58e786..4920aed 100644
--- a/slices/core/src/main/res/values-pt-rBR/strings.xml
+++ b/slices/core/src/main/res/values-pt-rBR/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Permitir que <xliff:g id="APP_0">%1$s</xliff:g> mostre partes do app <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- Pode ler informações do app <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- Pode realizar ações no app <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Permitir"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Negar"</string>
</resources>
diff --git a/slices/core/src/main/res/values-pt-rPT/strings.xml b/slices/core/src/main/res/values-pt-rPT/strings.xml
index 832b5e5..cdf0555 100644
--- a/slices/core/src/main/res/values-pt-rPT/strings.xml
+++ b/slices/core/src/main/res/values-pt-rPT/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Permitir que a app <xliff:g id="APP_0">%1$s</xliff:g> mostre partes da app <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- Pode ler informações da app <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- Pode realizar ações na app <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Permitir"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Recusar"</string>
</resources>
diff --git a/slices/core/src/main/res/values-pt/strings.xml b/slices/core/src/main/res/values-pt/strings.xml
index b58e786..4920aed 100644
--- a/slices/core/src/main/res/values-pt/strings.xml
+++ b/slices/core/src/main/res/values-pt/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Permitir que <xliff:g id="APP_0">%1$s</xliff:g> mostre partes do app <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- Pode ler informações do app <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- Pode realizar ações no app <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Permitir"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Negar"</string>
</resources>
diff --git a/slices/core/src/main/res/values-ro/strings.xml b/slices/core/src/main/res/values-ro/strings.xml
index 2db5e57..68da8f3 100644
--- a/slices/core/src/main/res/values-ro/strings.xml
+++ b/slices/core/src/main/res/values-ro/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Permiteți <xliff:g id="APP_0">%1$s</xliff:g> să afișeze porțiuni din <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- Poate citi informații din <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- Poate efectua acțiuni în <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Permiteți"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Refuzați"</string>
</resources>
diff --git a/slices/core/src/main/res/values-ru/strings.xml b/slices/core/src/main/res/values-ru/strings.xml
index 89dafbc..5a933ca 100644
--- a/slices/core/src/main/res/values-ru/strings.xml
+++ b/slices/core/src/main/res/values-ru/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Разрешить приложению \"<xliff:g id="APP_0">%1$s</xliff:g>\" показывать фрагменты приложения \"<xliff:g id="APP_2">%2$s</xliff:g>\"?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"– Ему станут доступны данные из приложения \"<xliff:g id="APP">%1$s</xliff:g>\"."</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"– Оно сможет совершать действия в приложении \"<xliff:g id="APP">%1$s</xliff:g>\"."</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Да"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Нет"</string>
</resources>
diff --git a/slices/core/src/main/res/values-si/strings.xml b/slices/core/src/main/res/values-si/strings.xml
index e5e8478..74ff957 100644
--- a/slices/core/src/main/res/values-si/strings.xml
+++ b/slices/core/src/main/res/values-si/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"<xliff:g id="APP_0">%1$s</xliff:g> හට කොටස් <xliff:g id="APP_2">%2$s</xliff:g>ක් පෙන්වීමට ඉඩ දෙන්නද?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- එයට <xliff:g id="APP">%1$s</xliff:g> වෙතින් තොරතුරු කියවිය හැකිය"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- එයට <xliff:g id="APP">%1$s</xliff:g> ඇතුළත ක්රියාමාර්ග ගත හැකිය"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"ඉඩ දෙන්න"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"ප්රතික්ෂේප කර."</string>
</resources>
diff --git a/slices/core/src/main/res/values-sk/strings.xml b/slices/core/src/main/res/values-sk/strings.xml
index 8070718..04053ff 100644
--- a/slices/core/src/main/res/values-sk/strings.xml
+++ b/slices/core/src/main/res/values-sk/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Povoliť aplikácii <xliff:g id="APP_0">%1$s</xliff:g> zobrazovať rezy z aplikácie <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"– Môže čítať informácie z aplikácie <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"– Môže vykonávať akcie v aplikácii <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Povoliť"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Zamietnuť"</string>
</resources>
diff --git a/slices/core/src/main/res/values-sl/strings.xml b/slices/core/src/main/res/values-sl/strings.xml
index b3ab6f4..88df9ec 100644
--- a/slices/core/src/main/res/values-sl/strings.xml
+++ b/slices/core/src/main/res/values-sl/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Ali aplikaciji <xliff:g id="APP_0">%1$s</xliff:g> dovolite prikazovanje izrezov iz aplikacije <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"– lahko bere podatke v aplikaciji <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"– lahko izvaja dejanja v aplikaciji <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Dovoli"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Zavrni"</string>
</resources>
diff --git a/slices/core/src/main/res/values-sq/strings.xml b/slices/core/src/main/res/values-sq/strings.xml
index 9ae77dd..5e56aea 100644
--- a/slices/core/src/main/res/values-sq/strings.xml
+++ b/slices/core/src/main/res/values-sq/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Të lejohet <xliff:g id="APP_0">%1$s</xliff:g> që të shfaqë pjesë të <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- Mund të lexojë informacion nga <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- Mund të ndërmarrë veprime brenda <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Lejo"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Refuzo"</string>
</resources>
diff --git a/slices/core/src/main/res/values-sr/strings.xml b/slices/core/src/main/res/values-sr/strings.xml
index b7c29c5..1064600 100644
--- a/slices/core/src/main/res/values-sr/strings.xml
+++ b/slices/core/src/main/res/values-sr/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Желите ли да дозволите апликацији <xliff:g id="APP_0">%1$s</xliff:g> да приказује исечке из апликације <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"– Може да чита податке из апликације <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"– Може да обавља радње у апликацији <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Дозволи"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Одбиј"</string>
</resources>
diff --git a/slices/core/src/main/res/values-sv/strings.xml b/slices/core/src/main/res/values-sv/strings.xml
index 9be28b3..3a78a19 100644
--- a/slices/core/src/main/res/values-sv/strings.xml
+++ b/slices/core/src/main/res/values-sv/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Tillåter du att bitar av <xliff:g id="APP_2">%2$s</xliff:g> visas i <xliff:g id="APP_0">%1$s</xliff:g>?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"– Kan läsa information från <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"– Kan vidta åtgärder i <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Tillåt"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Neka"</string>
</resources>
diff --git a/slices/core/src/main/res/values-sw/strings.xml b/slices/core/src/main/res/values-sw/strings.xml
index 28459cd..e6a72a0 100644
--- a/slices/core/src/main/res/values-sw/strings.xml
+++ b/slices/core/src/main/res/values-sw/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Ungependa kuruhusu <xliff:g id="APP_0">%1$s</xliff:g> ionyeshe vipengee <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- Inaweza kusoma maelezo kutoka <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- Inaweza kuchukua hatua ndani ya <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Ruhusu"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Kataa"</string>
</resources>
diff --git a/slices/core/src/main/res/values-ta/strings.xml b/slices/core/src/main/res/values-ta/strings.xml
index 36be75a..7f13957 100644
--- a/slices/core/src/main/res/values-ta/strings.xml
+++ b/slices/core/src/main/res/values-ta/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"<xliff:g id="APP_2">%2$s</xliff:g> ஆப்ஸின் விழிப்பூட்டல்களைக் காண்பிக்க, <xliff:g id="APP_0">%1$s</xliff:g> ஆப்ஸை அனுமதிக்கவா?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- இது, <xliff:g id="APP">%1$s</xliff:g> பயன்பாட்டிலிருக்கும் தகவலைப் படிக்கும்"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- இது, <xliff:g id="APP">%1$s</xliff:g> பயன்பாட்டிற்குள் செயல்பாடுகளில் ஈடுபடும்"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"அனுமதி"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"நிராகரி"</string>
</resources>
diff --git a/slices/core/src/main/res/values-te/strings.xml b/slices/core/src/main/res/values-te/strings.xml
index ce51a47..acbfe1f 100644
--- a/slices/core/src/main/res/values-te/strings.xml
+++ b/slices/core/src/main/res/values-te/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"<xliff:g id="APP_2">%2$s</xliff:g> స్లైస్లను చూపించడానికి <xliff:g id="APP_0">%1$s</xliff:g>ని అనుమతించాలా?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- ఇది <xliff:g id="APP">%1$s</xliff:g> నుండి సమాచారాన్ని చదవగలుగుతుంది"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- ఇది <xliff:g id="APP">%1$s</xliff:g> లోపల చర్యలు తీసుకోగలుగుతుంది"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"అనుమతించు"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"తిరస్కరించు"</string>
</resources>
diff --git a/slices/core/src/main/res/values-th/strings.xml b/slices/core/src/main/res/values-th/strings.xml
index 07371a0..b561742 100644
--- a/slices/core/src/main/res/values-th/strings.xml
+++ b/slices/core/src/main/res/values-th/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"อนุญาตให้ <xliff:g id="APP_0">%1$s</xliff:g> แสดงส่วนต่างๆ ของ <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- อ่านข้อมูลจาก <xliff:g id="APP">%1$s</xliff:g> ได้"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- ดำเนินการใน <xliff:g id="APP">%1$s</xliff:g> ได้"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"อนุญาต"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"ปฏิเสธ"</string>
</resources>
diff --git a/slices/core/src/main/res/values-tl/strings.xml b/slices/core/src/main/res/values-tl/strings.xml
index e4bf3e0..3e0daf7 100644
--- a/slices/core/src/main/res/values-tl/strings.xml
+++ b/slices/core/src/main/res/values-tl/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Payagan ang <xliff:g id="APP_0">%1$s</xliff:g> na ipakita ang mga slice ng <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- Nakakabasa ito ng impormasyon mula sa <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- Nakakagawa ito ng mga pagkilos sa loob ng <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Payagan"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Tanggihan"</string>
</resources>
diff --git a/slices/core/src/main/res/values-tr/strings.xml b/slices/core/src/main/res/values-tr/strings.xml
index 2cbcfda..d8d3c87 100644
--- a/slices/core/src/main/res/values-tr/strings.xml
+++ b/slices/core/src/main/res/values-tr/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"<xliff:g id="APP_0">%1$s</xliff:g> uygulamasının, <xliff:g id="APP_2">%2$s</xliff:g> dilimlerini göstermesine izin verilsin mi?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- <xliff:g id="APP">%1$s</xliff:g> uygulamasından bilgileri okuyabilir"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- <xliff:g id="APP">%1$s</xliff:g> uygulamasında işlem yapabilir"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"İzin ver"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Reddet"</string>
</resources>
diff --git a/slices/core/src/main/res/values-uk/strings.xml b/slices/core/src/main/res/values-uk/strings.xml
index b842f5c..86dd1bd 100644
--- a/slices/core/src/main/res/values-uk/strings.xml
+++ b/slices/core/src/main/res/values-uk/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Дозволити додатку <xliff:g id="APP_0">%1$s</xliff:g> показувати фрагменти додатка <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- Може переглядати інформацію з додатка <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- Може виконувати дії в додатку <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Дозволити"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Заборонити"</string>
</resources>
diff --git a/slices/core/src/main/res/values-ur/strings.xml b/slices/core/src/main/res/values-ur/strings.xml
index 8999e25..39a9820 100644
--- a/slices/core/src/main/res/values-ur/strings.xml
+++ b/slices/core/src/main/res/values-ur/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"<xliff:g id="APP_0">%1$s</xliff:g> کو <xliff:g id="APP_2">%2$s</xliff:g> کے سلائسز دکھانے کی اجازت دیں؟"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- یہ <xliff:g id="APP">%1$s</xliff:g> کی معلومات پڑھ سکتا ہے"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- یہ <xliff:g id="APP">%1$s</xliff:g> کے اندر کارروائیاں کر سکتا ہے"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"اجازت دیں"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"مسترد کریں"</string>
</resources>
diff --git a/slices/core/src/main/res/values-uz/strings.xml b/slices/core/src/main/res/values-uz/strings.xml
index 9fcb4ce..1d6febd 100644
--- a/slices/core/src/main/res/values-uz/strings.xml
+++ b/slices/core/src/main/res/values-uz/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"<xliff:g id="APP_0">%1$s</xliff:g> ilovasiga <xliff:g id="APP_2">%2$s</xliff:g> ilovasidan fragmentlar ko‘rsatishga ruxsat berilsinmi?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"– <xliff:g id="APP">%1$s</xliff:g> ma’lumotlarini o‘qiy oladi"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"– <xliff:g id="APP">%1$s</xliff:g> ichida amallar bajara oladi"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Ruxsat"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Rad etish"</string>
</resources>
diff --git a/slices/core/src/main/res/values-vi/strings.xml b/slices/core/src/main/res/values-vi/strings.xml
index c067ac8..d8e71a5 100644
--- a/slices/core/src/main/res/values-vi/strings.xml
+++ b/slices/core/src/main/res/values-vi/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Cho phép <xliff:g id="APP_0">%1$s</xliff:g> hiển thị các lát của <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- Có thể đọc thông tin từ <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- Có thể thực hiện hành động bên trong <xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Cho phép"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Từ chối"</string>
</resources>
diff --git a/slices/core/src/main/res/values-zh-rCN/strings.xml b/slices/core/src/main/res/values-zh-rCN/strings.xml
index 6ab2d3e..2ec34bc 100644
--- a/slices/core/src/main/res/values-zh-rCN/strings.xml
+++ b/slices/core/src/main/res/values-zh-rCN/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"要允许“<xliff:g id="APP_0">%1$s</xliff:g>”显示“<xliff:g id="APP_2">%2$s</xliff:g>”图块吗?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- 可以读取“<xliff:g id="APP">%1$s</xliff:g>”中的信息"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- 可以在“<xliff:g id="APP">%1$s</xliff:g>”内执行操作"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"允许"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"拒绝"</string>
</resources>
diff --git a/slices/core/src/main/res/values-zh-rHK/strings.xml b/slices/core/src/main/res/values-zh-rHK/strings.xml
index 5cf6943..118c2ac 100644
--- a/slices/core/src/main/res/values-zh-rHK/strings.xml
+++ b/slices/core/src/main/res/values-zh-rHK/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"要允許「<xliff:g id="APP_0">%1$s</xliff:g>」顯示「<xliff:g id="APP_2">%2$s</xliff:g>」的快訊嗎?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- 可以讀取「<xliff:g id="APP">%1$s</xliff:g>」中的資料"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- 可以在「<xliff:g id="APP">%1$s</xliff:g>」內執行操作"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"允許"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"拒絕"</string>
</resources>
diff --git a/slices/core/src/main/res/values-zh-rTW/strings.xml b/slices/core/src/main/res/values-zh-rTW/strings.xml
index df1ab8b..53c802d 100644
--- a/slices/core/src/main/res/values-zh-rTW/strings.xml
+++ b/slices/core/src/main/res/values-zh-rTW/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"要允許「<xliff:g id="APP_0">%1$s</xliff:g>」顯示「<xliff:g id="APP_2">%2$s</xliff:g>」的區塊嗎?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- 它可以讀取「<xliff:g id="APP">%1$s</xliff:g>」的資訊"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- 它可以在「<xliff:g id="APP">%1$s</xliff:g>」內執行操作"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"允許"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"拒絕"</string>
</resources>
diff --git a/slices/core/src/main/res/values-zu/strings.xml b/slices/core/src/main/res/values-zu/strings.xml
index d9ada48..b1fac39 100644
--- a/slices/core/src/main/res/values-zu/strings.xml
+++ b/slices/core/src/main/res/values-zu/strings.xml
@@ -21,7 +21,6 @@
<string name="abc_slice_permission_title" msgid="4175332421259324948">"Vumela i-<xliff:g id="APP_0">%1$s</xliff:g> ukuthi ibonise izingcezu ze-<xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="abc_slice_permission_text_1" msgid="4525743640399572811">"- Ingafunda ulwazi kusukela ku-<xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="abc_slice_permission_text_2" msgid="7323565634860251794">"- Ingenza izenzo ngaphakathi kwe-<xliff:g id="APP">%1$s</xliff:g>"</string>
-
<string name="abc_slice_permission_allow" msgid="5024599872061409708">"Vumela"</string>
<string name="abc_slice_permission_deny" msgid="3819478292430407705">"Phika"</string>
</resources>
diff --git a/slices/view/src/main/res/values-my/strings.xml b/slices/view/src/main/res/values-my/strings.xml
index 4a3e3d4..5b9d57c 100644
--- a/slices/view/src/main/res/values-my/strings.xml
+++ b/slices/view/src/main/res/values-my/strings.xml
@@ -19,7 +19,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
<string name="abc_slice_more" msgid="1983560225998630901">"နောက်ထပ်"</string>
- <string name="abc_slice_show_more" msgid="1567717014004692768">"နောက်ထပ် ပြပါ"</string>
+ <string name="abc_slice_show_more" msgid="1567717014004692768">"ပိုပြပါ"</string>
<string name="abc_slice_updated" msgid="8155085405396453848">"<xliff:g id="TIME">%1$s</xliff:g> က အပ်ဒိတ်လုပ်ထားသည်"</string>
<plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
<item quantity="other">ပြီးခဲ့သော<xliff:g id="ID_2">%d</xliff:g>မိနစ်</item>
diff --git a/startup/startup-runtime-lint/build.gradle b/startup/startup-runtime-lint/build.gradle
index 5423d95..22a35542 100644
--- a/startup/startup-runtime-lint/build.gradle
+++ b/startup/startup-runtime-lint/build.gradle
@@ -25,13 +25,7 @@
}
dependencies {
- // compileOnly because we use lintChecks and it doesn't allow other types of deps
- // this ugly hack exists because of b/63873667
- if (rootProject.hasProperty("android.injected.invoked.from.ide")) {
- compileOnly(LINT_API_LATEST)
- } else {
- compileOnly(LINT_API_MIN)
- }
+ compileOnly(LINT_API_MIN)
compileOnly(KOTLIN_STDLIB)
testImplementation(KOTLIN_STDLIB)
diff --git a/tracing/tracing/build.gradle b/tracing/tracing/build.gradle
index fbd0e02..7582a61 100644
--- a/tracing/tracing/build.gradle
+++ b/tracing/tracing/build.gradle
@@ -29,7 +29,7 @@
}
dependencies {
- implementation("androidx.annotation:annotation:1.1.0")
+ implementation("androidx.annotation:annotation:1.2.0")
androidTestImplementation(KOTLIN_STDLIB)
androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
androidTestImplementation(ANDROIDX_TEST_CORE)
diff --git a/tracing/tracing/lint-baseline.xml b/tracing/tracing/lint-baseline.xml
index 2c1ddc6..e17f06f 100644
--- a/tracing/tracing/lint-baseline.xml
+++ b/tracing/tracing/lint-baseline.xml
@@ -1,15 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<issues format="5" by="lint 4.2.0-beta06" client="gradle" variant="debug" version="4.2.0-beta06">
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 29; however, the containing class androidx.tracing.Trace is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" return android.os.Trace.isEnabled();"
- errorLine2=" ~~~~~~~~~">
- <location
- file="src/main/java/androidx/tracing/Trace.java"
- line="62"
- column="41"/>
- </issue>
-
</issues>
diff --git a/tracing/tracing/src/main/java/androidx/tracing/Trace.java b/tracing/tracing/src/main/java/androidx/tracing/Trace.java
index 9e63bc9..0520f2e 100644
--- a/tracing/tracing/src/main/java/androidx/tracing/Trace.java
+++ b/tracing/tracing/src/main/java/androidx/tracing/Trace.java
@@ -57,12 +57,10 @@
*/
@SuppressLint("NewApi")
public static boolean isEnabled() {
- try {
- if (sIsTagEnabledMethod == null) {
- return android.os.Trace.isEnabled();
- }
- } catch (NoSuchMethodError | NoClassDefFoundError ignore) {
+ if (Build.VERSION.SDK_INT >= 29) {
+ return TraceApi29Impl.isEnabled();
}
+
return isEnabledFallback();
}
diff --git a/tracing/tracing/src/main/java/androidx/tracing/TraceApi29Impl.java b/tracing/tracing/src/main/java/androidx/tracing/TraceApi29Impl.java
index 0665212..14ba157 100644
--- a/tracing/tracing/src/main/java/androidx/tracing/TraceApi29Impl.java
+++ b/tracing/tracing/src/main/java/androidx/tracing/TraceApi29Impl.java
@@ -16,6 +16,7 @@
package androidx.tracing;
+import androidx.annotation.DoNotInline;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
@@ -35,6 +36,14 @@
}
/**
+ * Checks whether or not tracing is currently enabled.
+ */
+ @DoNotInline
+ public static boolean isEnabled() {
+ return android.os.Trace.isEnabled();
+ }
+
+ /**
* Writes a trace message to indicate that a given section of code has
* begun. Must be followed by a call to {@link #endAsyncSection(String, int)} with the same
* methodName and cookie.
diff --git a/versionedparcelable/versionedparcelable/src/androidTest/java/androidx/versionedparcelable/ParcelUtilsTest.java b/versionedparcelable/versionedparcelable/src/androidTest/java/androidx/versionedparcelable/ParcelUtilsTest.java
index 8ca1a22..cd2540b 100644
--- a/versionedparcelable/versionedparcelable/src/androidTest/java/androidx/versionedparcelable/ParcelUtilsTest.java
+++ b/versionedparcelable/versionedparcelable/src/androidTest/java/androidx/versionedparcelable/ParcelUtilsTest.java
@@ -79,6 +79,17 @@
assertThat(result).isNull();
}
+ @Test
+ public void putVersionedParcelableNullClearsKey() {
+ Bundle bundle = new Bundle();
+
+ ParcelUtils.putVersionedParcelable(bundle, "key", new ParcelUtilsParcelable());
+ assertThat(bundle.getBundle("key")).isNotNull();
+
+ ParcelUtils.putVersionedParcelable(bundle, "key", null);
+ assertThat(bundle.getBundle("key")).isNull();
+ }
+
@VersionedParcelize
public static class ParcelUtilsParcelable implements VersionedParcelable {
@ParcelField(1)
diff --git a/versionedparcelable/versionedparcelable/src/main/java/androidx/versionedparcelable/ParcelUtils.java b/versionedparcelable/versionedparcelable/src/main/java/androidx/versionedparcelable/ParcelUtils.java
index 7f01a16a..15b615e 100644
--- a/versionedparcelable/versionedparcelable/src/main/java/androidx/versionedparcelable/ParcelUtils.java
+++ b/versionedparcelable/versionedparcelable/src/main/java/androidx/versionedparcelable/ParcelUtils.java
@@ -98,11 +98,12 @@
public static void putVersionedParcelable(@NonNull Bundle b, @NonNull String key,
@Nullable VersionedParcelable obj) {
if (obj == null) {
- return;
+ b.putParcelable(key, null);
+ } else {
+ Bundle innerBundle = new Bundle();
+ innerBundle.putParcelable(INNER_BUNDLE_KEY, toParcelable(obj));
+ b.putParcelable(key, innerBundle);
}
- Bundle innerBundle = new Bundle();
- innerBundle.putParcelable(INNER_BUNDLE_KEY, toParcelable(obj));
- b.putParcelable(key, innerBundle);
}
/**
diff --git a/wear/compose/material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/ChipTest.kt b/wear/compose/material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/ChipTest.kt
index fc4ae01..fe6d6f3 100644
--- a/wear/compose/material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/ChipTest.kt
+++ b/wear/compose/material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/ChipTest.kt
@@ -21,6 +21,7 @@
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.testutils.assertShape
@@ -36,16 +37,21 @@
import androidx.compose.ui.test.assertHeightIsEqualTo
import androidx.compose.ui.test.assertIsEnabled
import androidx.compose.ui.test.assertIsNotEnabled
+import androidx.compose.ui.test.assertTopPositionInRootIsEqualTo
import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.getUnclippedBoundsInRoot
import androidx.compose.ui.test.junit4.ComposeContentTestRule
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onChild
import androidx.compose.ui.test.onChildAt
+import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performClick
+import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.height
import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.Test
@@ -228,6 +234,27 @@
fun gives_base_chip_correct_height() =
verifyHeight(ChipDefaults.Height)
+ @Test
+ fun has_icon_in_correct_location_when_only_single_line_of_text() {
+ val iconTag = "TestIcon"
+ val chipTag = "chip"
+ rule
+ .setContentWithThemeForSizeAssertions(useUnmergedTree = true) {
+ Chip(
+ onClick = {},
+ label = { Text("Blue green orange") },
+ icon = { CreateImage(iconTag) },
+ modifier = Modifier.testTag(chipTag)
+ )
+ }
+ val itemBounds = rule.onNodeWithTag(chipTag).getUnclippedBoundsInRoot()
+ val iconBounds = rule.onNodeWithTag(iconTag, useUnmergedTree = true)
+ .getUnclippedBoundsInRoot()
+
+ rule.onNodeWithContentDescription(iconTag, useUnmergedTree = true)
+ .assertTopPositionInRootIsEqualTo((itemBounds.height - iconBounds.height) / 2)
+ }
+
private fun verifyHeight(expectedHeight: Dp) {
rule.verifyHeight(expectedHeight) {
Chip(
@@ -254,6 +281,17 @@
)
@Test
+ fun three_slot_layout_gives_primary_enabled_colors() =
+ verifyThreeSlotColors(
+ TestChipColors.Primary,
+ ChipStatus.Enabled,
+ { MaterialTheme.colors.primary },
+ { MaterialTheme.colors.onPrimary },
+ { MaterialTheme.colors.onPrimary },
+ { MaterialTheme.colors.onPrimary }
+ )
+
+ @Test
fun gives_primary_disabled_colors() =
verifyColors(
TestChipColors.Primary,
@@ -263,6 +301,17 @@
)
@Test
+ fun three_slot_layout_gives_primary_disabled_colors() =
+ verifyThreeSlotColors(
+ TestChipColors.Primary,
+ ChipStatus.Disabled,
+ { MaterialTheme.colors.primary },
+ { MaterialTheme.colors.onPrimary },
+ { MaterialTheme.colors.onPrimary },
+ { MaterialTheme.colors.onPrimary }
+ )
+
+ @Test
fun gives_secondary_enabled_colors() =
verifyColors(
TestChipColors.Secondary,
@@ -272,6 +321,17 @@
)
@Test
+ fun three_slot_layout_gives_secondary_enabled_colors() =
+ verifyThreeSlotColors(
+ TestChipColors.Secondary,
+ ChipStatus.Enabled,
+ { MaterialTheme.colors.surface },
+ { MaterialTheme.colors.onSurface },
+ { MaterialTheme.colors.onSurface },
+ { MaterialTheme.colors.onSurface }
+ )
+
+ @Test
fun gives_secondary_disabled_colors() =
verifyColors(
TestChipColors.Secondary,
@@ -281,6 +341,17 @@
)
@Test
+ fun three_slot_layout_gives_secondary_disabled_colors() =
+ verifyThreeSlotColors(
+ TestChipColors.Secondary,
+ ChipStatus.Enabled,
+ { MaterialTheme.colors.surface },
+ { MaterialTheme.colors.onSurface },
+ { MaterialTheme.colors.onSurface },
+ { MaterialTheme.colors.onSurface }
+ )
+
+ @Test
fun allows_custom_enabled_background_color_override() {
val overrideColor = Color.Yellow
rule.setContentWithTheme {
@@ -344,6 +415,60 @@
}
@Test
+ fun allows_custom_enabled_secondary_label_color_override() {
+ val overrideColor = Color.Red
+ var actualContentColor = Color.Transparent
+ var actualSecondaryContentColor = Color.Transparent
+ var expectedContent = Color.Transparent
+ rule.setContentWithTheme {
+ expectedContent = MaterialTheme.colors.onPrimary
+ Chip(
+ onClick = {},
+ colors = ChipDefaults.chipColors(
+ secondaryContentColor = overrideColor
+ ),
+ label = {
+ actualContentColor = LocalContentColor.current
+ },
+ secondaryLabel = {
+ actualSecondaryContentColor = LocalContentColor.current
+ },
+ enabled = true,
+ modifier = Modifier.testTag("test-item")
+ )
+ }
+ assertEquals(expectedContent, actualContentColor)
+ assertEquals(overrideColor, actualSecondaryContentColor)
+ }
+
+ @Test
+ fun allows_custom_enabled_icon_tint_color_override() {
+ val overrideColor = Color.Red
+ var actualContentColor = Color.Transparent
+ var actualIconTintColor = Color.Transparent
+ var expectedContent = Color.Transparent
+ rule.setContentWithTheme {
+ expectedContent = MaterialTheme.colors.onPrimary
+ Chip(
+ onClick = {},
+ colors = ChipDefaults.chipColors(
+ iconTintColor = overrideColor
+ ),
+ label = {
+ actualContentColor = LocalContentColor.current
+ },
+ icon = {
+ actualIconTintColor = LocalContentColor.current
+ },
+ enabled = true,
+ modifier = Modifier.testTag("test-item")
+ )
+ }
+ assertEquals(expectedContent, actualContentColor)
+ assertEquals(overrideColor, actualIconTintColor)
+ }
+
+ @Test
fun allows_custom_disabled_content_color_override() {
val overrideColor = Color.Yellow
var actualContentColor = Color.Transparent
@@ -406,6 +531,114 @@
.assertContainsColor(expectedBackground, 50.0f)
}
}
+
+ private fun verifyThreeSlotColors(
+ testChipColors: TestChipColors,
+ status: ChipStatus,
+ backgroundColor: @Composable () -> Color,
+ contentColor: @Composable () -> Color,
+ secondaryContentColor: @Composable () -> Color,
+ iconColor: @Composable () -> Color
+ ) {
+ var expectedBackground = Color.Transparent
+ var expectedContent = Color.Transparent
+ var expectedSecondaryContent = Color.Transparent
+ var expectedIcon = Color.Transparent
+ var actualContent = Color.Transparent
+ var actualSecondaryContent = Color.Transparent
+ var actualIcon = Color.Transparent
+ var expectedAlpha = 0.0f
+
+ rule.setContentWithTheme {
+ expectedBackground = backgroundColor()
+ expectedContent = contentColor()
+ expectedSecondaryContent = secondaryContentColor()
+ expectedIcon = iconColor()
+ expectedAlpha = ContentAlpha.disabled
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .background(expectedBackground)
+ ) {
+ Chip(
+ onClick = {},
+ colors = testChipColors.chipColors(),
+ label = { actualContent = LocalContentColor.current },
+ secondaryLabel = { actualSecondaryContent = LocalContentColor.current },
+ icon = { actualIcon = LocalContentColor.current },
+ enabled = status.enabled(),
+ modifier = Modifier.testTag("test-item")
+ )
+ }
+ }
+
+ if (status.enabled()) {
+ assertEquals(expectedContent, actualContent)
+ assertEquals(expectedSecondaryContent, actualSecondaryContent)
+ assertEquals(expectedIcon, actualIcon)
+ } else {
+ assertEquals(expectedContent.copy(alpha = expectedAlpha), actualContent)
+ assertEquals(
+ expectedSecondaryContent.copy(alpha = expectedAlpha),
+ actualSecondaryContent
+ )
+ assertEquals(expectedIcon.copy(alpha = expectedAlpha), actualIcon)
+ }
+
+ if (expectedBackground != Color.Transparent) {
+ rule.onNodeWithTag("test-item").onChildAt(0)
+ .captureToImage()
+ .assertContainsColor(expectedBackground, 50.0f)
+ }
+ }
+}
+
+class ChipFontTest {
+ @get:Rule
+ val rule = createComposeRule()
+
+ @Test
+ fun gives_correct_text_style_base() {
+ var actualTextStyle = TextStyle.Default
+ var expectedTextStyle = TextStyle.Default
+ rule.setContentWithTheme {
+ expectedTextStyle = MaterialTheme.typography.button
+ Chip(
+ onClick = {},
+ colors = ChipDefaults.primaryChipColors(),
+ content = {
+ actualTextStyle = LocalTextStyle.current
+ },
+ enabled = true,
+ modifier = Modifier.testTag("test-item")
+ )
+ }
+ assertEquals(expectedTextStyle, actualTextStyle)
+ }
+
+ @Test
+ fun gives_correct_text_style_three_slot_chip() {
+ var actualLabelTextStyle = TextStyle.Default
+ var actualSecondaryLabelTextStyle = TextStyle.Default
+ var expectedTextStyle = TextStyle.Default
+ rule.setContentWithTheme {
+ expectedTextStyle = MaterialTheme.typography.button
+ Chip(
+ onClick = {},
+ colors = ChipDefaults.primaryChipColors(),
+ label = {
+ actualLabelTextStyle = LocalTextStyle.current
+ },
+ secondaryLabel = {
+ actualSecondaryLabelTextStyle = LocalTextStyle.current
+ },
+ enabled = true,
+ modifier = Modifier.testTag("test-item")
+ )
+ }
+ assertEquals(expectedTextStyle, actualLabelTextStyle)
+ assertEquals(expectedTextStyle, actualSecondaryLabelTextStyle)
+ }
}
private fun ComposeContentTestRule.verifyHeight(expected: Dp, content: @Composable () -> Unit) {
diff --git a/wear/compose/material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/MaterialTest.kt b/wear/compose/material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/MaterialTest.kt
index d79bd27..5f4fb02 100644
--- a/wear/compose/material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/MaterialTest.kt
+++ b/wear/compose/material/src/androidAndroidTest/kotlin/androidx/wear/compose/material/MaterialTest.kt
@@ -19,16 +19,19 @@
import android.util.Log
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.sizeIn
import androidx.compose.material.Surface
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Add
import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asAndroidBitmap
import androidx.compose.ui.graphics.toPixelMap
+import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.semantics.SemanticsActions
import androidx.compose.ui.test.SemanticsNodeInteraction
@@ -110,9 +113,13 @@
@Composable
fun CreateImage(iconLabel: String = "TestIcon") {
val testImage = Icons.Outlined.Add
- Image(testImage, iconLabel)
+ Image(
+ testImage, iconLabel,
+ modifier = Modifier.fillMaxSize().testTag(iconLabel),
+ contentScale = ContentScale.Fit,
+ alignment = Alignment.Center
+ )
}
-
/**
* assertContainsColor - uses a threshold on an ImageBitmap's color distribution
* to check that a UI element is predominantly the expected color.
diff --git a/wear/compose/material/src/androidMain/kotlin/androidx/wear/compose/material/Chip.kt b/wear/compose/material/src/androidMain/kotlin/androidx/wear/compose/material/Chip.kt
index f4f85c0..f2b9c62 100644
--- a/wear/compose/material/src/androidMain/kotlin/androidx/wear/compose/material/Chip.kt
+++ b/wear/compose/material/src/androidMain/kotlin/androidx/wear/compose/material/Chip.kt
@@ -18,11 +18,18 @@
import androidx.compose.foundation.Indication
import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.clickable
+import androidx.compose.foundation.interaction.Interaction
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.material.ContentAlpha
import androidx.compose.material.Surface
import androidx.compose.runtime.Composable
@@ -32,6 +39,7 @@
import androidx.compose.runtime.State
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.paint
import androidx.compose.ui.graphics.Color
@@ -42,8 +50,45 @@
import androidx.compose.ui.unit.dp
/**
- * The most basic chip that provides a single content slot, used as the building block for other
- * chips.
+ * Base level Wear Material [Chip] that offers a single slot to take any content.
+ *
+ * Is used as the container for more opinionated [Chip] components that take specific content such
+ * as icons and labels.
+ *
+ * The [Chip] is Stadium shaped and has a max height designed to take no more than two lines of text
+ * of [Typography.button] style. The [Chip] can have an icon or image horizontally
+ * parallel to the two lines of text.
+ *
+ * The [Chip] can have different styles with configurable content colors, background colors
+ * including gradients, these are provided by [ChipColors] implementations.
+ *
+ * The recommended set of [ChipColors] styles can be obtained from [ChipDefaults], e.g.
+ * [ChipDefaults.primaryChipColors] to get a color scheme for a primary [Chip] which by default
+ * will have a solid background of [Colors.primary] and content color of
+ * [Colors.onPrimary].
+ *
+ * Chips can be enabled or disabled. A disabled chip will not respond to click events.
+ *
+ * @param onClick Will be called when the user clicks the chip
+ * @param colors [ChipColors] that will be used to resolve the background and content color for
+ * this chip in different states. See [ChipDefaults.chipColors].
+ * @param modifier Modifier to be applied to the chip
+ * @param enabled Controls the enabled state of the chip. When `false`, this chip will not
+ * be clickable
+ * @param onClickLabel Semantic / accessibility label for the [onClick] action
+ * @param contentPadding The spacing values to apply internally between the container and the
+ * content
+ * @param shape Defines the chip's shape. It is strongly recommended to use the default as this
+ * shape is a key characteristic of the Wear Material Theme
+ * @param interactionSource The [MutableInteractionSource] representing the stream of
+ * [Interaction]s for this Chip. You can create and pass in your own remembered
+ * [MutableInteractionSource] if you want to observe [Interaction]s and customize the
+ * appearance / behavior of this Chip in different [Interaction]s.
+ * @param indication Indication to be shown when surface is pressed. By default, indication from
+ * [LocalIndication] will be used. Pass `null` to show no indication, or current value from
+ * [LocalIndication] to show theme default
+ * @param role The type of user interface element. Accessibility services might use this
+ * to describe the element or do customizations
*/
@Composable
fun Chip(
@@ -93,6 +138,7 @@
) {
CompositionLocalProvider(
LocalContentColor provides colors.contentColor(enabled = enabled).value,
+ LocalTextStyle provides MaterialTheme.typography.button,
content = content
)
}
@@ -100,6 +146,108 @@
}
/**
+ * Wear Material [Chip] that offers three slots and a specific layout for an icon, label and
+ * secondaryLabel. The icon and secondaryLabel are optional. The items are laid out with the icon,
+ * if provided, a the start of a row, with a column next containing the two label slots.
+ *
+ * The [Chip] is Stadium shaped and has a max height designed to take no more than two lines of text
+ * of [Typography.button] style. If no secondary label is provided then the label
+ * can be two lines of text. The label and secondary label should be consistently aligned.
+ *
+ * If a icon is provided then the labels should be "start" aligned, e.g. left aligned in ltr so that
+ * the text starts next to the icon.
+ *
+ * The [Chip] can have different styles with configurable content colors, background colors
+ * including gradients, these are provided by [ChipColors] implementations.
+ *
+ * The recommended set of [ChipColors] styles can be obtained from [ChipDefaults], e.g.
+ * [ChipDefaults.primaryChipColors] to get a color scheme for a primary [Chip] which by default
+ * will have a solid background of [Colors.primary] and content color of
+ * [Colors.onPrimary].
+ *
+ * Chips can be enabled or disabled. A disabled chip will not respond to click events.
+ *
+ * @param label A slot for providing the chips main label. The contents are expected to be text
+ * which is "start" aligned if there is an icon preset and "start" or "center" aligned if not.
+ * @param onClick Will be called when the user clicks the chip
+ * @param modifier Modifier to be applied to the chip
+ * @param secondaryLabel A slot for providing the chips secondary label. The contents are expected
+ * to be text which is "start" aligned if there is an icon preset and "start" or "center" aligned if
+ * not. label and secondaryLabel contents should be consistently aligned.
+ * @param colors [ChipColors] that will be used to resolve the background and content color for
+ * this chip in different states. See [ChipDefaults.chipColors]. Defaults to
+ * [ChipDefaults.primaryChipColors]
+ * @param enabled Controls the enabled state of the chip. When `false`, this chip will not
+ * be clickable
+ * @param onClickLabel Semantic / accessibility label for the [onClick] action
+ * @param interactionSource The [MutableInteractionSource] representing the stream of
+ * [Interaction]s for this Chip. You can create and pass in your own remembered
+ * [MutableInteractionSource] if you want to observe [Interaction]s and customize the
+ * appearance / behavior of this Chip in different [Interaction]s.
+ * @param indication Indication to be shown when surface is pressed. By default, indication from
+ * [LocalIndication] will be used. Pass `null` to show no indication, or current value from
+ * [LocalIndication] to show theme default
+ * @param contentPadding The spacing values to apply internally between the container and the
+ * content
+ */
+@Composable
+fun Chip(
+ label: @Composable () -> Unit,
+ onClick: () -> Unit,
+ modifier: Modifier = Modifier,
+ secondaryLabel: (@Composable () -> Unit)? = null,
+ icon: (@Composable () -> Unit)? = null,
+ colors: ChipColors = ChipDefaults.primaryChipColors(),
+ enabled: Boolean = true,
+ onClickLabel: String? = null,
+ interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+ indication: Indication? = LocalIndication.current,
+ contentPadding: PaddingValues = ChipDefaults.contentPadding(icon != null),
+) {
+ Chip(
+ onClick = onClick,
+ colors = colors,
+ modifier = modifier,
+ enabled = enabled,
+ onClickLabel = onClickLabel,
+ interactionSource = interactionSource,
+ indication = indication,
+ contentPadding = contentPadding
+ ) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = Modifier.fillMaxSize()
+ ) {
+ if (icon != null) {
+ Box(
+ modifier = Modifier.wrapContentSize(align = Alignment.Center)
+ ) {
+ CompositionLocalProvider(
+ LocalContentColor provides colors.iconTintColor(enabled).value,
+ content = icon
+ )
+ }
+ Spacer(modifier = Modifier.size(ChipDefaults.IconSpacing))
+ }
+ Column {
+ CompositionLocalProvider(
+ LocalContentColor provides colors.contentColor(enabled).value,
+ LocalTextStyle provides MaterialTheme.typography.button,
+ content = label
+ )
+ if (secondaryLabel != null) {
+ CompositionLocalProvider(
+ LocalContentColor provides colors.secondaryContentColor(enabled).value,
+ LocalTextStyle provides MaterialTheme.typography.button,
+ content = secondaryLabel
+ )
+ }
+ }
+ }
+ }
+}
+
+/**
* Represents the background and content colors used in a chip in different states.
*
* See [ChipDefaults.primaryChipColors] for the default colors used in a primary styled [Chip].
@@ -107,13 +255,19 @@
*/
@Stable
interface ChipColors {
+ /**
+ * Represents the background treatment for this chip, depending on [enabled]. Backgrounds can
+ * be solid, transparent or have a gradient applied.
+ *
+ * @param enabled Whether the chip is enabled
+ */
@Composable
fun background(enabled: Boolean): State<Painter>
/**
* Represents the content color for this chip, depending on [enabled].
*
- * @param enabled whether the chip is enabled
+ * @param enabled Whether the chip is enabled
*/
@Composable
fun contentColor(enabled: Boolean): State<Color>
@@ -121,13 +275,15 @@
/**
* Represents the secondary content color for this chip, depending on [enabled].
*
- * @param enabled whether the chip is enabled
+ * @param enabled Whether the chip is enabled
*/
@Composable
fun secondaryContentColor(enabled: Boolean): State<Color>
/**
* Represents the icon tint color for this chip, depending on [enabled].
+ *
+ * @param enabled Whether the chip is enabled
*/
@Composable
fun iconTintColor(enabled: Boolean): State<Color>
@@ -138,6 +294,14 @@
*/
public object ChipDefaults {
+ /**
+ * Creates a [ChipColors] that represents the default background and content colors for a
+ * primary [Chip]. Primary chips have a colored background with a contrasting content color. If
+ * a chip is disabled then the color will have an alpha([ContentAlpha.disabled]) value applied.
+ *
+ * @param backgroundColor The background color of this [Chip] when enabled
+ * @param contentColor The content color of this [Chip] when enabled
+ */
@Composable
public fun primaryChipColors(
backgroundColor: Color = MaterialTheme.colors.primary,
@@ -149,6 +313,15 @@
)
}
+ /**
+ * Creates a [ChipColors] that represents the default background and content colors for a
+ * secondary [Chip]. Secondary chips have a muted background with a contrasting content color.
+ * If a chip is disabled then the color will have an alpha([ContentAlpha.disabled]) value
+ * applied.
+ *
+ * @param backgroundColor The background color of this [Chip] when enabled
+ * @param contentColor The content color of this [Chip] when enabled
+ */
@Composable
public fun secondaryChipColors(
backgroundColor: Color = MaterialTheme.colors.surface,
@@ -161,9 +334,23 @@
}
private val ChipHorizontalPadding = 16.dp
+ private val ChipWithIconHorizontalPadding = 12.dp
private val ChipVerticalPadding = 6.dp
/**
+ * Creates a [PaddingValues] for a [Chip] based on whether the chip has a icon. For chips with
+ * icons a smaller start and end padding are applied to compensate for the space that the icon
+ * consumes.
+ *
+ * @param hasIcon Whether the [Chip] has an icon.
+ */
+ public fun contentPadding(
+ hasIcon: Boolean
+ ): PaddingValues {
+ return if (hasIcon) ContentWithIconPadding else ContentPadding
+ }
+
+ /**
* The default content padding used by [Chip]
*/
public val ContentPadding = PaddingValues(
@@ -174,6 +361,16 @@
)
/**
+ * The content padding used by a [Chip] which includes an icon
+ */
+ public val ContentWithIconPadding = PaddingValues(
+ start = ChipWithIconHorizontalPadding,
+ top = ChipVerticalPadding,
+ end = ChipWithIconHorizontalPadding,
+ bottom = ChipVerticalPadding
+ )
+
+ /**
* The default height applied for the [Chip].
* Note that you can override it by applying Modifier.heightIn directly on [Chip].
*/
@@ -182,10 +379,21 @@
/**
* The default size of the icon when used inside a [Chip].
*/
- internal val IconSize = 24.dp
+ public val IconSize = 24.dp
/**
- * The default size of the spacing between an icon and a text when they used inside a [Chip].
+ * The size of the icon when used inside a Large "Avatar" [Chip].
+ */
+ public val LargeIconSize = 32.dp
+
+ /**
+ * The size of the icon when used inside a "Compact" [Chip].
+ */
+ public val SmallIconSize = 20.dp
+
+ /**
+ * The default size of the spacing between an icon and a text when they are used inside a
+ * [Chip].
*/
internal val IconSpacing = 8.dp
@@ -193,14 +401,14 @@
* Creates a [ChipColors] that represents the default background and content colors used in
* a [Chip].
*
- * @param backgroundColor the background color of this [Chip] when enabled
- * @param contentColor the content color of this [Chip] when enabled
- * @param secondaryContentColor the content color of this [Chip] when enabled
- * @param iconTintColor the content color of this [Chip] when enabled
- * @param disabledBackgroundColor the background color of this [Chip] when not enabled
- * @param disabledContentColor the content color of this [Chip] when not enabled
- * @param disabledSecondaryContentColor the content color of this [Chip] when not enabled
- * @param disabledIconTintColor the content color of this [Chip] when not enabled
+ * @param backgroundColor The background color of this [Chip] when enabled
+ * @param contentColor The content color of this [Chip] when enabled
+ * @param secondaryContentColor The content color of this [Chip] when enabled
+ * @param iconTintColor The content color of this [Chip] when enabled
+ * @param disabledBackgroundColor The background color of this [Chip] when not enabled
+ * @param disabledContentColor The content color of this [Chip] when not enabled
+ * @param disabledSecondaryContentColor The content color of this [Chip] when not enabled
+ * @param disabledIconTintColor The content color of this [Chip] when not enabled
*/
@Composable
fun chipColors(
diff --git a/wear/wear-complications-data/api/restricted_current.txt b/wear/wear-complications-data/api/restricted_current.txt
index 8463171..4b1188f 100644
--- a/wear/wear-complications-data/api/restricted_current.txt
+++ b/wear/wear-complications-data/api/restricted_current.txt
@@ -165,7 +165,7 @@
property public final java.util.Map<androidx.wear.complications.data.ComplicationType,android.graphics.RectF> perComplicationTypeBounds;
}
- @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final class ComplicationHelperActivity extends android.app.Activity implements androidx.core.app.ActivityCompat.OnRequestPermissionsResultCallback {
+ @RequiresApi(android.os.Build.VERSION_CODES.N) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final class ComplicationHelperActivity extends android.app.Activity implements androidx.core.app.ActivityCompat.OnRequestPermissionsResultCallback {
ctor public ComplicationHelperActivity();
method public static android.content.Intent createPermissionRequestHelperIntent(android.content.Context, android.content.ComponentName);
method public static android.content.Intent createProviderChooserHelperIntent(android.content.Context, android.content.ComponentName, int, java.util.Collection<androidx.wear.complications.data.ComplicationType!>, String?);
diff --git a/wear/wear-complications-data/src/main/java/androidx/wear/complications/ComplicationHelperActivity.java b/wear/wear-complications-data/src/main/java/androidx/wear/complications/ComplicationHelperActivity.java
index de5f491..10b577e 100644
--- a/wear/wear-complications-data/src/main/java/androidx/wear/complications/ComplicationHelperActivity.java
+++ b/wear/wear-complications-data/src/main/java/androidx/wear/complications/ComplicationHelperActivity.java
@@ -16,7 +16,6 @@
package androidx.wear.complications;
-import android.annotation.TargetApi;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.ComponentName;
@@ -30,6 +29,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.core.app.ActivityCompat;
import androidx.wear.complications.data.ComplicationType;
@@ -57,7 +57,7 @@
*
* @hide
*/
-@TargetApi(Build.VERSION_CODES.N)
+@RequiresApi(Build.VERSION_CODES.N)
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@SuppressWarnings("ForbiddenSuperClass")
public final class ComplicationHelperActivity extends Activity
diff --git a/wear/wear-watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceControlClient.kt b/wear/wear-watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceControlClient.kt
index 0d88448..7c8eb54 100644
--- a/wear/wear-watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceControlClient.kt
+++ b/wear/wear-watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceControlClient.kt
@@ -92,7 +92,12 @@
deferredService.completeExceptionally(ServiceStartFailureException())
}
}
- if (!context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)) {
+ if (!context.bindService(
+ intent,
+ serviceConnection,
+ Context.BIND_AUTO_CREATE or Context.BIND_IMPORTANT
+ )
+ ) {
throw ServiceNotBoundException()
}
return WatchFaceControlClientImpl(
diff --git a/wear/wear-watchface-complications-rendering/api/current.txt b/wear/wear-watchface-complications-rendering/api/current.txt
index dad6070..cea9495 100644
--- a/wear/wear-watchface-complications-rendering/api/current.txt
+++ b/wear/wear-watchface-complications-rendering/api/current.txt
@@ -13,8 +13,6 @@
method public void setHighlighted(boolean value);
property public final androidx.wear.watchface.complications.rendering.ComplicationDrawable drawable;
property public boolean isHighlighted;
- field public static final float EXPANSION_DP = 6.0f;
- field public static final float STROKE_WIDTH_DP = 3.0f;
}
public final class ComplicationDrawable extends android.graphics.drawable.Drawable {
diff --git a/wear/wear-watchface-complications-rendering/api/public_plus_experimental_current.txt b/wear/wear-watchface-complications-rendering/api/public_plus_experimental_current.txt
index dad6070..cea9495 100644
--- a/wear/wear-watchface-complications-rendering/api/public_plus_experimental_current.txt
+++ b/wear/wear-watchface-complications-rendering/api/public_plus_experimental_current.txt
@@ -13,8 +13,6 @@
method public void setHighlighted(boolean value);
property public final androidx.wear.watchface.complications.rendering.ComplicationDrawable drawable;
property public boolean isHighlighted;
- field public static final float EXPANSION_DP = 6.0f;
- field public static final float STROKE_WIDTH_DP = 3.0f;
}
public final class ComplicationDrawable extends android.graphics.drawable.Drawable {
diff --git a/wear/wear-watchface-complications-rendering/api/restricted_current.txt b/wear/wear-watchface-complications-rendering/api/restricted_current.txt
index 3710976..e15553d 100644
--- a/wear/wear-watchface-complications-rendering/api/restricted_current.txt
+++ b/wear/wear-watchface-complications-rendering/api/restricted_current.txt
@@ -13,8 +13,6 @@
method public void setHighlighted(boolean value);
property public final androidx.wear.watchface.complications.rendering.ComplicationDrawable drawable;
property public boolean isHighlighted;
- field public static final float EXPANSION_DP = 6.0f;
- field public static final float STROKE_WIDTH_DP = 3.0f;
}
public final class ComplicationDrawable extends android.graphics.drawable.Drawable {
diff --git a/wear/wear-watchface-complications-rendering/src/main/java/androidx/wear/watchface/complications/rendering/CanvasComplicationDrawable.kt b/wear/wear-watchface-complications-rendering/src/main/java/androidx/wear/watchface/complications/rendering/CanvasComplicationDrawable.kt
index c59f06e..3dde0c8 100644
--- a/wear/wear-watchface-complications-rendering/src/main/java/androidx/wear/watchface/complications/rendering/CanvasComplicationDrawable.kt
+++ b/wear/wear-watchface-complications-rendering/src/main/java/androidx/wear/watchface/complications/rendering/CanvasComplicationDrawable.kt
@@ -53,8 +53,8 @@
) : CanvasComplication {
private companion object {
- const val EXPANSION_DP = 6.0f
- const val STROKE_WIDTH_DP = 3.0f
+ internal const val EXPANSION_DP = 6.0f
+ internal const val STROKE_WIDTH_DP = 3.0f
}
private val complicationHighlightRenderer by lazy {
diff --git a/wear/wear-watchface-data/api/restricted_current.txt b/wear/wear-watchface-data/api/restricted_current.txt
index 9090fcf..dc55cb7 100644
--- a/wear/wear-watchface-data/api/restricted_current.txt
+++ b/wear/wear-watchface-data/api/restricted_current.txt
@@ -106,7 +106,7 @@
method public static android.support.wearable.complications.ComplicationText makeTimeAsComplicationText(android.content.Context);
}
- @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final class ContentDescriptionLabel implements android.os.Parcelable {
+ @RequiresApi(android.os.Build.VERSION_CODES.KITKAT) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public final class ContentDescriptionLabel implements android.os.Parcelable {
ctor public ContentDescriptionLabel(android.graphics.Rect, android.support.wearable.complications.ComplicationTextTemplate);
ctor public ContentDescriptionLabel(android.graphics.Rect, android.support.wearable.complications.ComplicationText);
ctor public ContentDescriptionLabel(android.content.Context, android.graphics.Rect, android.support.wearable.complications.ComplicationData);
diff --git a/wear/wear-watchface-data/src/main/java/android/support/wearable/watchface/accessibility/ContentDescriptionLabel.java b/wear/wear-watchface-data/src/main/java/android/support/wearable/watchface/accessibility/ContentDescriptionLabel.java
index fbc0738..ddb401f 100644
--- a/wear/wear-watchface-data/src/main/java/android/support/wearable/watchface/accessibility/ContentDescriptionLabel.java
+++ b/wear/wear-watchface-data/src/main/java/android/support/wearable/watchface/accessibility/ContentDescriptionLabel.java
@@ -16,7 +16,6 @@
package android.support.wearable.watchface.accessibility;
-import android.annotation.TargetApi;
import android.app.PendingIntent;
import android.content.Context;
import android.graphics.Rect;
@@ -32,6 +31,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import org.jetbrains.annotations.NotNull;
@@ -43,7 +43,7 @@
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-@TargetApi(VERSION_CODES.KITKAT)
+@RequiresApi(VERSION_CODES.KITKAT)
@SuppressWarnings("BanParcelableUsage")
public final class ContentDescriptionLabel implements Parcelable {
diff --git a/window/window/src/main/java/androidx/window/DisplayFeature.kt b/window/window/src/main/java/androidx/window/DisplayFeature.kt
index 5d3207c..72ed2c9 100644
--- a/window/window/src/main/java/androidx/window/DisplayFeature.kt
+++ b/window/window/src/main/java/androidx/window/DisplayFeature.kt
@@ -26,7 +26,7 @@
* visual or touch discontinuity, make some area invisible or create a logical divider or
* separation in the screen space.
*
- * @see FoldingFeature that represents a screen fold that intersects the application window.
+ * @see FoldingFeature Represents a screen fold that intersects the application window.
*/
public interface DisplayFeature {
/**
diff --git a/window/window/src/main/java/androidx/window/WindowBackend.kt b/window/window/src/main/java/androidx/window/WindowBackend.kt
index 29bc71f..6f6e351 100644
--- a/window/window/src/main/java/androidx/window/WindowBackend.kt
+++ b/window/window/src/main/java/androidx/window/WindowBackend.kt
@@ -20,9 +20,8 @@
import java.util.concurrent.Executor
/**
- * Backing interface for {@link androidx.window.WindowManager} instances that server as the default
- * information
- * supplier.
+ * Backing interface for [WindowManager] instances that serve as the default
+ * information supplier.
*/
public interface WindowBackend {
/**
diff --git a/window/window/src/main/java/androidx/window/WindowInfoRepo.kt b/window/window/src/main/java/androidx/window/WindowInfoRepo.kt
index 32ea8e3..053bdb3 100644
--- a/window/window/src/main/java/androidx/window/WindowInfoRepo.kt
+++ b/window/window/src/main/java/androidx/window/WindowInfoRepo.kt
@@ -61,8 +61,8 @@
* areas of the display are not available to windows created for the associated [Context].
* For example, devices with foldable displays that wrap around the enclosure may split the
* physical display into different regions, one for the front and one for the back, each acting
- * as different logical displays. In this case [.getMaximumWindowMetrics] would return
- * the region describing the side of the device the associated [context's][Context]
+ * as different logical displays. In this case [getMaximumWindowMetrics] would return
+ * the region describing the side of the device the associated [context's][Context]
* window is placed.
*
* @see currentWindowMetrics
diff --git a/work/workmanager-gcm/api/2.6.0-beta01.txt b/work/workmanager-gcm/api/2.6.0-beta01.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/work/workmanager-gcm/api/2.6.0-beta01.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/work/workmanager-gcm/api/public_plus_experimental_2.6.0-beta01.txt b/work/workmanager-gcm/api/public_plus_experimental_2.6.0-beta01.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/work/workmanager-gcm/api/public_plus_experimental_2.6.0-beta01.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/work/workmanager-gcm/api/res-2.6.0-beta01.txt b/work/workmanager-gcm/api/res-2.6.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/work/workmanager-gcm/api/res-2.6.0-beta01.txt
diff --git a/work/workmanager-gcm/api/restricted_2.6.0-beta01.txt b/work/workmanager-gcm/api/restricted_2.6.0-beta01.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/work/workmanager-gcm/api/restricted_2.6.0-beta01.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/work/workmanager-ktx/api/2.6.0-beta01.txt b/work/workmanager-ktx/api/2.6.0-beta01.txt
new file mode 100644
index 0000000..2c5f419
--- /dev/null
+++ b/work/workmanager-ktx/api/2.6.0-beta01.txt
@@ -0,0 +1,40 @@
+// Signature format: 4.0
+package androidx.work {
+
+ public abstract class CoroutineWorker extends androidx.work.ListenableWorker {
+ ctor public CoroutineWorker(android.content.Context appContext, androidx.work.WorkerParameters params);
+ method public abstract suspend Object? doWork(kotlin.coroutines.Continuation<? super androidx.work.ListenableWorker.Result> p);
+ method @Deprecated public kotlinx.coroutines.CoroutineDispatcher getCoroutineContext();
+ method public final void onStopped();
+ method public final suspend Object? setForeground(androidx.work.ForegroundInfo foregroundInfo, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+ method public final suspend Object? setProgress(androidx.work.Data data, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+ method public final com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result> startWork();
+ property @Deprecated public kotlinx.coroutines.CoroutineDispatcher coroutineContext;
+ }
+
+ public final class DataKt {
+ method public static inline <reified T> boolean hasKeyWithValueOfType(androidx.work.Data, String key);
+ method public static inline androidx.work.Data workDataOf(kotlin.Pair<java.lang.String,?>... pairs);
+ }
+
+ public final class ListenableFutureKt {
+ }
+
+ public final class OneTimeWorkRequestKt {
+ method public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.OneTimeWorkRequest.Builder! OneTimeWorkRequestBuilder();
+ method public static inline androidx.work.OneTimeWorkRequest.Builder setInputMerger(androidx.work.OneTimeWorkRequest.Builder, kotlin.reflect.KClass<? extends androidx.work.InputMerger> inputMerger);
+ }
+
+ public final class OperationKt {
+ method public static suspend inline Object? await(androidx.work.Operation, kotlin.coroutines.Continuation<? super androidx.work.Operation.State.SUCCESS> p);
+ }
+
+ public final class PeriodicWorkRequestKt {
+ method public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.PeriodicWorkRequest.Builder! PeriodicWorkRequestBuilder(long repeatInterval, java.util.concurrent.TimeUnit repeatIntervalTimeUnit);
+ method @RequiresApi(26) public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.PeriodicWorkRequest.Builder! PeriodicWorkRequestBuilder(java.time.Duration repeatInterval);
+ method public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.PeriodicWorkRequest.Builder! PeriodicWorkRequestBuilder(long repeatInterval, java.util.concurrent.TimeUnit repeatIntervalTimeUnit, long flexTimeInterval, java.util.concurrent.TimeUnit flexTimeIntervalUnit);
+ method @RequiresApi(26) public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.PeriodicWorkRequest.Builder! PeriodicWorkRequestBuilder(java.time.Duration repeatInterval, java.time.Duration flexTimeInterval);
+ }
+
+}
+
diff --git a/work/workmanager-ktx/api/public_plus_experimental_2.6.0-beta01.txt b/work/workmanager-ktx/api/public_plus_experimental_2.6.0-beta01.txt
new file mode 100644
index 0000000..2c5f419
--- /dev/null
+++ b/work/workmanager-ktx/api/public_plus_experimental_2.6.0-beta01.txt
@@ -0,0 +1,40 @@
+// Signature format: 4.0
+package androidx.work {
+
+ public abstract class CoroutineWorker extends androidx.work.ListenableWorker {
+ ctor public CoroutineWorker(android.content.Context appContext, androidx.work.WorkerParameters params);
+ method public abstract suspend Object? doWork(kotlin.coroutines.Continuation<? super androidx.work.ListenableWorker.Result> p);
+ method @Deprecated public kotlinx.coroutines.CoroutineDispatcher getCoroutineContext();
+ method public final void onStopped();
+ method public final suspend Object? setForeground(androidx.work.ForegroundInfo foregroundInfo, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+ method public final suspend Object? setProgress(androidx.work.Data data, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+ method public final com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result> startWork();
+ property @Deprecated public kotlinx.coroutines.CoroutineDispatcher coroutineContext;
+ }
+
+ public final class DataKt {
+ method public static inline <reified T> boolean hasKeyWithValueOfType(androidx.work.Data, String key);
+ method public static inline androidx.work.Data workDataOf(kotlin.Pair<java.lang.String,?>... pairs);
+ }
+
+ public final class ListenableFutureKt {
+ }
+
+ public final class OneTimeWorkRequestKt {
+ method public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.OneTimeWorkRequest.Builder! OneTimeWorkRequestBuilder();
+ method public static inline androidx.work.OneTimeWorkRequest.Builder setInputMerger(androidx.work.OneTimeWorkRequest.Builder, kotlin.reflect.KClass<? extends androidx.work.InputMerger> inputMerger);
+ }
+
+ public final class OperationKt {
+ method public static suspend inline Object? await(androidx.work.Operation, kotlin.coroutines.Continuation<? super androidx.work.Operation.State.SUCCESS> p);
+ }
+
+ public final class PeriodicWorkRequestKt {
+ method public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.PeriodicWorkRequest.Builder! PeriodicWorkRequestBuilder(long repeatInterval, java.util.concurrent.TimeUnit repeatIntervalTimeUnit);
+ method @RequiresApi(26) public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.PeriodicWorkRequest.Builder! PeriodicWorkRequestBuilder(java.time.Duration repeatInterval);
+ method public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.PeriodicWorkRequest.Builder! PeriodicWorkRequestBuilder(long repeatInterval, java.util.concurrent.TimeUnit repeatIntervalTimeUnit, long flexTimeInterval, java.util.concurrent.TimeUnit flexTimeIntervalUnit);
+ method @RequiresApi(26) public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.PeriodicWorkRequest.Builder! PeriodicWorkRequestBuilder(java.time.Duration repeatInterval, java.time.Duration flexTimeInterval);
+ }
+
+}
+
diff --git a/work/workmanager-ktx/api/res-2.6.0-beta01.txt b/work/workmanager-ktx/api/res-2.6.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/work/workmanager-ktx/api/res-2.6.0-beta01.txt
diff --git a/work/workmanager-ktx/api/restricted_2.6.0-beta01.txt b/work/workmanager-ktx/api/restricted_2.6.0-beta01.txt
new file mode 100644
index 0000000..2c5f419
--- /dev/null
+++ b/work/workmanager-ktx/api/restricted_2.6.0-beta01.txt
@@ -0,0 +1,40 @@
+// Signature format: 4.0
+package androidx.work {
+
+ public abstract class CoroutineWorker extends androidx.work.ListenableWorker {
+ ctor public CoroutineWorker(android.content.Context appContext, androidx.work.WorkerParameters params);
+ method public abstract suspend Object? doWork(kotlin.coroutines.Continuation<? super androidx.work.ListenableWorker.Result> p);
+ method @Deprecated public kotlinx.coroutines.CoroutineDispatcher getCoroutineContext();
+ method public final void onStopped();
+ method public final suspend Object? setForeground(androidx.work.ForegroundInfo foregroundInfo, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+ method public final suspend Object? setProgress(androidx.work.Data data, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+ method public final com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result> startWork();
+ property @Deprecated public kotlinx.coroutines.CoroutineDispatcher coroutineContext;
+ }
+
+ public final class DataKt {
+ method public static inline <reified T> boolean hasKeyWithValueOfType(androidx.work.Data, String key);
+ method public static inline androidx.work.Data workDataOf(kotlin.Pair<java.lang.String,?>... pairs);
+ }
+
+ public final class ListenableFutureKt {
+ }
+
+ public final class OneTimeWorkRequestKt {
+ method public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.OneTimeWorkRequest.Builder! OneTimeWorkRequestBuilder();
+ method public static inline androidx.work.OneTimeWorkRequest.Builder setInputMerger(androidx.work.OneTimeWorkRequest.Builder, kotlin.reflect.KClass<? extends androidx.work.InputMerger> inputMerger);
+ }
+
+ public final class OperationKt {
+ method public static suspend inline Object? await(androidx.work.Operation, kotlin.coroutines.Continuation<? super androidx.work.Operation.State.SUCCESS> p);
+ }
+
+ public final class PeriodicWorkRequestKt {
+ method public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.PeriodicWorkRequest.Builder! PeriodicWorkRequestBuilder(long repeatInterval, java.util.concurrent.TimeUnit repeatIntervalTimeUnit);
+ method @RequiresApi(26) public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.PeriodicWorkRequest.Builder! PeriodicWorkRequestBuilder(java.time.Duration repeatInterval);
+ method public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.PeriodicWorkRequest.Builder! PeriodicWorkRequestBuilder(long repeatInterval, java.util.concurrent.TimeUnit repeatIntervalTimeUnit, long flexTimeInterval, java.util.concurrent.TimeUnit flexTimeIntervalUnit);
+ method @RequiresApi(26) public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.PeriodicWorkRequest.Builder! PeriodicWorkRequestBuilder(java.time.Duration repeatInterval, java.time.Duration flexTimeInterval);
+ }
+
+}
+
diff --git a/work/workmanager-lint/build.gradle b/work/workmanager-lint/build.gradle
index 557fb01..7329d99 100644
--- a/work/workmanager-lint/build.gradle
+++ b/work/workmanager-lint/build.gradle
@@ -25,13 +25,7 @@
}
dependencies {
- // compileOnly because we use lintChecks and it doesn't allow other types of deps
- // this ugly hack exists because of b/63873667
- if (rootProject.hasProperty("android.injected.invoked.from.ide")) {
- compileOnly(libs.androidLintApi)
- } else {
- compileOnly(LINT_API_MIN)
- }
+ compileOnly(LINT_API_MIN)
compileOnly(libs.kotlinStdlib)
testImplementation(libs.kotlinStdlib)
diff --git a/work/workmanager-multiprocess/api/2.6.0-beta01.txt b/work/workmanager-multiprocess/api/2.6.0-beta01.txt
new file mode 100644
index 0000000..e033a49
--- /dev/null
+++ b/work/workmanager-multiprocess/api/2.6.0-beta01.txt
@@ -0,0 +1,26 @@
+// Signature format: 4.0
+package androidx.work.multiprocess {
+
+ public abstract class RemoteCoroutineWorker extends androidx.work.multiprocess.RemoteListenableWorker {
+ ctor public RemoteCoroutineWorker(android.content.Context context, androidx.work.WorkerParameters parameters);
+ method public abstract suspend Object? doRemoteWork(kotlin.coroutines.Continuation<? super androidx.work.ListenableWorker.Result> p);
+ method public final void onStopped();
+ method public final suspend Object? setProgress(androidx.work.Data data, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+ method public com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result> startRemoteWork();
+ }
+
+ public abstract class RemoteListenableWorker extends androidx.work.ListenableWorker {
+ ctor public RemoteListenableWorker(android.content.Context, androidx.work.WorkerParameters);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result!> startRemoteWork();
+ method public final com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result!> startWork();
+ field public static final String ARGUMENT_CLASS_NAME = "androidx.work.impl.workers.RemoteListenableWorker.ARGUMENT_CLASS_NAME";
+ field public static final String ARGUMENT_PACKAGE_NAME = "androidx.work.impl.workers.RemoteListenableWorker.ARGUMENT_PACKAGE_NAME";
+ }
+
+ public class RemoteWorkerService extends android.app.Service {
+ ctor public RemoteWorkerService();
+ method public android.os.IBinder? onBind(android.content.Intent);
+ }
+
+}
+
diff --git a/work/workmanager-multiprocess/api/public_plus_experimental_2.6.0-beta01.txt b/work/workmanager-multiprocess/api/public_plus_experimental_2.6.0-beta01.txt
new file mode 100644
index 0000000..e033a49
--- /dev/null
+++ b/work/workmanager-multiprocess/api/public_plus_experimental_2.6.0-beta01.txt
@@ -0,0 +1,26 @@
+// Signature format: 4.0
+package androidx.work.multiprocess {
+
+ public abstract class RemoteCoroutineWorker extends androidx.work.multiprocess.RemoteListenableWorker {
+ ctor public RemoteCoroutineWorker(android.content.Context context, androidx.work.WorkerParameters parameters);
+ method public abstract suspend Object? doRemoteWork(kotlin.coroutines.Continuation<? super androidx.work.ListenableWorker.Result> p);
+ method public final void onStopped();
+ method public final suspend Object? setProgress(androidx.work.Data data, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+ method public com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result> startRemoteWork();
+ }
+
+ public abstract class RemoteListenableWorker extends androidx.work.ListenableWorker {
+ ctor public RemoteListenableWorker(android.content.Context, androidx.work.WorkerParameters);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result!> startRemoteWork();
+ method public final com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result!> startWork();
+ field public static final String ARGUMENT_CLASS_NAME = "androidx.work.impl.workers.RemoteListenableWorker.ARGUMENT_CLASS_NAME";
+ field public static final String ARGUMENT_PACKAGE_NAME = "androidx.work.impl.workers.RemoteListenableWorker.ARGUMENT_PACKAGE_NAME";
+ }
+
+ public class RemoteWorkerService extends android.app.Service {
+ ctor public RemoteWorkerService();
+ method public android.os.IBinder? onBind(android.content.Intent);
+ }
+
+}
+
diff --git a/work/workmanager-multiprocess/api/res-2.6.0-beta01.txt b/work/workmanager-multiprocess/api/res-2.6.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/work/workmanager-multiprocess/api/res-2.6.0-beta01.txt
diff --git a/work/workmanager-multiprocess/api/restricted_2.6.0-beta01.txt b/work/workmanager-multiprocess/api/restricted_2.6.0-beta01.txt
new file mode 100644
index 0000000..e033a49
--- /dev/null
+++ b/work/workmanager-multiprocess/api/restricted_2.6.0-beta01.txt
@@ -0,0 +1,26 @@
+// Signature format: 4.0
+package androidx.work.multiprocess {
+
+ public abstract class RemoteCoroutineWorker extends androidx.work.multiprocess.RemoteListenableWorker {
+ ctor public RemoteCoroutineWorker(android.content.Context context, androidx.work.WorkerParameters parameters);
+ method public abstract suspend Object? doRemoteWork(kotlin.coroutines.Continuation<? super androidx.work.ListenableWorker.Result> p);
+ method public final void onStopped();
+ method public final suspend Object? setProgress(androidx.work.Data data, kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+ method public com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result> startRemoteWork();
+ }
+
+ public abstract class RemoteListenableWorker extends androidx.work.ListenableWorker {
+ ctor public RemoteListenableWorker(android.content.Context, androidx.work.WorkerParameters);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result!> startRemoteWork();
+ method public final com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result!> startWork();
+ field public static final String ARGUMENT_CLASS_NAME = "androidx.work.impl.workers.RemoteListenableWorker.ARGUMENT_CLASS_NAME";
+ field public static final String ARGUMENT_PACKAGE_NAME = "androidx.work.impl.workers.RemoteListenableWorker.ARGUMENT_PACKAGE_NAME";
+ }
+
+ public class RemoteWorkerService extends android.app.Service {
+ ctor public RemoteWorkerService();
+ method public android.os.IBinder? onBind(android.content.Intent);
+ }
+
+}
+
diff --git a/work/workmanager-rxjava2/api/2.6.0-beta01.txt b/work/workmanager-rxjava2/api/2.6.0-beta01.txt
new file mode 100644
index 0000000..2d667ee
--- /dev/null
+++ b/work/workmanager-rxjava2/api/2.6.0-beta01.txt
@@ -0,0 +1,14 @@
+// Signature format: 4.0
+package androidx.work {
+
+ public abstract class RxWorker extends androidx.work.ListenableWorker {
+ ctor public RxWorker(android.content.Context, androidx.work.WorkerParameters);
+ method @MainThread public abstract io.reactivex.Single<androidx.work.ListenableWorker.Result!> createWork();
+ method protected io.reactivex.Scheduler getBackgroundScheduler();
+ method public final io.reactivex.Completable setCompletableProgress(androidx.work.Data);
+ method @Deprecated public final io.reactivex.Single<java.lang.Void!> setProgress(androidx.work.Data);
+ method public com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result!> startWork();
+ }
+
+}
+
diff --git a/work/workmanager-rxjava2/api/public_plus_experimental_2.6.0-beta01.txt b/work/workmanager-rxjava2/api/public_plus_experimental_2.6.0-beta01.txt
new file mode 100644
index 0000000..2d667ee
--- /dev/null
+++ b/work/workmanager-rxjava2/api/public_plus_experimental_2.6.0-beta01.txt
@@ -0,0 +1,14 @@
+// Signature format: 4.0
+package androidx.work {
+
+ public abstract class RxWorker extends androidx.work.ListenableWorker {
+ ctor public RxWorker(android.content.Context, androidx.work.WorkerParameters);
+ method @MainThread public abstract io.reactivex.Single<androidx.work.ListenableWorker.Result!> createWork();
+ method protected io.reactivex.Scheduler getBackgroundScheduler();
+ method public final io.reactivex.Completable setCompletableProgress(androidx.work.Data);
+ method @Deprecated public final io.reactivex.Single<java.lang.Void!> setProgress(androidx.work.Data);
+ method public com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result!> startWork();
+ }
+
+}
+
diff --git a/work/workmanager-rxjava2/api/res-2.6.0-beta01.txt b/work/workmanager-rxjava2/api/res-2.6.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/work/workmanager-rxjava2/api/res-2.6.0-beta01.txt
diff --git a/work/workmanager-rxjava2/api/restricted_2.6.0-beta01.txt b/work/workmanager-rxjava2/api/restricted_2.6.0-beta01.txt
new file mode 100644
index 0000000..2d667ee
--- /dev/null
+++ b/work/workmanager-rxjava2/api/restricted_2.6.0-beta01.txt
@@ -0,0 +1,14 @@
+// Signature format: 4.0
+package androidx.work {
+
+ public abstract class RxWorker extends androidx.work.ListenableWorker {
+ ctor public RxWorker(android.content.Context, androidx.work.WorkerParameters);
+ method @MainThread public abstract io.reactivex.Single<androidx.work.ListenableWorker.Result!> createWork();
+ method protected io.reactivex.Scheduler getBackgroundScheduler();
+ method public final io.reactivex.Completable setCompletableProgress(androidx.work.Data);
+ method @Deprecated public final io.reactivex.Single<java.lang.Void!> setProgress(androidx.work.Data);
+ method public com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result!> startWork();
+ }
+
+}
+
diff --git a/work/workmanager-rxjava3/api/2.6.0-beta01.txt b/work/workmanager-rxjava3/api/2.6.0-beta01.txt
new file mode 100644
index 0000000..9293340
--- /dev/null
+++ b/work/workmanager-rxjava3/api/2.6.0-beta01.txt
@@ -0,0 +1,13 @@
+// Signature format: 4.0
+package androidx.work.rxjava3 {
+
+ public abstract class RxWorker extends androidx.work.ListenableWorker {
+ ctor public RxWorker(android.content.Context, androidx.work.WorkerParameters);
+ method @MainThread public abstract io.reactivex.rxjava3.core.Single<androidx.work.ListenableWorker.Result!> createWork();
+ method protected io.reactivex.rxjava3.core.Scheduler getBackgroundScheduler();
+ method public final io.reactivex.rxjava3.core.Completable setCompletableProgress(androidx.work.Data);
+ method public final com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result!> startWork();
+ }
+
+}
+
diff --git a/work/workmanager-rxjava3/api/public_plus_experimental_2.6.0-beta01.txt b/work/workmanager-rxjava3/api/public_plus_experimental_2.6.0-beta01.txt
new file mode 100644
index 0000000..9293340
--- /dev/null
+++ b/work/workmanager-rxjava3/api/public_plus_experimental_2.6.0-beta01.txt
@@ -0,0 +1,13 @@
+// Signature format: 4.0
+package androidx.work.rxjava3 {
+
+ public abstract class RxWorker extends androidx.work.ListenableWorker {
+ ctor public RxWorker(android.content.Context, androidx.work.WorkerParameters);
+ method @MainThread public abstract io.reactivex.rxjava3.core.Single<androidx.work.ListenableWorker.Result!> createWork();
+ method protected io.reactivex.rxjava3.core.Scheduler getBackgroundScheduler();
+ method public final io.reactivex.rxjava3.core.Completable setCompletableProgress(androidx.work.Data);
+ method public final com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result!> startWork();
+ }
+
+}
+
diff --git a/work/workmanager-rxjava3/api/res-2.6.0-beta01.txt b/work/workmanager-rxjava3/api/res-2.6.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/work/workmanager-rxjava3/api/res-2.6.0-beta01.txt
diff --git a/work/workmanager-rxjava3/api/restricted_2.6.0-beta01.txt b/work/workmanager-rxjava3/api/restricted_2.6.0-beta01.txt
new file mode 100644
index 0000000..9293340
--- /dev/null
+++ b/work/workmanager-rxjava3/api/restricted_2.6.0-beta01.txt
@@ -0,0 +1,13 @@
+// Signature format: 4.0
+package androidx.work.rxjava3 {
+
+ public abstract class RxWorker extends androidx.work.ListenableWorker {
+ ctor public RxWorker(android.content.Context, androidx.work.WorkerParameters);
+ method @MainThread public abstract io.reactivex.rxjava3.core.Single<androidx.work.ListenableWorker.Result!> createWork();
+ method protected io.reactivex.rxjava3.core.Scheduler getBackgroundScheduler();
+ method public final io.reactivex.rxjava3.core.Completable setCompletableProgress(androidx.work.Data);
+ method public final com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result!> startWork();
+ }
+
+}
+
diff --git a/work/workmanager-testing/api/2.6.0-beta01.txt b/work/workmanager-testing/api/2.6.0-beta01.txt
new file mode 100644
index 0000000..f3f3fe2
--- /dev/null
+++ b/work/workmanager-testing/api/2.6.0-beta01.txt
@@ -0,0 +1,52 @@
+// Signature format: 4.0
+package androidx.work.testing {
+
+ public class SynchronousExecutor implements java.util.concurrent.Executor {
+ ctor public SynchronousExecutor();
+ method public void execute(Runnable);
+ }
+
+ public interface TestDriver {
+ method public void setAllConstraintsMet(java.util.UUID);
+ method public void setInitialDelayMet(java.util.UUID);
+ method public void setPeriodDelayMet(java.util.UUID);
+ }
+
+ public class TestListenableWorkerBuilder<W extends androidx.work.ListenableWorker> {
+ method public W build();
+ method public static androidx.work.testing.TestListenableWorkerBuilder<? extends androidx.work.ListenableWorker> from(android.content.Context, androidx.work.WorkRequest);
+ method public static <W extends androidx.work.ListenableWorker> androidx.work.testing.TestListenableWorkerBuilder<W!> from(android.content.Context, Class<W!>);
+ method public androidx.work.testing.TestListenableWorkerBuilder<W!> setForegroundUpdater(androidx.work.ForegroundUpdater);
+ method public androidx.work.testing.TestListenableWorkerBuilder<W!> setId(java.util.UUID);
+ method public androidx.work.testing.TestListenableWorkerBuilder<W!> setInputData(androidx.work.Data);
+ method @RequiresApi(28) public androidx.work.testing.TestListenableWorkerBuilder<W!> setNetwork(android.net.Network);
+ method public androidx.work.testing.TestListenableWorkerBuilder<W!> setProgressUpdater(androidx.work.ProgressUpdater);
+ method public androidx.work.testing.TestListenableWorkerBuilder<W!> setRunAttemptCount(int);
+ method public androidx.work.testing.TestListenableWorkerBuilder<W!> setTags(java.util.List<java.lang.String!>);
+ method @RequiresApi(24) public androidx.work.testing.TestListenableWorkerBuilder<W!> setTriggeredContentAuthorities(java.util.List<java.lang.String!>);
+ method @RequiresApi(24) public androidx.work.testing.TestListenableWorkerBuilder<W!> setTriggeredContentUris(java.util.List<android.net.Uri!>);
+ method public androidx.work.testing.TestListenableWorkerBuilder<W!> setWorkerFactory(androidx.work.WorkerFactory);
+ }
+
+ public final class TestListenableWorkerBuilderKt {
+ method public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.testing.TestListenableWorkerBuilder<W>! TestListenableWorkerBuilder(android.content.Context context, optional androidx.work.Data inputData, optional java.util.List<? extends java.lang.String> tags, optional int runAttemptCount, optional java.util.List<? extends android.net.Uri> triggeredContentUris, optional java.util.List<? extends java.lang.String> triggeredContentAuthorities);
+ }
+
+ public class TestWorkerBuilder<W extends androidx.work.Worker> extends androidx.work.testing.TestListenableWorkerBuilder<W> {
+ method public static androidx.work.testing.TestWorkerBuilder<? extends androidx.work.Worker> from(android.content.Context, androidx.work.WorkRequest, java.util.concurrent.Executor);
+ method public static <W extends androidx.work.Worker> androidx.work.testing.TestWorkerBuilder<W!> from(android.content.Context, Class<W!>, java.util.concurrent.Executor);
+ }
+
+ public final class TestWorkerBuilderKt {
+ method public static inline <reified W extends androidx.work.Worker> androidx.work.testing.TestWorkerBuilder<W>! TestWorkerBuilder(android.content.Context context, java.util.concurrent.Executor executor, optional androidx.work.Data inputData, optional java.util.List<? extends java.lang.String> tags, optional int runAttemptCount, optional java.util.List<? extends android.net.Uri> triggeredContentUris, optional java.util.List<? extends java.lang.String> triggeredContentAuthorities);
+ }
+
+ public final class WorkManagerTestInitHelper {
+ method @Deprecated public static androidx.work.testing.TestDriver? getTestDriver();
+ method public static androidx.work.testing.TestDriver? getTestDriver(android.content.Context);
+ method public static void initializeTestWorkManager(android.content.Context);
+ method public static void initializeTestWorkManager(android.content.Context, androidx.work.Configuration);
+ }
+
+}
+
diff --git a/work/workmanager-testing/api/public_plus_experimental_2.6.0-beta01.txt b/work/workmanager-testing/api/public_plus_experimental_2.6.0-beta01.txt
new file mode 100644
index 0000000..f3f3fe2
--- /dev/null
+++ b/work/workmanager-testing/api/public_plus_experimental_2.6.0-beta01.txt
@@ -0,0 +1,52 @@
+// Signature format: 4.0
+package androidx.work.testing {
+
+ public class SynchronousExecutor implements java.util.concurrent.Executor {
+ ctor public SynchronousExecutor();
+ method public void execute(Runnable);
+ }
+
+ public interface TestDriver {
+ method public void setAllConstraintsMet(java.util.UUID);
+ method public void setInitialDelayMet(java.util.UUID);
+ method public void setPeriodDelayMet(java.util.UUID);
+ }
+
+ public class TestListenableWorkerBuilder<W extends androidx.work.ListenableWorker> {
+ method public W build();
+ method public static androidx.work.testing.TestListenableWorkerBuilder<? extends androidx.work.ListenableWorker> from(android.content.Context, androidx.work.WorkRequest);
+ method public static <W extends androidx.work.ListenableWorker> androidx.work.testing.TestListenableWorkerBuilder<W!> from(android.content.Context, Class<W!>);
+ method public androidx.work.testing.TestListenableWorkerBuilder<W!> setForegroundUpdater(androidx.work.ForegroundUpdater);
+ method public androidx.work.testing.TestListenableWorkerBuilder<W!> setId(java.util.UUID);
+ method public androidx.work.testing.TestListenableWorkerBuilder<W!> setInputData(androidx.work.Data);
+ method @RequiresApi(28) public androidx.work.testing.TestListenableWorkerBuilder<W!> setNetwork(android.net.Network);
+ method public androidx.work.testing.TestListenableWorkerBuilder<W!> setProgressUpdater(androidx.work.ProgressUpdater);
+ method public androidx.work.testing.TestListenableWorkerBuilder<W!> setRunAttemptCount(int);
+ method public androidx.work.testing.TestListenableWorkerBuilder<W!> setTags(java.util.List<java.lang.String!>);
+ method @RequiresApi(24) public androidx.work.testing.TestListenableWorkerBuilder<W!> setTriggeredContentAuthorities(java.util.List<java.lang.String!>);
+ method @RequiresApi(24) public androidx.work.testing.TestListenableWorkerBuilder<W!> setTriggeredContentUris(java.util.List<android.net.Uri!>);
+ method public androidx.work.testing.TestListenableWorkerBuilder<W!> setWorkerFactory(androidx.work.WorkerFactory);
+ }
+
+ public final class TestListenableWorkerBuilderKt {
+ method public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.testing.TestListenableWorkerBuilder<W>! TestListenableWorkerBuilder(android.content.Context context, optional androidx.work.Data inputData, optional java.util.List<? extends java.lang.String> tags, optional int runAttemptCount, optional java.util.List<? extends android.net.Uri> triggeredContentUris, optional java.util.List<? extends java.lang.String> triggeredContentAuthorities);
+ }
+
+ public class TestWorkerBuilder<W extends androidx.work.Worker> extends androidx.work.testing.TestListenableWorkerBuilder<W> {
+ method public static androidx.work.testing.TestWorkerBuilder<? extends androidx.work.Worker> from(android.content.Context, androidx.work.WorkRequest, java.util.concurrent.Executor);
+ method public static <W extends androidx.work.Worker> androidx.work.testing.TestWorkerBuilder<W!> from(android.content.Context, Class<W!>, java.util.concurrent.Executor);
+ }
+
+ public final class TestWorkerBuilderKt {
+ method public static inline <reified W extends androidx.work.Worker> androidx.work.testing.TestWorkerBuilder<W>! TestWorkerBuilder(android.content.Context context, java.util.concurrent.Executor executor, optional androidx.work.Data inputData, optional java.util.List<? extends java.lang.String> tags, optional int runAttemptCount, optional java.util.List<? extends android.net.Uri> triggeredContentUris, optional java.util.List<? extends java.lang.String> triggeredContentAuthorities);
+ }
+
+ public final class WorkManagerTestInitHelper {
+ method @Deprecated public static androidx.work.testing.TestDriver? getTestDriver();
+ method public static androidx.work.testing.TestDriver? getTestDriver(android.content.Context);
+ method public static void initializeTestWorkManager(android.content.Context);
+ method public static void initializeTestWorkManager(android.content.Context, androidx.work.Configuration);
+ }
+
+}
+
diff --git a/work/workmanager-testing/api/res-2.6.0-beta01.txt b/work/workmanager-testing/api/res-2.6.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/work/workmanager-testing/api/res-2.6.0-beta01.txt
diff --git a/work/workmanager-testing/api/restricted_2.6.0-beta01.txt b/work/workmanager-testing/api/restricted_2.6.0-beta01.txt
new file mode 100644
index 0000000..f3f3fe2
--- /dev/null
+++ b/work/workmanager-testing/api/restricted_2.6.0-beta01.txt
@@ -0,0 +1,52 @@
+// Signature format: 4.0
+package androidx.work.testing {
+
+ public class SynchronousExecutor implements java.util.concurrent.Executor {
+ ctor public SynchronousExecutor();
+ method public void execute(Runnable);
+ }
+
+ public interface TestDriver {
+ method public void setAllConstraintsMet(java.util.UUID);
+ method public void setInitialDelayMet(java.util.UUID);
+ method public void setPeriodDelayMet(java.util.UUID);
+ }
+
+ public class TestListenableWorkerBuilder<W extends androidx.work.ListenableWorker> {
+ method public W build();
+ method public static androidx.work.testing.TestListenableWorkerBuilder<? extends androidx.work.ListenableWorker> from(android.content.Context, androidx.work.WorkRequest);
+ method public static <W extends androidx.work.ListenableWorker> androidx.work.testing.TestListenableWorkerBuilder<W!> from(android.content.Context, Class<W!>);
+ method public androidx.work.testing.TestListenableWorkerBuilder<W!> setForegroundUpdater(androidx.work.ForegroundUpdater);
+ method public androidx.work.testing.TestListenableWorkerBuilder<W!> setId(java.util.UUID);
+ method public androidx.work.testing.TestListenableWorkerBuilder<W!> setInputData(androidx.work.Data);
+ method @RequiresApi(28) public androidx.work.testing.TestListenableWorkerBuilder<W!> setNetwork(android.net.Network);
+ method public androidx.work.testing.TestListenableWorkerBuilder<W!> setProgressUpdater(androidx.work.ProgressUpdater);
+ method public androidx.work.testing.TestListenableWorkerBuilder<W!> setRunAttemptCount(int);
+ method public androidx.work.testing.TestListenableWorkerBuilder<W!> setTags(java.util.List<java.lang.String!>);
+ method @RequiresApi(24) public androidx.work.testing.TestListenableWorkerBuilder<W!> setTriggeredContentAuthorities(java.util.List<java.lang.String!>);
+ method @RequiresApi(24) public androidx.work.testing.TestListenableWorkerBuilder<W!> setTriggeredContentUris(java.util.List<android.net.Uri!>);
+ method public androidx.work.testing.TestListenableWorkerBuilder<W!> setWorkerFactory(androidx.work.WorkerFactory);
+ }
+
+ public final class TestListenableWorkerBuilderKt {
+ method public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.testing.TestListenableWorkerBuilder<W>! TestListenableWorkerBuilder(android.content.Context context, optional androidx.work.Data inputData, optional java.util.List<? extends java.lang.String> tags, optional int runAttemptCount, optional java.util.List<? extends android.net.Uri> triggeredContentUris, optional java.util.List<? extends java.lang.String> triggeredContentAuthorities);
+ }
+
+ public class TestWorkerBuilder<W extends androidx.work.Worker> extends androidx.work.testing.TestListenableWorkerBuilder<W> {
+ method public static androidx.work.testing.TestWorkerBuilder<? extends androidx.work.Worker> from(android.content.Context, androidx.work.WorkRequest, java.util.concurrent.Executor);
+ method public static <W extends androidx.work.Worker> androidx.work.testing.TestWorkerBuilder<W!> from(android.content.Context, Class<W!>, java.util.concurrent.Executor);
+ }
+
+ public final class TestWorkerBuilderKt {
+ method public static inline <reified W extends androidx.work.Worker> androidx.work.testing.TestWorkerBuilder<W>! TestWorkerBuilder(android.content.Context context, java.util.concurrent.Executor executor, optional androidx.work.Data inputData, optional java.util.List<? extends java.lang.String> tags, optional int runAttemptCount, optional java.util.List<? extends android.net.Uri> triggeredContentUris, optional java.util.List<? extends java.lang.String> triggeredContentAuthorities);
+ }
+
+ public final class WorkManagerTestInitHelper {
+ method @Deprecated public static androidx.work.testing.TestDriver? getTestDriver();
+ method public static androidx.work.testing.TestDriver? getTestDriver(android.content.Context);
+ method public static void initializeTestWorkManager(android.content.Context);
+ method public static void initializeTestWorkManager(android.content.Context, androidx.work.Configuration);
+ }
+
+}
+
diff --git a/work/workmanager/api/2.6.0-beta01.txt b/work/workmanager/api/2.6.0-beta01.txt
new file mode 100644
index 0000000..54713f5
--- /dev/null
+++ b/work/workmanager/api/2.6.0-beta01.txt
@@ -0,0 +1,400 @@
+// Signature format: 4.0
+package androidx.work {
+
+ public final class ArrayCreatingInputMerger extends androidx.work.InputMerger {
+ ctor public ArrayCreatingInputMerger();
+ method public androidx.work.Data merge(java.util.List<androidx.work.Data!>);
+ }
+
+ public enum BackoffPolicy {
+ enum_constant public static final androidx.work.BackoffPolicy EXPONENTIAL;
+ enum_constant public static final androidx.work.BackoffPolicy LINEAR;
+ }
+
+ public final class Configuration {
+ method public String? getDefaultProcessName();
+ method public java.util.concurrent.Executor getExecutor();
+ method public androidx.work.InputMergerFactory getInputMergerFactory();
+ method public int getMaxJobSchedulerId();
+ method public int getMinJobSchedulerId();
+ method public androidx.work.RunnableScheduler getRunnableScheduler();
+ method public java.util.concurrent.Executor getTaskExecutor();
+ method public androidx.work.WorkerFactory getWorkerFactory();
+ field public static final int MIN_SCHEDULER_LIMIT = 20; // 0x14
+ }
+
+ public static final class Configuration.Builder {
+ ctor public Configuration.Builder();
+ method public androidx.work.Configuration build();
+ method public androidx.work.Configuration.Builder setDefaultProcessName(String);
+ method public androidx.work.Configuration.Builder setExecutor(java.util.concurrent.Executor);
+ method public androidx.work.Configuration.Builder setInputMergerFactory(androidx.work.InputMergerFactory);
+ method public androidx.work.Configuration.Builder setJobSchedulerJobIdRange(int, int);
+ method public androidx.work.Configuration.Builder setMaxSchedulerLimit(int);
+ method public androidx.work.Configuration.Builder setMinimumLoggingLevel(int);
+ method public androidx.work.Configuration.Builder setRunnableScheduler(androidx.work.RunnableScheduler);
+ method public androidx.work.Configuration.Builder setTaskExecutor(java.util.concurrent.Executor);
+ method public androidx.work.Configuration.Builder setWorkerFactory(androidx.work.WorkerFactory);
+ }
+
+ public static interface Configuration.Provider {
+ method public androidx.work.Configuration getWorkManagerConfiguration();
+ }
+
+ public final class Constraints {
+ ctor public Constraints(androidx.work.Constraints);
+ method public androidx.work.NetworkType getRequiredNetworkType();
+ method public boolean requiresBatteryNotLow();
+ method public boolean requiresCharging();
+ method @RequiresApi(23) public boolean requiresDeviceIdle();
+ method public boolean requiresStorageNotLow();
+ field public static final androidx.work.Constraints NONE;
+ }
+
+ public static final class Constraints.Builder {
+ ctor public Constraints.Builder();
+ method @RequiresApi(24) public androidx.work.Constraints.Builder addContentUriTrigger(android.net.Uri, boolean);
+ method public androidx.work.Constraints build();
+ method public androidx.work.Constraints.Builder setRequiredNetworkType(androidx.work.NetworkType);
+ method public androidx.work.Constraints.Builder setRequiresBatteryNotLow(boolean);
+ method public androidx.work.Constraints.Builder setRequiresCharging(boolean);
+ method @RequiresApi(23) public androidx.work.Constraints.Builder setRequiresDeviceIdle(boolean);
+ method public androidx.work.Constraints.Builder setRequiresStorageNotLow(boolean);
+ method @RequiresApi(24) public androidx.work.Constraints.Builder setTriggerContentMaxDelay(long, java.util.concurrent.TimeUnit);
+ method @RequiresApi(26) public androidx.work.Constraints.Builder setTriggerContentMaxDelay(java.time.Duration!);
+ method @RequiresApi(24) public androidx.work.Constraints.Builder setTriggerContentUpdateDelay(long, java.util.concurrent.TimeUnit);
+ method @RequiresApi(26) public androidx.work.Constraints.Builder setTriggerContentUpdateDelay(java.time.Duration!);
+ }
+
+ public final class Data {
+ ctor public Data(androidx.work.Data);
+ method @androidx.room.TypeConverter public static androidx.work.Data fromByteArray(byte[]);
+ method public boolean getBoolean(String, boolean);
+ method public boolean[]? getBooleanArray(String);
+ method public byte getByte(String, byte);
+ method public byte[]? getByteArray(String);
+ method public double getDouble(String, double);
+ method public double[]? getDoubleArray(String);
+ method public float getFloat(String, float);
+ method public float[]? getFloatArray(String);
+ method public int getInt(String, int);
+ method public int[]? getIntArray(String);
+ method public java.util.Map<java.lang.String!,java.lang.Object!> getKeyValueMap();
+ method public long getLong(String, long);
+ method public long[]? getLongArray(String);
+ method public String? getString(String);
+ method public String![]? getStringArray(String);
+ method public <T> boolean hasKeyWithValueOfType(String, Class<T!>);
+ method public byte[] toByteArray();
+ field public static final androidx.work.Data EMPTY;
+ field public static final int MAX_DATA_BYTES = 10240; // 0x2800
+ }
+
+ public static final class Data.Builder {
+ ctor public Data.Builder();
+ method public androidx.work.Data build();
+ method public androidx.work.Data.Builder putAll(androidx.work.Data);
+ method public androidx.work.Data.Builder putAll(java.util.Map<java.lang.String!,java.lang.Object!>);
+ method public androidx.work.Data.Builder putBoolean(String, boolean);
+ method public androidx.work.Data.Builder putBooleanArray(String, boolean[]);
+ method public androidx.work.Data.Builder putByte(String, byte);
+ method public androidx.work.Data.Builder putByteArray(String, byte[]);
+ method public androidx.work.Data.Builder putDouble(String, double);
+ method public androidx.work.Data.Builder putDoubleArray(String, double[]);
+ method public androidx.work.Data.Builder putFloat(String, float);
+ method public androidx.work.Data.Builder putFloatArray(String, float[]);
+ method public androidx.work.Data.Builder putInt(String, int);
+ method public androidx.work.Data.Builder putIntArray(String, int[]);
+ method public androidx.work.Data.Builder putLong(String, long);
+ method public androidx.work.Data.Builder putLongArray(String, long[]);
+ method public androidx.work.Data.Builder putString(String, String?);
+ method public androidx.work.Data.Builder putStringArray(String, String![]);
+ }
+
+ public class DelegatingWorkerFactory extends androidx.work.WorkerFactory {
+ ctor public DelegatingWorkerFactory();
+ method public final void addFactory(androidx.work.WorkerFactory);
+ method public final androidx.work.ListenableWorker? createWorker(android.content.Context, String, androidx.work.WorkerParameters);
+ }
+
+ public enum ExistingPeriodicWorkPolicy {
+ enum_constant public static final androidx.work.ExistingPeriodicWorkPolicy KEEP;
+ enum_constant public static final androidx.work.ExistingPeriodicWorkPolicy REPLACE;
+ }
+
+ public enum ExistingWorkPolicy {
+ enum_constant public static final androidx.work.ExistingWorkPolicy APPEND;
+ enum_constant public static final androidx.work.ExistingWorkPolicy APPEND_OR_REPLACE;
+ enum_constant public static final androidx.work.ExistingWorkPolicy KEEP;
+ enum_constant public static final androidx.work.ExistingWorkPolicy REPLACE;
+ }
+
+ public final class ForegroundInfo {
+ ctor public ForegroundInfo(int, android.app.Notification);
+ ctor public ForegroundInfo(int, android.app.Notification, int);
+ method public int getForegroundServiceType();
+ method public android.app.Notification getNotification();
+ method public int getNotificationId();
+ }
+
+ public interface ForegroundUpdater {
+ method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setForegroundAsync(android.content.Context, java.util.UUID, androidx.work.ForegroundInfo);
+ }
+
+ public abstract class InputMerger {
+ ctor public InputMerger();
+ method public abstract androidx.work.Data merge(java.util.List<androidx.work.Data!>);
+ }
+
+ public abstract class InputMergerFactory {
+ ctor public InputMergerFactory();
+ method public abstract androidx.work.InputMerger? createInputMerger(String);
+ }
+
+ public abstract class ListenableWorker {
+ ctor @Keep public ListenableWorker(android.content.Context, androidx.work.WorkerParameters);
+ method public final android.content.Context getApplicationContext();
+ method public final java.util.UUID getId();
+ method public final androidx.work.Data getInputData();
+ method @RequiresApi(28) public final android.net.Network? getNetwork();
+ method @IntRange(from=0) public final int getRunAttemptCount();
+ method public final java.util.Set<java.lang.String!> getTags();
+ method @RequiresApi(24) public final java.util.List<java.lang.String!> getTriggeredContentAuthorities();
+ method @RequiresApi(24) public final java.util.List<android.net.Uri!> getTriggeredContentUris();
+ method public final boolean isStopped();
+ method public void onStopped();
+ method public final com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setForegroundAsync(androidx.work.ForegroundInfo);
+ method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setProgressAsync(androidx.work.Data);
+ method @MainThread public abstract com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result!> startWork();
+ }
+
+ public abstract static class ListenableWorker.Result {
+ method public static androidx.work.ListenableWorker.Result failure();
+ method public static androidx.work.ListenableWorker.Result failure(androidx.work.Data);
+ method public abstract androidx.work.Data getOutputData();
+ method public static androidx.work.ListenableWorker.Result retry();
+ method public static androidx.work.ListenableWorker.Result success();
+ method public static androidx.work.ListenableWorker.Result success(androidx.work.Data);
+ }
+
+ public enum NetworkType {
+ enum_constant public static final androidx.work.NetworkType CONNECTED;
+ enum_constant public static final androidx.work.NetworkType METERED;
+ enum_constant public static final androidx.work.NetworkType NOT_REQUIRED;
+ enum_constant public static final androidx.work.NetworkType NOT_ROAMING;
+ enum_constant @RequiresApi(30) public static final androidx.work.NetworkType TEMPORARILY_UNMETERED;
+ enum_constant public static final androidx.work.NetworkType UNMETERED;
+ }
+
+ public final class OneTimeWorkRequest extends androidx.work.WorkRequest {
+ method public static androidx.work.OneTimeWorkRequest from(Class<? extends androidx.work.ListenableWorker>);
+ method public static java.util.List<androidx.work.OneTimeWorkRequest!> from(java.util.List<java.lang.Class<? extends androidx.work.ListenableWorker>!>);
+ }
+
+ public static final class OneTimeWorkRequest.Builder extends androidx.work.WorkRequest.Builder<androidx.work.OneTimeWorkRequest.Builder,androidx.work.OneTimeWorkRequest> {
+ ctor public OneTimeWorkRequest.Builder(Class<? extends androidx.work.ListenableWorker>);
+ method public androidx.work.OneTimeWorkRequest.Builder setInputMerger(Class<? extends androidx.work.InputMerger>);
+ }
+
+ public interface Operation {
+ method public com.google.common.util.concurrent.ListenableFuture<androidx.work.Operation.State.SUCCESS!> getResult();
+ method public androidx.lifecycle.LiveData<androidx.work.Operation.State!> getState();
+ }
+
+ public abstract static class Operation.State {
+ }
+
+ public static final class Operation.State.FAILURE extends androidx.work.Operation.State {
+ ctor public Operation.State.FAILURE(Throwable);
+ method public Throwable getThrowable();
+ }
+
+ public static final class Operation.State.IN_PROGRESS extends androidx.work.Operation.State {
+ }
+
+ public static final class Operation.State.SUCCESS extends androidx.work.Operation.State {
+ }
+
+ public final class OverwritingInputMerger extends androidx.work.InputMerger {
+ ctor public OverwritingInputMerger();
+ method public androidx.work.Data merge(java.util.List<androidx.work.Data!>);
+ }
+
+ public final class PeriodicWorkRequest extends androidx.work.WorkRequest {
+ field public static final long MIN_PERIODIC_FLEX_MILLIS = 300000L; // 0x493e0L
+ field public static final long MIN_PERIODIC_INTERVAL_MILLIS = 900000L; // 0xdbba0L
+ }
+
+ public static final class PeriodicWorkRequest.Builder extends androidx.work.WorkRequest.Builder<androidx.work.PeriodicWorkRequest.Builder,androidx.work.PeriodicWorkRequest> {
+ ctor public PeriodicWorkRequest.Builder(Class<? extends androidx.work.ListenableWorker>, long, java.util.concurrent.TimeUnit);
+ ctor @RequiresApi(26) public PeriodicWorkRequest.Builder(Class<? extends androidx.work.ListenableWorker>, java.time.Duration);
+ ctor public PeriodicWorkRequest.Builder(Class<? extends androidx.work.ListenableWorker>, long, java.util.concurrent.TimeUnit, long, java.util.concurrent.TimeUnit);
+ ctor @RequiresApi(26) public PeriodicWorkRequest.Builder(Class<? extends androidx.work.ListenableWorker>, java.time.Duration, java.time.Duration);
+ }
+
+ public interface ProgressUpdater {
+ method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> updateProgress(android.content.Context, java.util.UUID, androidx.work.Data);
+ }
+
+ public interface RunnableScheduler {
+ method public void cancel(Runnable);
+ method public void scheduleWithDelay(@IntRange(from=0) long, Runnable);
+ }
+
+ public abstract class WorkContinuation {
+ ctor public WorkContinuation();
+ method public static androidx.work.WorkContinuation combine(java.util.List<androidx.work.WorkContinuation!>);
+ method public abstract androidx.work.Operation enqueue();
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.util.List<androidx.work.WorkInfo!>!> getWorkInfos();
+ method public abstract androidx.lifecycle.LiveData<java.util.List<androidx.work.WorkInfo!>!> getWorkInfosLiveData();
+ method public final androidx.work.WorkContinuation then(androidx.work.OneTimeWorkRequest);
+ method public abstract androidx.work.WorkContinuation then(java.util.List<androidx.work.OneTimeWorkRequest!>);
+ }
+
+ public final class WorkInfo {
+ method public java.util.UUID getId();
+ method public androidx.work.Data getOutputData();
+ method public androidx.work.Data getProgress();
+ method @IntRange(from=0) public int getRunAttemptCount();
+ method public androidx.work.WorkInfo.State getState();
+ method public java.util.Set<java.lang.String!> getTags();
+ }
+
+ public enum WorkInfo.State {
+ method public boolean isFinished();
+ enum_constant public static final androidx.work.WorkInfo.State BLOCKED;
+ enum_constant public static final androidx.work.WorkInfo.State CANCELLED;
+ enum_constant public static final androidx.work.WorkInfo.State ENQUEUED;
+ enum_constant public static final androidx.work.WorkInfo.State FAILED;
+ enum_constant public static final androidx.work.WorkInfo.State RUNNING;
+ enum_constant public static final androidx.work.WorkInfo.State SUCCEEDED;
+ }
+
+ public abstract class WorkManager {
+ method public final androidx.work.WorkContinuation beginUniqueWork(String, androidx.work.ExistingWorkPolicy, androidx.work.OneTimeWorkRequest);
+ method public abstract androidx.work.WorkContinuation beginUniqueWork(String, androidx.work.ExistingWorkPolicy, java.util.List<androidx.work.OneTimeWorkRequest!>);
+ method public final androidx.work.WorkContinuation beginWith(androidx.work.OneTimeWorkRequest);
+ method public abstract androidx.work.WorkContinuation beginWith(java.util.List<androidx.work.OneTimeWorkRequest!>);
+ method public abstract androidx.work.Operation cancelAllWork();
+ method public abstract androidx.work.Operation cancelAllWorkByTag(String);
+ method public abstract androidx.work.Operation cancelUniqueWork(String);
+ method public abstract androidx.work.Operation cancelWorkById(java.util.UUID);
+ method public abstract android.app.PendingIntent createCancelPendingIntent(java.util.UUID);
+ method public final androidx.work.Operation enqueue(androidx.work.WorkRequest);
+ method public abstract androidx.work.Operation enqueue(java.util.List<? extends androidx.work.WorkRequest>);
+ method public abstract androidx.work.Operation enqueueUniquePeriodicWork(String, androidx.work.ExistingPeriodicWorkPolicy, androidx.work.PeriodicWorkRequest);
+ method public androidx.work.Operation enqueueUniqueWork(String, androidx.work.ExistingWorkPolicy, androidx.work.OneTimeWorkRequest);
+ method public abstract androidx.work.Operation enqueueUniqueWork(String, androidx.work.ExistingWorkPolicy, java.util.List<androidx.work.OneTimeWorkRequest!>);
+ method @Deprecated public static androidx.work.WorkManager getInstance();
+ method public static androidx.work.WorkManager getInstance(android.content.Context);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Long!> getLastCancelAllTimeMillis();
+ method public abstract androidx.lifecycle.LiveData<java.lang.Long!> getLastCancelAllTimeMillisLiveData();
+ method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.work.WorkInfo!> getWorkInfoById(java.util.UUID);
+ method public abstract androidx.lifecycle.LiveData<androidx.work.WorkInfo!> getWorkInfoByIdLiveData(java.util.UUID);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.util.List<androidx.work.WorkInfo!>!> getWorkInfos(androidx.work.WorkQuery);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.util.List<androidx.work.WorkInfo!>!> getWorkInfosByTag(String);
+ method public abstract androidx.lifecycle.LiveData<java.util.List<androidx.work.WorkInfo!>!> getWorkInfosByTagLiveData(String);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.util.List<androidx.work.WorkInfo!>!> getWorkInfosForUniqueWork(String);
+ method public abstract androidx.lifecycle.LiveData<java.util.List<androidx.work.WorkInfo!>!> getWorkInfosForUniqueWorkLiveData(String);
+ method public abstract androidx.lifecycle.LiveData<java.util.List<androidx.work.WorkInfo!>!> getWorkInfosLiveData(androidx.work.WorkQuery);
+ method public static void initialize(android.content.Context, androidx.work.Configuration);
+ method public abstract androidx.work.Operation pruneWork();
+ }
+
+ public final class WorkManagerInitializer implements androidx.startup.Initializer<androidx.work.WorkManager> {
+ ctor public WorkManagerInitializer();
+ method public androidx.work.WorkManager create(android.content.Context);
+ method public java.util.List<java.lang.Class<? extends androidx.startup.Initializer<?>>!> dependencies();
+ }
+
+ public final class WorkQuery {
+ method public java.util.List<java.util.UUID!> getIds();
+ method public java.util.List<androidx.work.WorkInfo.State!> getStates();
+ method public java.util.List<java.lang.String!> getTags();
+ method public java.util.List<java.lang.String!> getUniqueWorkNames();
+ }
+
+ public static final class WorkQuery.Builder {
+ method public androidx.work.WorkQuery.Builder addIds(java.util.List<java.util.UUID!>);
+ method public androidx.work.WorkQuery.Builder addStates(java.util.List<androidx.work.WorkInfo.State!>);
+ method public androidx.work.WorkQuery.Builder addTags(java.util.List<java.lang.String!>);
+ method public androidx.work.WorkQuery.Builder addUniqueWorkNames(java.util.List<java.lang.String!>);
+ method public androidx.work.WorkQuery build();
+ method public static androidx.work.WorkQuery.Builder fromIds(java.util.List<java.util.UUID!>);
+ method public static androidx.work.WorkQuery.Builder fromStates(java.util.List<androidx.work.WorkInfo.State!>);
+ method public static androidx.work.WorkQuery.Builder fromTags(java.util.List<java.lang.String!>);
+ method public static androidx.work.WorkQuery.Builder fromUniqueWorkNames(java.util.List<java.lang.String!>);
+ }
+
+ public abstract class WorkRequest {
+ method public java.util.UUID getId();
+ field public static final long DEFAULT_BACKOFF_DELAY_MILLIS = 30000L; // 0x7530L
+ field public static final long MAX_BACKOFF_MILLIS = 18000000L; // 0x112a880L
+ field public static final long MIN_BACKOFF_MILLIS = 10000L; // 0x2710L
+ }
+
+ public abstract static class WorkRequest.Builder<B extends androidx.work.WorkRequest.Builder<?, ?>, W extends androidx.work.WorkRequest> {
+ method public final B addTag(String);
+ method public final W build();
+ method public final B keepResultsForAtLeast(long, java.util.concurrent.TimeUnit);
+ method @RequiresApi(26) public final B keepResultsForAtLeast(java.time.Duration);
+ method public final B setBackoffCriteria(androidx.work.BackoffPolicy, long, java.util.concurrent.TimeUnit);
+ method @RequiresApi(26) public final B setBackoffCriteria(androidx.work.BackoffPolicy, java.time.Duration);
+ method public final B setConstraints(androidx.work.Constraints);
+ method public B setInitialDelay(long, java.util.concurrent.TimeUnit);
+ method @RequiresApi(26) public B setInitialDelay(java.time.Duration);
+ method public final B setInputData(androidx.work.Data);
+ }
+
+ public abstract class Worker extends androidx.work.ListenableWorker {
+ ctor @Keep public Worker(android.content.Context, androidx.work.WorkerParameters);
+ method @WorkerThread public abstract androidx.work.ListenableWorker.Result doWork();
+ method public final com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result!> startWork();
+ }
+
+ public abstract class WorkerFactory {
+ ctor public WorkerFactory();
+ method public abstract androidx.work.ListenableWorker? createWorker(android.content.Context, String, androidx.work.WorkerParameters);
+ }
+
+ public final class WorkerParameters {
+ method public java.util.UUID getId();
+ method public androidx.work.Data getInputData();
+ method @RequiresApi(28) public android.net.Network? getNetwork();
+ method @IntRange(from=0) public int getRunAttemptCount();
+ method public java.util.Set<java.lang.String!> getTags();
+ method @RequiresApi(24) public java.util.List<java.lang.String!> getTriggeredContentAuthorities();
+ method @RequiresApi(24) public java.util.List<android.net.Uri!> getTriggeredContentUris();
+ }
+
+}
+
+package androidx.work.multiprocess {
+
+ public abstract class RemoteWorkContinuation {
+ method public static androidx.work.multiprocess.RemoteWorkContinuation combine(java.util.List<androidx.work.multiprocess.RemoteWorkContinuation!>);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enqueue();
+ method public final androidx.work.multiprocess.RemoteWorkContinuation then(androidx.work.OneTimeWorkRequest);
+ method public abstract androidx.work.multiprocess.RemoteWorkContinuation then(java.util.List<androidx.work.OneTimeWorkRequest!>);
+ }
+
+ public abstract class RemoteWorkManager {
+ method public final androidx.work.multiprocess.RemoteWorkContinuation beginUniqueWork(String, androidx.work.ExistingWorkPolicy, androidx.work.OneTimeWorkRequest);
+ method public abstract androidx.work.multiprocess.RemoteWorkContinuation beginUniqueWork(String, androidx.work.ExistingWorkPolicy, java.util.List<androidx.work.OneTimeWorkRequest!>);
+ method public final androidx.work.multiprocess.RemoteWorkContinuation beginWith(androidx.work.OneTimeWorkRequest);
+ method public abstract androidx.work.multiprocess.RemoteWorkContinuation beginWith(java.util.List<androidx.work.OneTimeWorkRequest!>);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> cancelAllWork();
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> cancelAllWorkByTag(String);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> cancelUniqueWork(String);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> cancelWorkById(java.util.UUID);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enqueue(androidx.work.WorkRequest);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enqueue(java.util.List<androidx.work.WorkRequest!>);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enqueueUniquePeriodicWork(String, androidx.work.ExistingPeriodicWorkPolicy, androidx.work.PeriodicWorkRequest);
+ method public final com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enqueueUniqueWork(String, androidx.work.ExistingWorkPolicy, androidx.work.OneTimeWorkRequest);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enqueueUniqueWork(String, androidx.work.ExistingWorkPolicy, java.util.List<androidx.work.OneTimeWorkRequest!>);
+ method public static androidx.work.multiprocess.RemoteWorkManager getInstance(android.content.Context);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.util.List<androidx.work.WorkInfo!>!> getWorkInfos(androidx.work.WorkQuery);
+ }
+
+}
+
diff --git a/work/workmanager/api/public_plus_experimental_2.6.0-beta01.txt b/work/workmanager/api/public_plus_experimental_2.6.0-beta01.txt
new file mode 100644
index 0000000..54713f5
--- /dev/null
+++ b/work/workmanager/api/public_plus_experimental_2.6.0-beta01.txt
@@ -0,0 +1,400 @@
+// Signature format: 4.0
+package androidx.work {
+
+ public final class ArrayCreatingInputMerger extends androidx.work.InputMerger {
+ ctor public ArrayCreatingInputMerger();
+ method public androidx.work.Data merge(java.util.List<androidx.work.Data!>);
+ }
+
+ public enum BackoffPolicy {
+ enum_constant public static final androidx.work.BackoffPolicy EXPONENTIAL;
+ enum_constant public static final androidx.work.BackoffPolicy LINEAR;
+ }
+
+ public final class Configuration {
+ method public String? getDefaultProcessName();
+ method public java.util.concurrent.Executor getExecutor();
+ method public androidx.work.InputMergerFactory getInputMergerFactory();
+ method public int getMaxJobSchedulerId();
+ method public int getMinJobSchedulerId();
+ method public androidx.work.RunnableScheduler getRunnableScheduler();
+ method public java.util.concurrent.Executor getTaskExecutor();
+ method public androidx.work.WorkerFactory getWorkerFactory();
+ field public static final int MIN_SCHEDULER_LIMIT = 20; // 0x14
+ }
+
+ public static final class Configuration.Builder {
+ ctor public Configuration.Builder();
+ method public androidx.work.Configuration build();
+ method public androidx.work.Configuration.Builder setDefaultProcessName(String);
+ method public androidx.work.Configuration.Builder setExecutor(java.util.concurrent.Executor);
+ method public androidx.work.Configuration.Builder setInputMergerFactory(androidx.work.InputMergerFactory);
+ method public androidx.work.Configuration.Builder setJobSchedulerJobIdRange(int, int);
+ method public androidx.work.Configuration.Builder setMaxSchedulerLimit(int);
+ method public androidx.work.Configuration.Builder setMinimumLoggingLevel(int);
+ method public androidx.work.Configuration.Builder setRunnableScheduler(androidx.work.RunnableScheduler);
+ method public androidx.work.Configuration.Builder setTaskExecutor(java.util.concurrent.Executor);
+ method public androidx.work.Configuration.Builder setWorkerFactory(androidx.work.WorkerFactory);
+ }
+
+ public static interface Configuration.Provider {
+ method public androidx.work.Configuration getWorkManagerConfiguration();
+ }
+
+ public final class Constraints {
+ ctor public Constraints(androidx.work.Constraints);
+ method public androidx.work.NetworkType getRequiredNetworkType();
+ method public boolean requiresBatteryNotLow();
+ method public boolean requiresCharging();
+ method @RequiresApi(23) public boolean requiresDeviceIdle();
+ method public boolean requiresStorageNotLow();
+ field public static final androidx.work.Constraints NONE;
+ }
+
+ public static final class Constraints.Builder {
+ ctor public Constraints.Builder();
+ method @RequiresApi(24) public androidx.work.Constraints.Builder addContentUriTrigger(android.net.Uri, boolean);
+ method public androidx.work.Constraints build();
+ method public androidx.work.Constraints.Builder setRequiredNetworkType(androidx.work.NetworkType);
+ method public androidx.work.Constraints.Builder setRequiresBatteryNotLow(boolean);
+ method public androidx.work.Constraints.Builder setRequiresCharging(boolean);
+ method @RequiresApi(23) public androidx.work.Constraints.Builder setRequiresDeviceIdle(boolean);
+ method public androidx.work.Constraints.Builder setRequiresStorageNotLow(boolean);
+ method @RequiresApi(24) public androidx.work.Constraints.Builder setTriggerContentMaxDelay(long, java.util.concurrent.TimeUnit);
+ method @RequiresApi(26) public androidx.work.Constraints.Builder setTriggerContentMaxDelay(java.time.Duration!);
+ method @RequiresApi(24) public androidx.work.Constraints.Builder setTriggerContentUpdateDelay(long, java.util.concurrent.TimeUnit);
+ method @RequiresApi(26) public androidx.work.Constraints.Builder setTriggerContentUpdateDelay(java.time.Duration!);
+ }
+
+ public final class Data {
+ ctor public Data(androidx.work.Data);
+ method @androidx.room.TypeConverter public static androidx.work.Data fromByteArray(byte[]);
+ method public boolean getBoolean(String, boolean);
+ method public boolean[]? getBooleanArray(String);
+ method public byte getByte(String, byte);
+ method public byte[]? getByteArray(String);
+ method public double getDouble(String, double);
+ method public double[]? getDoubleArray(String);
+ method public float getFloat(String, float);
+ method public float[]? getFloatArray(String);
+ method public int getInt(String, int);
+ method public int[]? getIntArray(String);
+ method public java.util.Map<java.lang.String!,java.lang.Object!> getKeyValueMap();
+ method public long getLong(String, long);
+ method public long[]? getLongArray(String);
+ method public String? getString(String);
+ method public String![]? getStringArray(String);
+ method public <T> boolean hasKeyWithValueOfType(String, Class<T!>);
+ method public byte[] toByteArray();
+ field public static final androidx.work.Data EMPTY;
+ field public static final int MAX_DATA_BYTES = 10240; // 0x2800
+ }
+
+ public static final class Data.Builder {
+ ctor public Data.Builder();
+ method public androidx.work.Data build();
+ method public androidx.work.Data.Builder putAll(androidx.work.Data);
+ method public androidx.work.Data.Builder putAll(java.util.Map<java.lang.String!,java.lang.Object!>);
+ method public androidx.work.Data.Builder putBoolean(String, boolean);
+ method public androidx.work.Data.Builder putBooleanArray(String, boolean[]);
+ method public androidx.work.Data.Builder putByte(String, byte);
+ method public androidx.work.Data.Builder putByteArray(String, byte[]);
+ method public androidx.work.Data.Builder putDouble(String, double);
+ method public androidx.work.Data.Builder putDoubleArray(String, double[]);
+ method public androidx.work.Data.Builder putFloat(String, float);
+ method public androidx.work.Data.Builder putFloatArray(String, float[]);
+ method public androidx.work.Data.Builder putInt(String, int);
+ method public androidx.work.Data.Builder putIntArray(String, int[]);
+ method public androidx.work.Data.Builder putLong(String, long);
+ method public androidx.work.Data.Builder putLongArray(String, long[]);
+ method public androidx.work.Data.Builder putString(String, String?);
+ method public androidx.work.Data.Builder putStringArray(String, String![]);
+ }
+
+ public class DelegatingWorkerFactory extends androidx.work.WorkerFactory {
+ ctor public DelegatingWorkerFactory();
+ method public final void addFactory(androidx.work.WorkerFactory);
+ method public final androidx.work.ListenableWorker? createWorker(android.content.Context, String, androidx.work.WorkerParameters);
+ }
+
+ public enum ExistingPeriodicWorkPolicy {
+ enum_constant public static final androidx.work.ExistingPeriodicWorkPolicy KEEP;
+ enum_constant public static final androidx.work.ExistingPeriodicWorkPolicy REPLACE;
+ }
+
+ public enum ExistingWorkPolicy {
+ enum_constant public static final androidx.work.ExistingWorkPolicy APPEND;
+ enum_constant public static final androidx.work.ExistingWorkPolicy APPEND_OR_REPLACE;
+ enum_constant public static final androidx.work.ExistingWorkPolicy KEEP;
+ enum_constant public static final androidx.work.ExistingWorkPolicy REPLACE;
+ }
+
+ public final class ForegroundInfo {
+ ctor public ForegroundInfo(int, android.app.Notification);
+ ctor public ForegroundInfo(int, android.app.Notification, int);
+ method public int getForegroundServiceType();
+ method public android.app.Notification getNotification();
+ method public int getNotificationId();
+ }
+
+ public interface ForegroundUpdater {
+ method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setForegroundAsync(android.content.Context, java.util.UUID, androidx.work.ForegroundInfo);
+ }
+
+ public abstract class InputMerger {
+ ctor public InputMerger();
+ method public abstract androidx.work.Data merge(java.util.List<androidx.work.Data!>);
+ }
+
+ public abstract class InputMergerFactory {
+ ctor public InputMergerFactory();
+ method public abstract androidx.work.InputMerger? createInputMerger(String);
+ }
+
+ public abstract class ListenableWorker {
+ ctor @Keep public ListenableWorker(android.content.Context, androidx.work.WorkerParameters);
+ method public final android.content.Context getApplicationContext();
+ method public final java.util.UUID getId();
+ method public final androidx.work.Data getInputData();
+ method @RequiresApi(28) public final android.net.Network? getNetwork();
+ method @IntRange(from=0) public final int getRunAttemptCount();
+ method public final java.util.Set<java.lang.String!> getTags();
+ method @RequiresApi(24) public final java.util.List<java.lang.String!> getTriggeredContentAuthorities();
+ method @RequiresApi(24) public final java.util.List<android.net.Uri!> getTriggeredContentUris();
+ method public final boolean isStopped();
+ method public void onStopped();
+ method public final com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setForegroundAsync(androidx.work.ForegroundInfo);
+ method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setProgressAsync(androidx.work.Data);
+ method @MainThread public abstract com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result!> startWork();
+ }
+
+ public abstract static class ListenableWorker.Result {
+ method public static androidx.work.ListenableWorker.Result failure();
+ method public static androidx.work.ListenableWorker.Result failure(androidx.work.Data);
+ method public abstract androidx.work.Data getOutputData();
+ method public static androidx.work.ListenableWorker.Result retry();
+ method public static androidx.work.ListenableWorker.Result success();
+ method public static androidx.work.ListenableWorker.Result success(androidx.work.Data);
+ }
+
+ public enum NetworkType {
+ enum_constant public static final androidx.work.NetworkType CONNECTED;
+ enum_constant public static final androidx.work.NetworkType METERED;
+ enum_constant public static final androidx.work.NetworkType NOT_REQUIRED;
+ enum_constant public static final androidx.work.NetworkType NOT_ROAMING;
+ enum_constant @RequiresApi(30) public static final androidx.work.NetworkType TEMPORARILY_UNMETERED;
+ enum_constant public static final androidx.work.NetworkType UNMETERED;
+ }
+
+ public final class OneTimeWorkRequest extends androidx.work.WorkRequest {
+ method public static androidx.work.OneTimeWorkRequest from(Class<? extends androidx.work.ListenableWorker>);
+ method public static java.util.List<androidx.work.OneTimeWorkRequest!> from(java.util.List<java.lang.Class<? extends androidx.work.ListenableWorker>!>);
+ }
+
+ public static final class OneTimeWorkRequest.Builder extends androidx.work.WorkRequest.Builder<androidx.work.OneTimeWorkRequest.Builder,androidx.work.OneTimeWorkRequest> {
+ ctor public OneTimeWorkRequest.Builder(Class<? extends androidx.work.ListenableWorker>);
+ method public androidx.work.OneTimeWorkRequest.Builder setInputMerger(Class<? extends androidx.work.InputMerger>);
+ }
+
+ public interface Operation {
+ method public com.google.common.util.concurrent.ListenableFuture<androidx.work.Operation.State.SUCCESS!> getResult();
+ method public androidx.lifecycle.LiveData<androidx.work.Operation.State!> getState();
+ }
+
+ public abstract static class Operation.State {
+ }
+
+ public static final class Operation.State.FAILURE extends androidx.work.Operation.State {
+ ctor public Operation.State.FAILURE(Throwable);
+ method public Throwable getThrowable();
+ }
+
+ public static final class Operation.State.IN_PROGRESS extends androidx.work.Operation.State {
+ }
+
+ public static final class Operation.State.SUCCESS extends androidx.work.Operation.State {
+ }
+
+ public final class OverwritingInputMerger extends androidx.work.InputMerger {
+ ctor public OverwritingInputMerger();
+ method public androidx.work.Data merge(java.util.List<androidx.work.Data!>);
+ }
+
+ public final class PeriodicWorkRequest extends androidx.work.WorkRequest {
+ field public static final long MIN_PERIODIC_FLEX_MILLIS = 300000L; // 0x493e0L
+ field public static final long MIN_PERIODIC_INTERVAL_MILLIS = 900000L; // 0xdbba0L
+ }
+
+ public static final class PeriodicWorkRequest.Builder extends androidx.work.WorkRequest.Builder<androidx.work.PeriodicWorkRequest.Builder,androidx.work.PeriodicWorkRequest> {
+ ctor public PeriodicWorkRequest.Builder(Class<? extends androidx.work.ListenableWorker>, long, java.util.concurrent.TimeUnit);
+ ctor @RequiresApi(26) public PeriodicWorkRequest.Builder(Class<? extends androidx.work.ListenableWorker>, java.time.Duration);
+ ctor public PeriodicWorkRequest.Builder(Class<? extends androidx.work.ListenableWorker>, long, java.util.concurrent.TimeUnit, long, java.util.concurrent.TimeUnit);
+ ctor @RequiresApi(26) public PeriodicWorkRequest.Builder(Class<? extends androidx.work.ListenableWorker>, java.time.Duration, java.time.Duration);
+ }
+
+ public interface ProgressUpdater {
+ method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> updateProgress(android.content.Context, java.util.UUID, androidx.work.Data);
+ }
+
+ public interface RunnableScheduler {
+ method public void cancel(Runnable);
+ method public void scheduleWithDelay(@IntRange(from=0) long, Runnable);
+ }
+
+ public abstract class WorkContinuation {
+ ctor public WorkContinuation();
+ method public static androidx.work.WorkContinuation combine(java.util.List<androidx.work.WorkContinuation!>);
+ method public abstract androidx.work.Operation enqueue();
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.util.List<androidx.work.WorkInfo!>!> getWorkInfos();
+ method public abstract androidx.lifecycle.LiveData<java.util.List<androidx.work.WorkInfo!>!> getWorkInfosLiveData();
+ method public final androidx.work.WorkContinuation then(androidx.work.OneTimeWorkRequest);
+ method public abstract androidx.work.WorkContinuation then(java.util.List<androidx.work.OneTimeWorkRequest!>);
+ }
+
+ public final class WorkInfo {
+ method public java.util.UUID getId();
+ method public androidx.work.Data getOutputData();
+ method public androidx.work.Data getProgress();
+ method @IntRange(from=0) public int getRunAttemptCount();
+ method public androidx.work.WorkInfo.State getState();
+ method public java.util.Set<java.lang.String!> getTags();
+ }
+
+ public enum WorkInfo.State {
+ method public boolean isFinished();
+ enum_constant public static final androidx.work.WorkInfo.State BLOCKED;
+ enum_constant public static final androidx.work.WorkInfo.State CANCELLED;
+ enum_constant public static final androidx.work.WorkInfo.State ENQUEUED;
+ enum_constant public static final androidx.work.WorkInfo.State FAILED;
+ enum_constant public static final androidx.work.WorkInfo.State RUNNING;
+ enum_constant public static final androidx.work.WorkInfo.State SUCCEEDED;
+ }
+
+ public abstract class WorkManager {
+ method public final androidx.work.WorkContinuation beginUniqueWork(String, androidx.work.ExistingWorkPolicy, androidx.work.OneTimeWorkRequest);
+ method public abstract androidx.work.WorkContinuation beginUniqueWork(String, androidx.work.ExistingWorkPolicy, java.util.List<androidx.work.OneTimeWorkRequest!>);
+ method public final androidx.work.WorkContinuation beginWith(androidx.work.OneTimeWorkRequest);
+ method public abstract androidx.work.WorkContinuation beginWith(java.util.List<androidx.work.OneTimeWorkRequest!>);
+ method public abstract androidx.work.Operation cancelAllWork();
+ method public abstract androidx.work.Operation cancelAllWorkByTag(String);
+ method public abstract androidx.work.Operation cancelUniqueWork(String);
+ method public abstract androidx.work.Operation cancelWorkById(java.util.UUID);
+ method public abstract android.app.PendingIntent createCancelPendingIntent(java.util.UUID);
+ method public final androidx.work.Operation enqueue(androidx.work.WorkRequest);
+ method public abstract androidx.work.Operation enqueue(java.util.List<? extends androidx.work.WorkRequest>);
+ method public abstract androidx.work.Operation enqueueUniquePeriodicWork(String, androidx.work.ExistingPeriodicWorkPolicy, androidx.work.PeriodicWorkRequest);
+ method public androidx.work.Operation enqueueUniqueWork(String, androidx.work.ExistingWorkPolicy, androidx.work.OneTimeWorkRequest);
+ method public abstract androidx.work.Operation enqueueUniqueWork(String, androidx.work.ExistingWorkPolicy, java.util.List<androidx.work.OneTimeWorkRequest!>);
+ method @Deprecated public static androidx.work.WorkManager getInstance();
+ method public static androidx.work.WorkManager getInstance(android.content.Context);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Long!> getLastCancelAllTimeMillis();
+ method public abstract androidx.lifecycle.LiveData<java.lang.Long!> getLastCancelAllTimeMillisLiveData();
+ method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.work.WorkInfo!> getWorkInfoById(java.util.UUID);
+ method public abstract androidx.lifecycle.LiveData<androidx.work.WorkInfo!> getWorkInfoByIdLiveData(java.util.UUID);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.util.List<androidx.work.WorkInfo!>!> getWorkInfos(androidx.work.WorkQuery);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.util.List<androidx.work.WorkInfo!>!> getWorkInfosByTag(String);
+ method public abstract androidx.lifecycle.LiveData<java.util.List<androidx.work.WorkInfo!>!> getWorkInfosByTagLiveData(String);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.util.List<androidx.work.WorkInfo!>!> getWorkInfosForUniqueWork(String);
+ method public abstract androidx.lifecycle.LiveData<java.util.List<androidx.work.WorkInfo!>!> getWorkInfosForUniqueWorkLiveData(String);
+ method public abstract androidx.lifecycle.LiveData<java.util.List<androidx.work.WorkInfo!>!> getWorkInfosLiveData(androidx.work.WorkQuery);
+ method public static void initialize(android.content.Context, androidx.work.Configuration);
+ method public abstract androidx.work.Operation pruneWork();
+ }
+
+ public final class WorkManagerInitializer implements androidx.startup.Initializer<androidx.work.WorkManager> {
+ ctor public WorkManagerInitializer();
+ method public androidx.work.WorkManager create(android.content.Context);
+ method public java.util.List<java.lang.Class<? extends androidx.startup.Initializer<?>>!> dependencies();
+ }
+
+ public final class WorkQuery {
+ method public java.util.List<java.util.UUID!> getIds();
+ method public java.util.List<androidx.work.WorkInfo.State!> getStates();
+ method public java.util.List<java.lang.String!> getTags();
+ method public java.util.List<java.lang.String!> getUniqueWorkNames();
+ }
+
+ public static final class WorkQuery.Builder {
+ method public androidx.work.WorkQuery.Builder addIds(java.util.List<java.util.UUID!>);
+ method public androidx.work.WorkQuery.Builder addStates(java.util.List<androidx.work.WorkInfo.State!>);
+ method public androidx.work.WorkQuery.Builder addTags(java.util.List<java.lang.String!>);
+ method public androidx.work.WorkQuery.Builder addUniqueWorkNames(java.util.List<java.lang.String!>);
+ method public androidx.work.WorkQuery build();
+ method public static androidx.work.WorkQuery.Builder fromIds(java.util.List<java.util.UUID!>);
+ method public static androidx.work.WorkQuery.Builder fromStates(java.util.List<androidx.work.WorkInfo.State!>);
+ method public static androidx.work.WorkQuery.Builder fromTags(java.util.List<java.lang.String!>);
+ method public static androidx.work.WorkQuery.Builder fromUniqueWorkNames(java.util.List<java.lang.String!>);
+ }
+
+ public abstract class WorkRequest {
+ method public java.util.UUID getId();
+ field public static final long DEFAULT_BACKOFF_DELAY_MILLIS = 30000L; // 0x7530L
+ field public static final long MAX_BACKOFF_MILLIS = 18000000L; // 0x112a880L
+ field public static final long MIN_BACKOFF_MILLIS = 10000L; // 0x2710L
+ }
+
+ public abstract static class WorkRequest.Builder<B extends androidx.work.WorkRequest.Builder<?, ?>, W extends androidx.work.WorkRequest> {
+ method public final B addTag(String);
+ method public final W build();
+ method public final B keepResultsForAtLeast(long, java.util.concurrent.TimeUnit);
+ method @RequiresApi(26) public final B keepResultsForAtLeast(java.time.Duration);
+ method public final B setBackoffCriteria(androidx.work.BackoffPolicy, long, java.util.concurrent.TimeUnit);
+ method @RequiresApi(26) public final B setBackoffCriteria(androidx.work.BackoffPolicy, java.time.Duration);
+ method public final B setConstraints(androidx.work.Constraints);
+ method public B setInitialDelay(long, java.util.concurrent.TimeUnit);
+ method @RequiresApi(26) public B setInitialDelay(java.time.Duration);
+ method public final B setInputData(androidx.work.Data);
+ }
+
+ public abstract class Worker extends androidx.work.ListenableWorker {
+ ctor @Keep public Worker(android.content.Context, androidx.work.WorkerParameters);
+ method @WorkerThread public abstract androidx.work.ListenableWorker.Result doWork();
+ method public final com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result!> startWork();
+ }
+
+ public abstract class WorkerFactory {
+ ctor public WorkerFactory();
+ method public abstract androidx.work.ListenableWorker? createWorker(android.content.Context, String, androidx.work.WorkerParameters);
+ }
+
+ public final class WorkerParameters {
+ method public java.util.UUID getId();
+ method public androidx.work.Data getInputData();
+ method @RequiresApi(28) public android.net.Network? getNetwork();
+ method @IntRange(from=0) public int getRunAttemptCount();
+ method public java.util.Set<java.lang.String!> getTags();
+ method @RequiresApi(24) public java.util.List<java.lang.String!> getTriggeredContentAuthorities();
+ method @RequiresApi(24) public java.util.List<android.net.Uri!> getTriggeredContentUris();
+ }
+
+}
+
+package androidx.work.multiprocess {
+
+ public abstract class RemoteWorkContinuation {
+ method public static androidx.work.multiprocess.RemoteWorkContinuation combine(java.util.List<androidx.work.multiprocess.RemoteWorkContinuation!>);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enqueue();
+ method public final androidx.work.multiprocess.RemoteWorkContinuation then(androidx.work.OneTimeWorkRequest);
+ method public abstract androidx.work.multiprocess.RemoteWorkContinuation then(java.util.List<androidx.work.OneTimeWorkRequest!>);
+ }
+
+ public abstract class RemoteWorkManager {
+ method public final androidx.work.multiprocess.RemoteWorkContinuation beginUniqueWork(String, androidx.work.ExistingWorkPolicy, androidx.work.OneTimeWorkRequest);
+ method public abstract androidx.work.multiprocess.RemoteWorkContinuation beginUniqueWork(String, androidx.work.ExistingWorkPolicy, java.util.List<androidx.work.OneTimeWorkRequest!>);
+ method public final androidx.work.multiprocess.RemoteWorkContinuation beginWith(androidx.work.OneTimeWorkRequest);
+ method public abstract androidx.work.multiprocess.RemoteWorkContinuation beginWith(java.util.List<androidx.work.OneTimeWorkRequest!>);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> cancelAllWork();
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> cancelAllWorkByTag(String);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> cancelUniqueWork(String);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> cancelWorkById(java.util.UUID);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enqueue(androidx.work.WorkRequest);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enqueue(java.util.List<androidx.work.WorkRequest!>);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enqueueUniquePeriodicWork(String, androidx.work.ExistingPeriodicWorkPolicy, androidx.work.PeriodicWorkRequest);
+ method public final com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enqueueUniqueWork(String, androidx.work.ExistingWorkPolicy, androidx.work.OneTimeWorkRequest);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enqueueUniqueWork(String, androidx.work.ExistingWorkPolicy, java.util.List<androidx.work.OneTimeWorkRequest!>);
+ method public static androidx.work.multiprocess.RemoteWorkManager getInstance(android.content.Context);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.util.List<androidx.work.WorkInfo!>!> getWorkInfos(androidx.work.WorkQuery);
+ }
+
+}
+
diff --git a/work/workmanager/api/res-2.6.0-beta01.txt b/work/workmanager/api/res-2.6.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/work/workmanager/api/res-2.6.0-beta01.txt
diff --git a/work/workmanager/api/restricted_2.6.0-beta01.txt b/work/workmanager/api/restricted_2.6.0-beta01.txt
new file mode 100644
index 0000000..54713f5
--- /dev/null
+++ b/work/workmanager/api/restricted_2.6.0-beta01.txt
@@ -0,0 +1,400 @@
+// Signature format: 4.0
+package androidx.work {
+
+ public final class ArrayCreatingInputMerger extends androidx.work.InputMerger {
+ ctor public ArrayCreatingInputMerger();
+ method public androidx.work.Data merge(java.util.List<androidx.work.Data!>);
+ }
+
+ public enum BackoffPolicy {
+ enum_constant public static final androidx.work.BackoffPolicy EXPONENTIAL;
+ enum_constant public static final androidx.work.BackoffPolicy LINEAR;
+ }
+
+ public final class Configuration {
+ method public String? getDefaultProcessName();
+ method public java.util.concurrent.Executor getExecutor();
+ method public androidx.work.InputMergerFactory getInputMergerFactory();
+ method public int getMaxJobSchedulerId();
+ method public int getMinJobSchedulerId();
+ method public androidx.work.RunnableScheduler getRunnableScheduler();
+ method public java.util.concurrent.Executor getTaskExecutor();
+ method public androidx.work.WorkerFactory getWorkerFactory();
+ field public static final int MIN_SCHEDULER_LIMIT = 20; // 0x14
+ }
+
+ public static final class Configuration.Builder {
+ ctor public Configuration.Builder();
+ method public androidx.work.Configuration build();
+ method public androidx.work.Configuration.Builder setDefaultProcessName(String);
+ method public androidx.work.Configuration.Builder setExecutor(java.util.concurrent.Executor);
+ method public androidx.work.Configuration.Builder setInputMergerFactory(androidx.work.InputMergerFactory);
+ method public androidx.work.Configuration.Builder setJobSchedulerJobIdRange(int, int);
+ method public androidx.work.Configuration.Builder setMaxSchedulerLimit(int);
+ method public androidx.work.Configuration.Builder setMinimumLoggingLevel(int);
+ method public androidx.work.Configuration.Builder setRunnableScheduler(androidx.work.RunnableScheduler);
+ method public androidx.work.Configuration.Builder setTaskExecutor(java.util.concurrent.Executor);
+ method public androidx.work.Configuration.Builder setWorkerFactory(androidx.work.WorkerFactory);
+ }
+
+ public static interface Configuration.Provider {
+ method public androidx.work.Configuration getWorkManagerConfiguration();
+ }
+
+ public final class Constraints {
+ ctor public Constraints(androidx.work.Constraints);
+ method public androidx.work.NetworkType getRequiredNetworkType();
+ method public boolean requiresBatteryNotLow();
+ method public boolean requiresCharging();
+ method @RequiresApi(23) public boolean requiresDeviceIdle();
+ method public boolean requiresStorageNotLow();
+ field public static final androidx.work.Constraints NONE;
+ }
+
+ public static final class Constraints.Builder {
+ ctor public Constraints.Builder();
+ method @RequiresApi(24) public androidx.work.Constraints.Builder addContentUriTrigger(android.net.Uri, boolean);
+ method public androidx.work.Constraints build();
+ method public androidx.work.Constraints.Builder setRequiredNetworkType(androidx.work.NetworkType);
+ method public androidx.work.Constraints.Builder setRequiresBatteryNotLow(boolean);
+ method public androidx.work.Constraints.Builder setRequiresCharging(boolean);
+ method @RequiresApi(23) public androidx.work.Constraints.Builder setRequiresDeviceIdle(boolean);
+ method public androidx.work.Constraints.Builder setRequiresStorageNotLow(boolean);
+ method @RequiresApi(24) public androidx.work.Constraints.Builder setTriggerContentMaxDelay(long, java.util.concurrent.TimeUnit);
+ method @RequiresApi(26) public androidx.work.Constraints.Builder setTriggerContentMaxDelay(java.time.Duration!);
+ method @RequiresApi(24) public androidx.work.Constraints.Builder setTriggerContentUpdateDelay(long, java.util.concurrent.TimeUnit);
+ method @RequiresApi(26) public androidx.work.Constraints.Builder setTriggerContentUpdateDelay(java.time.Duration!);
+ }
+
+ public final class Data {
+ ctor public Data(androidx.work.Data);
+ method @androidx.room.TypeConverter public static androidx.work.Data fromByteArray(byte[]);
+ method public boolean getBoolean(String, boolean);
+ method public boolean[]? getBooleanArray(String);
+ method public byte getByte(String, byte);
+ method public byte[]? getByteArray(String);
+ method public double getDouble(String, double);
+ method public double[]? getDoubleArray(String);
+ method public float getFloat(String, float);
+ method public float[]? getFloatArray(String);
+ method public int getInt(String, int);
+ method public int[]? getIntArray(String);
+ method public java.util.Map<java.lang.String!,java.lang.Object!> getKeyValueMap();
+ method public long getLong(String, long);
+ method public long[]? getLongArray(String);
+ method public String? getString(String);
+ method public String![]? getStringArray(String);
+ method public <T> boolean hasKeyWithValueOfType(String, Class<T!>);
+ method public byte[] toByteArray();
+ field public static final androidx.work.Data EMPTY;
+ field public static final int MAX_DATA_BYTES = 10240; // 0x2800
+ }
+
+ public static final class Data.Builder {
+ ctor public Data.Builder();
+ method public androidx.work.Data build();
+ method public androidx.work.Data.Builder putAll(androidx.work.Data);
+ method public androidx.work.Data.Builder putAll(java.util.Map<java.lang.String!,java.lang.Object!>);
+ method public androidx.work.Data.Builder putBoolean(String, boolean);
+ method public androidx.work.Data.Builder putBooleanArray(String, boolean[]);
+ method public androidx.work.Data.Builder putByte(String, byte);
+ method public androidx.work.Data.Builder putByteArray(String, byte[]);
+ method public androidx.work.Data.Builder putDouble(String, double);
+ method public androidx.work.Data.Builder putDoubleArray(String, double[]);
+ method public androidx.work.Data.Builder putFloat(String, float);
+ method public androidx.work.Data.Builder putFloatArray(String, float[]);
+ method public androidx.work.Data.Builder putInt(String, int);
+ method public androidx.work.Data.Builder putIntArray(String, int[]);
+ method public androidx.work.Data.Builder putLong(String, long);
+ method public androidx.work.Data.Builder putLongArray(String, long[]);
+ method public androidx.work.Data.Builder putString(String, String?);
+ method public androidx.work.Data.Builder putStringArray(String, String![]);
+ }
+
+ public class DelegatingWorkerFactory extends androidx.work.WorkerFactory {
+ ctor public DelegatingWorkerFactory();
+ method public final void addFactory(androidx.work.WorkerFactory);
+ method public final androidx.work.ListenableWorker? createWorker(android.content.Context, String, androidx.work.WorkerParameters);
+ }
+
+ public enum ExistingPeriodicWorkPolicy {
+ enum_constant public static final androidx.work.ExistingPeriodicWorkPolicy KEEP;
+ enum_constant public static final androidx.work.ExistingPeriodicWorkPolicy REPLACE;
+ }
+
+ public enum ExistingWorkPolicy {
+ enum_constant public static final androidx.work.ExistingWorkPolicy APPEND;
+ enum_constant public static final androidx.work.ExistingWorkPolicy APPEND_OR_REPLACE;
+ enum_constant public static final androidx.work.ExistingWorkPolicy KEEP;
+ enum_constant public static final androidx.work.ExistingWorkPolicy REPLACE;
+ }
+
+ public final class ForegroundInfo {
+ ctor public ForegroundInfo(int, android.app.Notification);
+ ctor public ForegroundInfo(int, android.app.Notification, int);
+ method public int getForegroundServiceType();
+ method public android.app.Notification getNotification();
+ method public int getNotificationId();
+ }
+
+ public interface ForegroundUpdater {
+ method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setForegroundAsync(android.content.Context, java.util.UUID, androidx.work.ForegroundInfo);
+ }
+
+ public abstract class InputMerger {
+ ctor public InputMerger();
+ method public abstract androidx.work.Data merge(java.util.List<androidx.work.Data!>);
+ }
+
+ public abstract class InputMergerFactory {
+ ctor public InputMergerFactory();
+ method public abstract androidx.work.InputMerger? createInputMerger(String);
+ }
+
+ public abstract class ListenableWorker {
+ ctor @Keep public ListenableWorker(android.content.Context, androidx.work.WorkerParameters);
+ method public final android.content.Context getApplicationContext();
+ method public final java.util.UUID getId();
+ method public final androidx.work.Data getInputData();
+ method @RequiresApi(28) public final android.net.Network? getNetwork();
+ method @IntRange(from=0) public final int getRunAttemptCount();
+ method public final java.util.Set<java.lang.String!> getTags();
+ method @RequiresApi(24) public final java.util.List<java.lang.String!> getTriggeredContentAuthorities();
+ method @RequiresApi(24) public final java.util.List<android.net.Uri!> getTriggeredContentUris();
+ method public final boolean isStopped();
+ method public void onStopped();
+ method public final com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setForegroundAsync(androidx.work.ForegroundInfo);
+ method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> setProgressAsync(androidx.work.Data);
+ method @MainThread public abstract com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result!> startWork();
+ }
+
+ public abstract static class ListenableWorker.Result {
+ method public static androidx.work.ListenableWorker.Result failure();
+ method public static androidx.work.ListenableWorker.Result failure(androidx.work.Data);
+ method public abstract androidx.work.Data getOutputData();
+ method public static androidx.work.ListenableWorker.Result retry();
+ method public static androidx.work.ListenableWorker.Result success();
+ method public static androidx.work.ListenableWorker.Result success(androidx.work.Data);
+ }
+
+ public enum NetworkType {
+ enum_constant public static final androidx.work.NetworkType CONNECTED;
+ enum_constant public static final androidx.work.NetworkType METERED;
+ enum_constant public static final androidx.work.NetworkType NOT_REQUIRED;
+ enum_constant public static final androidx.work.NetworkType NOT_ROAMING;
+ enum_constant @RequiresApi(30) public static final androidx.work.NetworkType TEMPORARILY_UNMETERED;
+ enum_constant public static final androidx.work.NetworkType UNMETERED;
+ }
+
+ public final class OneTimeWorkRequest extends androidx.work.WorkRequest {
+ method public static androidx.work.OneTimeWorkRequest from(Class<? extends androidx.work.ListenableWorker>);
+ method public static java.util.List<androidx.work.OneTimeWorkRequest!> from(java.util.List<java.lang.Class<? extends androidx.work.ListenableWorker>!>);
+ }
+
+ public static final class OneTimeWorkRequest.Builder extends androidx.work.WorkRequest.Builder<androidx.work.OneTimeWorkRequest.Builder,androidx.work.OneTimeWorkRequest> {
+ ctor public OneTimeWorkRequest.Builder(Class<? extends androidx.work.ListenableWorker>);
+ method public androidx.work.OneTimeWorkRequest.Builder setInputMerger(Class<? extends androidx.work.InputMerger>);
+ }
+
+ public interface Operation {
+ method public com.google.common.util.concurrent.ListenableFuture<androidx.work.Operation.State.SUCCESS!> getResult();
+ method public androidx.lifecycle.LiveData<androidx.work.Operation.State!> getState();
+ }
+
+ public abstract static class Operation.State {
+ }
+
+ public static final class Operation.State.FAILURE extends androidx.work.Operation.State {
+ ctor public Operation.State.FAILURE(Throwable);
+ method public Throwable getThrowable();
+ }
+
+ public static final class Operation.State.IN_PROGRESS extends androidx.work.Operation.State {
+ }
+
+ public static final class Operation.State.SUCCESS extends androidx.work.Operation.State {
+ }
+
+ public final class OverwritingInputMerger extends androidx.work.InputMerger {
+ ctor public OverwritingInputMerger();
+ method public androidx.work.Data merge(java.util.List<androidx.work.Data!>);
+ }
+
+ public final class PeriodicWorkRequest extends androidx.work.WorkRequest {
+ field public static final long MIN_PERIODIC_FLEX_MILLIS = 300000L; // 0x493e0L
+ field public static final long MIN_PERIODIC_INTERVAL_MILLIS = 900000L; // 0xdbba0L
+ }
+
+ public static final class PeriodicWorkRequest.Builder extends androidx.work.WorkRequest.Builder<androidx.work.PeriodicWorkRequest.Builder,androidx.work.PeriodicWorkRequest> {
+ ctor public PeriodicWorkRequest.Builder(Class<? extends androidx.work.ListenableWorker>, long, java.util.concurrent.TimeUnit);
+ ctor @RequiresApi(26) public PeriodicWorkRequest.Builder(Class<? extends androidx.work.ListenableWorker>, java.time.Duration);
+ ctor public PeriodicWorkRequest.Builder(Class<? extends androidx.work.ListenableWorker>, long, java.util.concurrent.TimeUnit, long, java.util.concurrent.TimeUnit);
+ ctor @RequiresApi(26) public PeriodicWorkRequest.Builder(Class<? extends androidx.work.ListenableWorker>, java.time.Duration, java.time.Duration);
+ }
+
+ public interface ProgressUpdater {
+ method public com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> updateProgress(android.content.Context, java.util.UUID, androidx.work.Data);
+ }
+
+ public interface RunnableScheduler {
+ method public void cancel(Runnable);
+ method public void scheduleWithDelay(@IntRange(from=0) long, Runnable);
+ }
+
+ public abstract class WorkContinuation {
+ ctor public WorkContinuation();
+ method public static androidx.work.WorkContinuation combine(java.util.List<androidx.work.WorkContinuation!>);
+ method public abstract androidx.work.Operation enqueue();
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.util.List<androidx.work.WorkInfo!>!> getWorkInfos();
+ method public abstract androidx.lifecycle.LiveData<java.util.List<androidx.work.WorkInfo!>!> getWorkInfosLiveData();
+ method public final androidx.work.WorkContinuation then(androidx.work.OneTimeWorkRequest);
+ method public abstract androidx.work.WorkContinuation then(java.util.List<androidx.work.OneTimeWorkRequest!>);
+ }
+
+ public final class WorkInfo {
+ method public java.util.UUID getId();
+ method public androidx.work.Data getOutputData();
+ method public androidx.work.Data getProgress();
+ method @IntRange(from=0) public int getRunAttemptCount();
+ method public androidx.work.WorkInfo.State getState();
+ method public java.util.Set<java.lang.String!> getTags();
+ }
+
+ public enum WorkInfo.State {
+ method public boolean isFinished();
+ enum_constant public static final androidx.work.WorkInfo.State BLOCKED;
+ enum_constant public static final androidx.work.WorkInfo.State CANCELLED;
+ enum_constant public static final androidx.work.WorkInfo.State ENQUEUED;
+ enum_constant public static final androidx.work.WorkInfo.State FAILED;
+ enum_constant public static final androidx.work.WorkInfo.State RUNNING;
+ enum_constant public static final androidx.work.WorkInfo.State SUCCEEDED;
+ }
+
+ public abstract class WorkManager {
+ method public final androidx.work.WorkContinuation beginUniqueWork(String, androidx.work.ExistingWorkPolicy, androidx.work.OneTimeWorkRequest);
+ method public abstract androidx.work.WorkContinuation beginUniqueWork(String, androidx.work.ExistingWorkPolicy, java.util.List<androidx.work.OneTimeWorkRequest!>);
+ method public final androidx.work.WorkContinuation beginWith(androidx.work.OneTimeWorkRequest);
+ method public abstract androidx.work.WorkContinuation beginWith(java.util.List<androidx.work.OneTimeWorkRequest!>);
+ method public abstract androidx.work.Operation cancelAllWork();
+ method public abstract androidx.work.Operation cancelAllWorkByTag(String);
+ method public abstract androidx.work.Operation cancelUniqueWork(String);
+ method public abstract androidx.work.Operation cancelWorkById(java.util.UUID);
+ method public abstract android.app.PendingIntent createCancelPendingIntent(java.util.UUID);
+ method public final androidx.work.Operation enqueue(androidx.work.WorkRequest);
+ method public abstract androidx.work.Operation enqueue(java.util.List<? extends androidx.work.WorkRequest>);
+ method public abstract androidx.work.Operation enqueueUniquePeriodicWork(String, androidx.work.ExistingPeriodicWorkPolicy, androidx.work.PeriodicWorkRequest);
+ method public androidx.work.Operation enqueueUniqueWork(String, androidx.work.ExistingWorkPolicy, androidx.work.OneTimeWorkRequest);
+ method public abstract androidx.work.Operation enqueueUniqueWork(String, androidx.work.ExistingWorkPolicy, java.util.List<androidx.work.OneTimeWorkRequest!>);
+ method @Deprecated public static androidx.work.WorkManager getInstance();
+ method public static androidx.work.WorkManager getInstance(android.content.Context);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Long!> getLastCancelAllTimeMillis();
+ method public abstract androidx.lifecycle.LiveData<java.lang.Long!> getLastCancelAllTimeMillisLiveData();
+ method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.work.WorkInfo!> getWorkInfoById(java.util.UUID);
+ method public abstract androidx.lifecycle.LiveData<androidx.work.WorkInfo!> getWorkInfoByIdLiveData(java.util.UUID);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.util.List<androidx.work.WorkInfo!>!> getWorkInfos(androidx.work.WorkQuery);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.util.List<androidx.work.WorkInfo!>!> getWorkInfosByTag(String);
+ method public abstract androidx.lifecycle.LiveData<java.util.List<androidx.work.WorkInfo!>!> getWorkInfosByTagLiveData(String);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.util.List<androidx.work.WorkInfo!>!> getWorkInfosForUniqueWork(String);
+ method public abstract androidx.lifecycle.LiveData<java.util.List<androidx.work.WorkInfo!>!> getWorkInfosForUniqueWorkLiveData(String);
+ method public abstract androidx.lifecycle.LiveData<java.util.List<androidx.work.WorkInfo!>!> getWorkInfosLiveData(androidx.work.WorkQuery);
+ method public static void initialize(android.content.Context, androidx.work.Configuration);
+ method public abstract androidx.work.Operation pruneWork();
+ }
+
+ public final class WorkManagerInitializer implements androidx.startup.Initializer<androidx.work.WorkManager> {
+ ctor public WorkManagerInitializer();
+ method public androidx.work.WorkManager create(android.content.Context);
+ method public java.util.List<java.lang.Class<? extends androidx.startup.Initializer<?>>!> dependencies();
+ }
+
+ public final class WorkQuery {
+ method public java.util.List<java.util.UUID!> getIds();
+ method public java.util.List<androidx.work.WorkInfo.State!> getStates();
+ method public java.util.List<java.lang.String!> getTags();
+ method public java.util.List<java.lang.String!> getUniqueWorkNames();
+ }
+
+ public static final class WorkQuery.Builder {
+ method public androidx.work.WorkQuery.Builder addIds(java.util.List<java.util.UUID!>);
+ method public androidx.work.WorkQuery.Builder addStates(java.util.List<androidx.work.WorkInfo.State!>);
+ method public androidx.work.WorkQuery.Builder addTags(java.util.List<java.lang.String!>);
+ method public androidx.work.WorkQuery.Builder addUniqueWorkNames(java.util.List<java.lang.String!>);
+ method public androidx.work.WorkQuery build();
+ method public static androidx.work.WorkQuery.Builder fromIds(java.util.List<java.util.UUID!>);
+ method public static androidx.work.WorkQuery.Builder fromStates(java.util.List<androidx.work.WorkInfo.State!>);
+ method public static androidx.work.WorkQuery.Builder fromTags(java.util.List<java.lang.String!>);
+ method public static androidx.work.WorkQuery.Builder fromUniqueWorkNames(java.util.List<java.lang.String!>);
+ }
+
+ public abstract class WorkRequest {
+ method public java.util.UUID getId();
+ field public static final long DEFAULT_BACKOFF_DELAY_MILLIS = 30000L; // 0x7530L
+ field public static final long MAX_BACKOFF_MILLIS = 18000000L; // 0x112a880L
+ field public static final long MIN_BACKOFF_MILLIS = 10000L; // 0x2710L
+ }
+
+ public abstract static class WorkRequest.Builder<B extends androidx.work.WorkRequest.Builder<?, ?>, W extends androidx.work.WorkRequest> {
+ method public final B addTag(String);
+ method public final W build();
+ method public final B keepResultsForAtLeast(long, java.util.concurrent.TimeUnit);
+ method @RequiresApi(26) public final B keepResultsForAtLeast(java.time.Duration);
+ method public final B setBackoffCriteria(androidx.work.BackoffPolicy, long, java.util.concurrent.TimeUnit);
+ method @RequiresApi(26) public final B setBackoffCriteria(androidx.work.BackoffPolicy, java.time.Duration);
+ method public final B setConstraints(androidx.work.Constraints);
+ method public B setInitialDelay(long, java.util.concurrent.TimeUnit);
+ method @RequiresApi(26) public B setInitialDelay(java.time.Duration);
+ method public final B setInputData(androidx.work.Data);
+ }
+
+ public abstract class Worker extends androidx.work.ListenableWorker {
+ ctor @Keep public Worker(android.content.Context, androidx.work.WorkerParameters);
+ method @WorkerThread public abstract androidx.work.ListenableWorker.Result doWork();
+ method public final com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result!> startWork();
+ }
+
+ public abstract class WorkerFactory {
+ ctor public WorkerFactory();
+ method public abstract androidx.work.ListenableWorker? createWorker(android.content.Context, String, androidx.work.WorkerParameters);
+ }
+
+ public final class WorkerParameters {
+ method public java.util.UUID getId();
+ method public androidx.work.Data getInputData();
+ method @RequiresApi(28) public android.net.Network? getNetwork();
+ method @IntRange(from=0) public int getRunAttemptCount();
+ method public java.util.Set<java.lang.String!> getTags();
+ method @RequiresApi(24) public java.util.List<java.lang.String!> getTriggeredContentAuthorities();
+ method @RequiresApi(24) public java.util.List<android.net.Uri!> getTriggeredContentUris();
+ }
+
+}
+
+package androidx.work.multiprocess {
+
+ public abstract class RemoteWorkContinuation {
+ method public static androidx.work.multiprocess.RemoteWorkContinuation combine(java.util.List<androidx.work.multiprocess.RemoteWorkContinuation!>);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enqueue();
+ method public final androidx.work.multiprocess.RemoteWorkContinuation then(androidx.work.OneTimeWorkRequest);
+ method public abstract androidx.work.multiprocess.RemoteWorkContinuation then(java.util.List<androidx.work.OneTimeWorkRequest!>);
+ }
+
+ public abstract class RemoteWorkManager {
+ method public final androidx.work.multiprocess.RemoteWorkContinuation beginUniqueWork(String, androidx.work.ExistingWorkPolicy, androidx.work.OneTimeWorkRequest);
+ method public abstract androidx.work.multiprocess.RemoteWorkContinuation beginUniqueWork(String, androidx.work.ExistingWorkPolicy, java.util.List<androidx.work.OneTimeWorkRequest!>);
+ method public final androidx.work.multiprocess.RemoteWorkContinuation beginWith(androidx.work.OneTimeWorkRequest);
+ method public abstract androidx.work.multiprocess.RemoteWorkContinuation beginWith(java.util.List<androidx.work.OneTimeWorkRequest!>);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> cancelAllWork();
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> cancelAllWorkByTag(String);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> cancelUniqueWork(String);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> cancelWorkById(java.util.UUID);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enqueue(androidx.work.WorkRequest);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enqueue(java.util.List<androidx.work.WorkRequest!>);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enqueueUniquePeriodicWork(String, androidx.work.ExistingPeriodicWorkPolicy, androidx.work.PeriodicWorkRequest);
+ method public final com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enqueueUniqueWork(String, androidx.work.ExistingWorkPolicy, androidx.work.OneTimeWorkRequest);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Void!> enqueueUniqueWork(String, androidx.work.ExistingWorkPolicy, java.util.List<androidx.work.OneTimeWorkRequest!>);
+ method public static androidx.work.multiprocess.RemoteWorkManager getInstance(android.content.Context);
+ method public abstract com.google.common.util.concurrent.ListenableFuture<java.util.List<androidx.work.WorkInfo!>!> getWorkInfos(androidx.work.WorkQuery);
+ }
+
+}
+