Merge "AndroidX Webkit: add thread annotations to callbacks" into androidx-master-dev
diff --git a/activity/activity-ktx/api/api_lint.ignore b/activity/activity-ktx/api/api_lint.ignore
new file mode 100644
index 0000000..285b745
--- /dev/null
+++ b/activity/activity-ktx/api/api_lint.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+RegistrationName: androidx.activity.OnBackPressedDispatcherKt#addCallback(androidx.activity.OnBackPressedDispatcher, androidx.lifecycle.LifecycleOwner, boolean, kotlin.jvm.functions.Function1<? super androidx.activity.OnBackPressedCallback,kotlin.Unit>):
+ Callback methods should be named register/unregister; was addCallback
diff --git a/activity/activity/api/api_lint.ignore b/activity/activity/api/api_lint.ignore
new file mode 100644
index 0000000..da1d0a9
--- /dev/null
+++ b/activity/activity/api/api_lint.ignore
@@ -0,0 +1,13 @@
+// Baseline format: 1.0
+CallbackMethodName: androidx.activity.OnBackPressedCallback:
+ Callback method names must follow the on<Something> style: setEnabled
+
+
+ForbiddenSuperClass: androidx.activity.ComponentActivity:
+ ComponentActivity should not extend `Activity`. Activity subclasses are impossible to compose. Expose a composable API instead.
+
+
+RegistrationName: androidx.activity.OnBackPressedDispatcher#addCallback(androidx.activity.OnBackPressedCallback):
+ Callback methods should be named register/unregister; was addCallback
+RegistrationName: androidx.activity.OnBackPressedDispatcher#addCallback(androidx.lifecycle.LifecycleOwner, androidx.activity.OnBackPressedCallback):
+ Callback methods should be named register/unregister; was addCallback
diff --git a/annotation/annotation-experimental-lint/build.gradle b/annotation/annotation-experimental-lint/build.gradle
new file mode 100644
index 0000000..6ef83b3
--- /dev/null
+++ b/annotation/annotation-experimental-lint/build.gradle
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.CompilationTarget
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.Publish
+
+plugins {
+ id("AndroidXPlugin")
+ id("kotlin")
+}
+
+sourceSets {
+ test.resources.srcDirs(
+ project(":annotation:annotation-experimental-lint-integration-tests")
+ .projectDir.toString() + "/src/main"
+ )
+}
+
+dependencies {
+ compileOnly LINT_API_LATEST
+ compileOnly KOTLIN_STDLIB
+
+ testImplementation KOTLIN_STDLIB
+ testImplementation LINT_CORE
+ testImplementation LINT_TESTS
+}
+
+androidx {
+ name = "Experimental annotation lint checks"
+ toolingProject = true
+ publish = Publish.NONE
+ mavenVersion = LibraryVersions.ANNOTATION
+ mavenGroup = LibraryGroups.ANNOTATION
+ inceptionYear = "2019"
+ description = "Lint checks for the experimental annotation library"
+ url = ARCHITECTURE_URL
+ compilationTarget = CompilationTarget.HOST
+}
diff --git a/annotation/annotation-experimental-lint/integration-tests/build.gradle b/annotation/annotation-experimental-lint/integration-tests/build.gradle
new file mode 100644
index 0000000..656f04a
--- /dev/null
+++ b/annotation/annotation-experimental-lint/integration-tests/build.gradle
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.Publish
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
+import static androidx.build.dependencies.DependenciesKt.KOTLIN_STDLIB
+
+plugins {
+ id("AndroidXPlugin")
+ id("com.android.library")
+ id("org.jetbrains.kotlin.android")
+}
+
+dependencies {
+ compileOnly KOTLIN_STDLIB
+ compileOnly project(":annotation:annotation-experimental")
+}
+
+androidx {
+ name = "Integration test for Experimental"
+ publish = Publish.NONE
+ mavenVersion = LibraryVersions.ANNOTATION
+ mavenGroup = LibraryGroups.ANNOTATION
+ inceptionYear = "2019"
+ description = "Suite of integration tests for the Experimental annotation"
+}
+
+// Allow usage of Kotlin's @Experimental annotation, which is itself experimental.
+tasks.withType(KotlinCompile).configureEach {
+ kotlinOptions {
+ freeCompilerArgs += [ "-Xuse-experimental=kotlin.Experimental" ]
+ }
+}
diff --git a/annotation/annotation-experimental-lint/integration-tests/lint-baseline.xml b/annotation/annotation-experimental-lint/integration-tests/lint-baseline.xml
new file mode 100644
index 0000000..2fe55b4
--- /dev/null
+++ b/annotation/annotation-experimental-lint/integration-tests/lint-baseline.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="5" by="lint 3.5.0-beta04" client="gradle" variant="debug" version="3.5.0-beta04">
+
+ <issue
+ id="UnsafeExperimentalUsageError"
+ message="This declaration is experimental and its usage should be marked with
'@sample.ExperimentalDateTime' or '@UseExperimental(sample.ExperimentalDateTime.class)'"
+ errorLine1=" DateProvider provider = new DateProvider();"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~">
+ <location
+ file="src/main/java/sample/UseExperimentalClassUnchecked.java"
+ line="29"
+ column="33"/>
+ </issue>
+
+ <issue
+ id="UnsafeExperimentalUsageError"
+ message="This declaration is experimental and its usage should be marked with
'@sample.ExperimentalDateTime' or '@UseExperimental(sample.ExperimentalDateTime.class)'"
+ errorLine1=" return provider.getDate();"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/sample/UseExperimentalClassUnchecked.java"
+ line="30"
+ column="25"/>
+ </issue>
+
+ <issue
+ id="UnsafeExperimentalUsageError"
+ message="This declaration is experimental and its usage should be marked with
'@sample.ExperimentalDateTime' or '@UseExperimental(sample.ExperimentalDateTime.class)'"
+ errorLine1=" System.out.println(getDate());"
+ errorLine2=" ~~~~~~~">
+ <location
+ file="src/main/java/sample/UseExperimentalMethodUnchecked.java"
+ line="35"
+ column="28"/>
+ </issue>
+
+</issues>
diff --git a/annotation/annotation-experimental-lint/integration-tests/src/main/AndroidManifest.xml b/annotation/annotation-experimental-lint/integration-tests/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..ed18b0e
--- /dev/null
+++ b/annotation/annotation-experimental-lint/integration-tests/src/main/AndroidManifest.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+<manifest package="androidx.annotation.experimental.lint.integrationtests" />
diff --git a/annotation/annotation-experimental-lint/integration-tests/src/main/java/sample/DateProvider.java b/annotation/annotation-experimental-lint/integration-tests/src/main/java/sample/DateProvider.java
new file mode 100644
index 0000000..28657e1
--- /dev/null
+++ b/annotation/annotation-experimental-lint/integration-tests/src/main/java/sample/DateProvider.java
@@ -0,0 +1,24 @@
+/*
+ * 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 sample;
+
+@ExperimentalDateTime
+class DateProvider {
+ int getDate() {
+ return -1;
+ }
+}
diff --git a/annotation/annotation-experimental-lint/integration-tests/src/main/java/sample/DateProviderKt.kt b/annotation/annotation-experimental-lint/integration-tests/src/main/java/sample/DateProviderKt.kt
new file mode 100644
index 0000000..0c3ed56
--- /dev/null
+++ b/annotation/annotation-experimental-lint/integration-tests/src/main/java/sample/DateProviderKt.kt
@@ -0,0 +1,24 @@
+/*
+ * 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 sample
+
+@ExperimentalDateTimeKt
+class DateProviderKt {
+ fun getDate(): Int {
+ return -1
+ }
+}
\ No newline at end of file
diff --git a/annotation/annotation-experimental-lint/integration-tests/src/main/java/sample/ExperimentalDateTime.java b/annotation/annotation-experimental-lint/integration-tests/src/main/java/sample/ExperimentalDateTime.java
new file mode 100644
index 0000000..ea54036
--- /dev/null
+++ b/annotation/annotation-experimental-lint/integration-tests/src/main/java/sample/ExperimentalDateTime.java
@@ -0,0 +1,29 @@
+/*
+ * 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 sample;
+
+import static androidx.annotation.experimental.Experimental.Level.ERROR;
+
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import androidx.annotation.experimental.Experimental;
+
+import java.lang.annotation.Retention;
+
+@Retention(CLASS)
+@Experimental(level = ERROR)
+@interface ExperimentalDateTime {}
diff --git a/annotation/annotation-experimental-lint/integration-tests/src/main/java/sample/ExperimentalDateTimeKt.kt b/annotation/annotation-experimental-lint/integration-tests/src/main/java/sample/ExperimentalDateTimeKt.kt
new file mode 100644
index 0000000..dbb2289
--- /dev/null
+++ b/annotation/annotation-experimental-lint/integration-tests/src/main/java/sample/ExperimentalDateTimeKt.kt
@@ -0,0 +1,22 @@
+/*
+ * 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 sample
+
+@Experimental
+@Retention(AnnotationRetention.BINARY)
+@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
+annotation class ExperimentalDateTimeKt
diff --git a/annotation/annotation-experimental-lint/integration-tests/src/main/java/sample/UseJavaExperimentalFromJava.java b/annotation/annotation-experimental-lint/integration-tests/src/main/java/sample/UseJavaExperimentalFromJava.java
new file mode 100644
index 0000000..8ffaf35
--- /dev/null
+++ b/annotation/annotation-experimental-lint/integration-tests/src/main/java/sample/UseJavaExperimentalFromJava.java
@@ -0,0 +1,43 @@
+/*
+ * 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 sample;
+
+import androidx.annotation.experimental.UseExperimental;
+
+@SuppressWarnings("unused")
+class UseJavaExperimentalFromJava {
+ int getDateUnsafe() {
+ DateProvider dateProvider = new DateProvider();
+ return dateProvider.getDate();
+ }
+
+ @ExperimentalDateTime
+ int getDateExperimental() {
+ DateProvider dateProvider = new DateProvider();
+ return dateProvider.getDate();
+ }
+
+ @UseExperimental(markerClass = ExperimentalDateTime.class)
+ int getDateUseExperimental() {
+ DateProvider dateProvider = new DateProvider();
+ return dateProvider.getDate();
+ }
+
+ void displayDate() {
+ System.out.println("" + getDateUnsafe());
+ }
+}
diff --git a/annotation/annotation-experimental-lint/integration-tests/src/main/java/sample/UseJavaExperimentalFromKt.kt b/annotation/annotation-experimental-lint/integration-tests/src/main/java/sample/UseJavaExperimentalFromKt.kt
new file mode 100644
index 0000000..da07470
--- /dev/null
+++ b/annotation/annotation-experimental-lint/integration-tests/src/main/java/sample/UseJavaExperimentalFromKt.kt
@@ -0,0 +1,43 @@
+/*
+ * 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 sample
+
+import androidx.annotation.experimental.UseExperimental
+
+@Suppress("unused")
+class UseJavaExperimentalFromKt {
+ fun getDateUnsafe(): Int {
+ val dateProvider = DateProvider()
+ return dateProvider.date
+ }
+
+ @ExperimentalDateTime
+ fun getDateExperimental(): Int {
+ val dateProvider = DateProvider()
+ return dateProvider.date
+ }
+
+ @UseExperimental(markerClass = ExperimentalDateTime::class)
+ fun getDateUseExperimental(): Int {
+ val dateProvider = DateProvider()
+ return dateProvider.date
+ }
+
+ fun displayDate() {
+ println("" + getDateUnsafe())
+ }
+}
\ No newline at end of file
diff --git a/annotation/annotation-experimental-lint/integration-tests/src/main/java/sample/UseKtExperimentalFromJava.java b/annotation/annotation-experimental-lint/integration-tests/src/main/java/sample/UseKtExperimentalFromJava.java
new file mode 100644
index 0000000..2ac44d3
--- /dev/null
+++ b/annotation/annotation-experimental-lint/integration-tests/src/main/java/sample/UseKtExperimentalFromJava.java
@@ -0,0 +1,43 @@
+/*
+ * 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 sample;
+
+import kotlin.UseExperimental;
+
+@SuppressWarnings("unused")
+class UseKtExperimentalFromJava {
+ int getDateUnsafe() {
+ DateProviderKt dateProvider = new DateProviderKt();
+ return dateProvider.getDate();
+ }
+
+ @ExperimentalDateTimeKt
+ int getDateExperimental() {
+ DateProviderKt dateProvider = new DateProviderKt();
+ return dateProvider.getDate();
+ }
+
+ @UseExperimental(markerClass = ExperimentalDateTimeKt.class)
+ int getDateUseExperimental() {
+ DateProviderKt dateProvider = new DateProviderKt();
+ return dateProvider.getDate();
+ }
+
+ void displayDate() {
+ System.out.println("" + getDateUnsafe());
+ }
+}
diff --git a/annotation/annotation-experimental-lint/src/main/java/androidx/annotation/experimental/lint/ExperimentalDetector.kt b/annotation/annotation-experimental-lint/src/main/java/androidx/annotation/experimental/lint/ExperimentalDetector.kt
new file mode 100644
index 0000000..0a7567f
--- /dev/null
+++ b/annotation/annotation-experimental-lint/src/main/java/androidx/annotation/experimental/lint/ExperimentalDetector.kt
@@ -0,0 +1,218 @@
+/*
+ * 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.annotation.experimental.lint
+
+import com.android.tools.lint.detector.api.AnnotationUsageType
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.android.tools.lint.detector.api.isKotlin
+import com.intellij.psi.PsiClassType
+import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiMethod
+import org.jetbrains.uast.UAnnotated
+import org.jetbrains.uast.UAnnotation
+import org.jetbrains.uast.UClass
+import org.jetbrains.uast.UElement
+import org.jetbrains.uast.UNamedExpression
+import org.jetbrains.uast.USimpleNameReferenceExpression
+import org.jetbrains.uast.getParentOfType
+
+@Suppress("SyntheticAccessor")
+class ExperimentalDetector : Detector(), SourceCodeScanner {
+ override fun applicableAnnotations(): List<String>? = listOf(
+ JAVA_EXPERIMENTAL_ANNOTATION,
+ KOTLIN_EXPERIMENTAL_ANNOTATION
+ )
+
+ override fun visitAnnotationUsage(
+ context: JavaContext,
+ usage: UElement,
+ type: AnnotationUsageType,
+ annotation: UAnnotation,
+ qualifiedName: String,
+ method: PsiMethod?,
+ referenced: PsiElement?,
+ annotations: List<UAnnotation>,
+ allMemberAnnotations: List<UAnnotation>,
+ allClassAnnotations: List<UAnnotation>,
+ allPackageAnnotations: List<UAnnotation>
+ ) {
+ when (qualifiedName) {
+ JAVA_EXPERIMENTAL_ANNOTATION -> {
+ checkExperimentalUsage(context, annotation, usage,
+ JAVA_USE_EXPERIMENTAL_ANNOTATION
+ )
+ }
+ KOTLIN_EXPERIMENTAL_ANNOTATION -> {
+ if (!isKotlin(usage.sourcePsi)) {
+ checkExperimentalUsage(context, annotation, usage,
+ KOTLIN_USE_EXPERIMENTAL_ANNOTATION
+ )
+ }
+ }
+ }
+ }
+
+ /**
+ * Check whether the given experimental API [annotation] can be referenced from [usage] call
+ * site.
+ *
+ * @param context the lint scanning context
+ * @param annotation the experimental annotation detected on the referenced element
+ * @param usage the element whose usage should be checked
+ * @param useAnnotationName fully-qualified class name for experimental opt-in annotation
+ */
+ private fun checkExperimentalUsage(
+ context: JavaContext,
+ annotation: UAnnotation,
+ usage: UElement,
+ useAnnotationName: String
+ ) {
+ val useAnnotation = (annotation.uastParent as? UClass)?.qualifiedName ?: return
+ if (!hasOrUsesAnnotation(context, usage, useAnnotation, useAnnotationName)) {
+ val level = extractAttribute(annotation, "level")
+ report(context, usage, """
+ This declaration is experimental and its usage should be marked with
+ '@$useAnnotation' or '@UseExperimental($useAnnotation.class)'
+ """, level)
+ }
+ }
+
+ private fun extractAttribute(annotation: UAnnotation, name: String): String {
+ if (annotation.attributeValues.isNotEmpty()) {
+ return annotation.attributeValues[0].simpleName
+ ?: throw IllegalStateException("Failed to extract level from simple annotation")
+ }
+ return (annotation.findAttributeValue(name)?.evaluate() as? Pair<*, *>)?.second?.toString()
+ ?: throw IllegalStateException("Failed to extract level from reference annotation")
+ }
+
+ /**
+ * Check whether the specified [usage] is either within the scope of [annotationName] or an
+ * explicit opt-in via the [useAnnotationName] annotation.
+ */
+ private fun hasOrUsesAnnotation(
+ context: JavaContext,
+ usage: UElement,
+ annotationName: String,
+ useAnnotationName: String
+ ): Boolean {
+ var element: UAnnotated? = if (usage is UAnnotated) {
+ usage
+ } else {
+ usage.getParentOfType(UAnnotated::class.java)
+ }
+
+ while (element != null) {
+ val annotations = context.evaluator.getAllAnnotations(element, false)
+ val matchName = annotations.any { it.qualifiedName == annotationName }
+ val matchUse = annotations
+ .filter { annotation -> annotation.qualifiedName == useAnnotationName }
+ .mapNotNull { annotation -> annotation.attributeValues.getOrNull(0) }
+ .any { attrValue -> attrValue.getFullyQualifiedName(context) == annotationName }
+ if (matchName || matchUse) return true
+ element = element.getParentOfType(UAnnotated::class.java)
+ }
+ return false
+ }
+
+ /**
+ * Reports an issue and trims indentation on the [message].
+ */
+ private fun report(
+ context: JavaContext,
+ usage: UElement,
+ message: String,
+ level: String
+
+ ) {
+ val issue = when (level) {
+ "ERROR" -> ISSUE_ERROR
+ "WARNING" -> ISSUE_WARNING
+ else -> throw IllegalArgumentException("Level must be one of ERROR, WARNING")
+ }
+ context.report(issue, usage, context.getNameLocation(usage), message.trimIndent())
+ }
+
+ companion object {
+ private val IMPLEMENTATION = Implementation(
+ ExperimentalDetector::class.java,
+ Scope.JAVA_FILE_SCOPE
+ )
+
+ private const val KOTLIN_EXPERIMENTAL_ANNOTATION = "kotlin.Experimental"
+ private const val KOTLIN_USE_EXPERIMENTAL_ANNOTATION = "kotlin.UseExperimental"
+
+ private const val JAVA_EXPERIMENTAL_ANNOTATION =
+ "androidx.annotation.experimental.Experimental"
+ private const val JAVA_USE_EXPERIMENTAL_ANNOTATION =
+ "androidx.annotation.experimental.UseExperimental"
+
+ @Suppress("DefaultLocale")
+ private fun issueForLevel(level: String, severity: Severity): Issue = Issue.create(
+ id = "UnsafeExperimentalUsage${level.capitalize()}",
+ briefDescription = "Unsafe experimental usage intended to be $level-level severity",
+ explanation = """
+ This API has been flagged as experimental with $level-level severity.
+
+ Any declaration annotated with this marker is considered part of an unstable API \
+ surface and its call sites should accept the experimental aspect of it either by \
+ using `@UseExperimental`, or by being annotated with that marker themselves, \
+ effectively causing further propagation of that experimental aspect.
+ """,
+ category = Category.CORRECTNESS,
+ priority = 4,
+ severity = severity,
+ implementation = IMPLEMENTATION
+ )
+
+ val ISSUE_ERROR =
+ issueForLevel(
+ "error",
+ Severity.ERROR
+ )
+ val ISSUE_WARNING =
+ issueForLevel(
+ "warning",
+ Severity.WARNING
+ )
+
+ val ISSUES = listOf(
+ ISSUE_ERROR,
+ ISSUE_WARNING
+ )
+ }
+}
+
+/**
+ * Returns the simple name (not qualified) associated with an expression, if any.
+ */
+private val UNamedExpression?.simpleName: String? get() = this?.let {
+ (expression as? USimpleNameReferenceExpression)?.identifier
+}
+
+/**
+ * Returns the fully-qualified class name for a given attribute value, if any.
+ */
+private fun UNamedExpression?.getFullyQualifiedName(context: JavaContext): String? =
+ this?.let { (evaluate() as? PsiClassType)?.let { context.evaluator.getQualifiedName(it) } }
diff --git a/annotation/annotation-experimental-lint/src/main/java/androidx/annotation/experimental/lint/ExperimentalIssueRegistry.kt b/annotation/annotation-experimental-lint/src/main/java/androidx/annotation/experimental/lint/ExperimentalIssueRegistry.kt
new file mode 100644
index 0000000..7e69019
--- /dev/null
+++ b/annotation/annotation-experimental-lint/src/main/java/androidx/annotation/experimental/lint/ExperimentalIssueRegistry.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.annotation.experimental.lint
+
+import com.android.tools.lint.client.api.IssueRegistry
+
+class ExperimentalIssueRegistry : IssueRegistry() {
+ override val issues get() = ExperimentalDetector.ISSUES
+}
diff --git a/annotation/annotation-experimental-lint/src/main/resources/META-INF/services/com.android.tools.lint.client.api.IssueRegistry b/annotation/annotation-experimental-lint/src/main/resources/META-INF/services/com.android.tools.lint.client.api.IssueRegistry
new file mode 100644
index 0000000..160ad53
--- /dev/null
+++ b/annotation/annotation-experimental-lint/src/main/resources/META-INF/services/com.android.tools.lint.client.api.IssueRegistry
@@ -0,0 +1 @@
+androidx.annotation.experimental.lint.ExperimentalIssueRegistry
diff --git a/annotation/annotation-experimental-lint/src/test/kotlin/androidx/annotation/experimental/lint/ExperimentalDetectorTest.kt b/annotation/annotation-experimental-lint/src/test/kotlin/androidx/annotation/experimental/lint/ExperimentalDetectorTest.kt
new file mode 100644
index 0000000..21744e9
--- /dev/null
+++ b/annotation/annotation-experimental-lint/src/test/kotlin/androidx/annotation/experimental/lint/ExperimentalDetectorTest.kt
@@ -0,0 +1,227 @@
+/*
+ * 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.annotation.experimental.lint
+
+import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.lint.checks.infrastructure.TestFiles.java
+import com.android.tools.lint.checks.infrastructure.TestFiles.kotlin
+import com.android.tools.lint.checks.infrastructure.TestLintResult
+import com.android.tools.lint.checks.infrastructure.TestLintTask.lint
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class ExperimentalDetectorTest {
+
+ private fun checkJava(vararg testFiles: TestFile): TestLintResult {
+ return lint()
+ .files(
+ EXPERIMENTAL_JAVA,
+ USE_EXPERIMENTAL_JAVA,
+ *testFiles
+ )
+ .allowMissingSdk(true)
+ .issues(*ExperimentalDetector.ISSUES.toTypedArray())
+ .run()
+ }
+
+ @Test
+ fun useJavaExperimentalFromJava() {
+ val input = arrayOf(
+ javaSample("sample.DateProvider"),
+ javaSample("sample.ExperimentalDateTime"),
+ javaSample("sample.UseJavaExperimentalFromJava")
+ )
+
+ /* ktlint-disable max-line-length */
+ val expected = """
+src/sample/UseJavaExperimentalFromJava.java:24: Error: This declaration is experimental and its usage should be marked with
+'@sample.ExperimentalDateTime' or '@UseExperimental(sample.ExperimentalDateTime.class)' [UnsafeExperimentalUsageError]
+ DateProvider dateProvider = new DateProvider();
+ ~~~~~~~~~~~~~~~~~~
+src/sample/UseJavaExperimentalFromJava.java:25: Error: This declaration is experimental and its usage should be marked with
+'@sample.ExperimentalDateTime' or '@UseExperimental(sample.ExperimentalDateTime.class)' [UnsafeExperimentalUsageError]
+ return dateProvider.getDate();
+ ~~~~~~~
+2 errors, 0 warnings
+ """.trimIndent()
+ /* ktlint-enable max-line-length */
+
+ checkJava(*input).expect(expected)
+ }
+
+ @Test
+ fun useJavaExperimentalFromKt() {
+ val input = arrayOf(
+ javaSample("sample.DateProvider"),
+ javaSample("sample.ExperimentalDateTime"),
+ ktSample("sample.UseJavaExperimentalFromKt")
+ )
+
+ /* ktlint-disable max-line-length */
+ val expected = """
+src/sample/UseJavaExperimentalFromKt.kt:24: Error: This declaration is experimental and its usage should be marked with
+'@sample.ExperimentalDateTime' or '@UseExperimental(sample.ExperimentalDateTime.class)' [UnsafeExperimentalUsageError]
+ val dateProvider = DateProvider()
+ ~~~~~~~~~~~~
+src/sample/UseJavaExperimentalFromKt.kt:25: Error: This declaration is experimental and its usage should be marked with
+'@sample.ExperimentalDateTime' or '@UseExperimental(sample.ExperimentalDateTime.class)' [UnsafeExperimentalUsageError]
+ return dateProvider.date
+ ~~~~
+src/sample/UseJavaExperimentalFromKt.kt:36: Error: This declaration is experimental and its usage should be marked with
+'@sample.ExperimentalDateTime' or '@UseExperimental(sample.ExperimentalDateTime.class)' [UnsafeExperimentalUsageError]
+ val dateProvider = DateProvider()
+ ~~~~~~~~~~~~
+src/sample/UseJavaExperimentalFromKt.kt:37: Error: This declaration is experimental and its usage should be marked with
+'@sample.ExperimentalDateTime' or '@UseExperimental(sample.ExperimentalDateTime.class)' [UnsafeExperimentalUsageError]
+ return dateProvider.date
+ ~~~~
+4 errors, 0 warnings
+ """.trimIndent()
+ /* ktlint-enable max-line-length */
+
+ checkJava(*input).expect(expected)
+ }
+
+ @Test
+ fun useKtExperimentalFromJava() {
+ val input = arrayOf(
+ EXPERIMENTAL_KT,
+ ktSample("sample.DateProviderKt"),
+ ktSample("sample.ExperimentalDateTimeKt"),
+ javaSample("sample.UseKtExperimentalFromJava")
+ )
+
+ /* ktlint-disable max-line-length */
+ val expected = """
+src/sample/UseKtExperimentalFromJava.java:24: Error: This declaration is experimental and its usage should be marked with
+'@sample.ExperimentalDateTimeKt' or '@UseExperimental(sample.ExperimentalDateTimeKt.class)' [UnsafeExperimentalUsageError]
+ DateProviderKt dateProvider = new DateProviderKt();
+ ~~~~~~~~~~~~~~~~~~~~
+src/sample/UseKtExperimentalFromJava.java:25: Error: This declaration is experimental and its usage should be marked with
+'@sample.ExperimentalDateTimeKt' or '@UseExperimental(sample.ExperimentalDateTimeKt.class)' [UnsafeExperimentalUsageError]
+ return dateProvider.getDate();
+ ~~~~~~~
+2 errors, 0 warnings
+ """.trimIndent()
+ /* ktlint-enable max-line-length */
+
+ checkJava(*input).expect(expected)
+ }
+
+ /**
+ * Loads a [TestFile] from Java source code included in the JAR resources.
+ */
+ private fun javaSample(className: String): TestFile {
+ return java(javaClass.getResource("/java/${className.replace('.','/')}.java").readText())
+ }
+
+ /**
+ * Loads a [TestFile] from Kotlin source code included in the JAR resources.
+ */
+ private fun ktSample(className: String): TestFile {
+ return kotlin(javaClass.getResource("/java/${className.replace('.','/')}.kt").readText())
+ }
+
+ companion object {
+ /* ktlint-disable max-line-length */
+ // The contents of Experimental.java from the experimental annotation library.
+ val EXPERIMENTAL_JAVA: TestFile = java("""
+ package androidx.annotation.experimental;
+
+ import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+ import static java.lang.annotation.RetentionPolicy.CLASS;
+
+ import java.lang.annotation.Retention;
+ import java.lang.annotation.Target;
+
+ @Retention(CLASS)
+ @Target({ANNOTATION_TYPE})
+ public @interface Experimental {
+ enum Level {
+ WARNING,
+ ERROR,
+ }
+
+ Level level() default Level.ERROR;
+ }
+ """.trimIndent())
+
+ // The contents of UseExperimental.java from the experimental annotation library.
+ val USE_EXPERIMENTAL_JAVA: TestFile = java("""
+ package androidx.annotation.experimental;
+
+ import static java.lang.annotation.ElementType.CONSTRUCTOR;
+ import static java.lang.annotation.ElementType.FIELD;
+ import static java.lang.annotation.ElementType.METHOD;
+ import static java.lang.annotation.ElementType.PACKAGE;
+ import static java.lang.annotation.ElementType.TYPE;
+ import static java.lang.annotation.RetentionPolicy.CLASS;
+
+ import java.lang.annotation.Retention;
+ import java.lang.annotation.Target;
+
+ @Retention(CLASS)
+ @Target({TYPE, METHOD, CONSTRUCTOR, FIELD, PACKAGE})
+ public @interface UseExperimental {
+ Class<?> markerClass();
+ }
+ """.trimIndent())
+
+ // The contents of Experimental.kt from the Kotlin standard library.
+ val EXPERIMENTAL_KT: TestFile = kotlin("""
+ package kotlin
+
+ import kotlin.annotation.AnnotationRetention.BINARY
+ import kotlin.annotation.AnnotationRetention.SOURCE
+ import kotlin.annotation.AnnotationTarget.*
+ import kotlin.internal.RequireKotlin
+ import kotlin.internal.RequireKotlinVersionKind
+ import kotlin.reflect.KClass
+
+ @Target(ANNOTATION_CLASS)
+ @Retention(BINARY)
+ @SinceKotlin("1.2")
+ @RequireKotlin("1.2.50", versionKind = RequireKotlinVersionKind.COMPILER_VERSION)
+ @Suppress("ANNOTATION_CLASS_MEMBER")
+ public annotation class Experimental(val level: Level = Level.ERROR) {
+ public enum class Level {
+ WARNING,
+ ERROR,
+ }
+ }
+
+ @Target(
+ CLASS, PROPERTY, LOCAL_VARIABLE, VALUE_PARAMETER, CONSTRUCTOR, FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER, EXPRESSION, FILE, TYPEALIAS
+ )
+ @Retention(SOURCE)
+ @SinceKotlin("1.2")
+ @RequireKotlin("1.2.50", versionKind = RequireKotlinVersionKind.COMPILER_VERSION)
+ public annotation class UseExperimental(
+ vararg val markerClass: KClass<out Annotation>
+ )
+
+ @Target(CLASS, PROPERTY, CONSTRUCTOR, FUNCTION, TYPEALIAS)
+ @Retention(BINARY)
+ internal annotation class WasExperimental(
+ vararg val markerClass: KClass<out Annotation>
+ )
+ """.trimIndent())
+ /* ktlint-enable max-line-length */
+ }
+}
diff --git a/annotation/annotation-experimental/build.gradle b/annotation/annotation-experimental/build.gradle
new file mode 100644
index 0000000..922edae
--- /dev/null
+++ b/annotation/annotation-experimental/build.gradle
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.Publish
+
+plugins {
+ id("AndroidXPlugin")
+ id("com.android.library")
+}
+
+dependencies {
+ lintPublish(project(":annotation:annotation-experimental-lint"))
+}
+
+androidx {
+ name = "Experimental annotation"
+ publish = Publish.SNAPSHOT_AND_RELEASE
+ mavenVersion = LibraryVersions.ANNOTATION
+ mavenGroup = LibraryGroups.ANNOTATION
+ inceptionYear = "2019"
+ description = "Annotation for use on unstable API surfaces"
+}
diff --git a/annotation/annotation-experimental/src/main/AndroidManifest.xml b/annotation/annotation-experimental/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..0294c29
--- /dev/null
+++ b/annotation/annotation-experimental/src/main/AndroidManifest.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+<manifest package="androidx.annotation.experimental" />
diff --git a/annotation/annotation-experimental/src/main/java/androidx/annotation/experimental/Experimental.java b/annotation/annotation-experimental/src/main/java/androidx/annotation/experimental/Experimental.java
new file mode 100644
index 0000000..bb6b7b7
--- /dev/null
+++ b/annotation/annotation-experimental/src/main/java/androidx/annotation/experimental/Experimental.java
@@ -0,0 +1,90 @@
+/*
+ * 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.annotation.experimental;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Denotes that the annotated element is a marker of an experimental API.
+ *
+ * Any declaration annotated with this marker is considered part of an unstable API surface and its
+ * call sites should accept the experimental aspect of it either by using {@link UseExperimental},
+ * or by being annotated with that marker themselves, effectively causing further propagation of
+ * that experimental aspect.
+ *
+ * Example:
+ * <pre><code>
+ * // Library code
+ * @Retention(CLASS)
+ * @Target({TYPE, METHOD, CONSTRUCTOR, FIELD, PACKAGE})
+ * @Experimental(level = Level.ERROR)
+ * public @interface ExperimentalDateTime {}
+ *
+ * @ExperimentalDateTime
+ * public class DateProvider {
+ * // ...
+ * }
+ * </code></pre>
+ *
+ * <pre><code>
+ * // Client code
+ * int getYear() {
+ * DateProvider provider; // Error: DateProvider is experimental
+ * // ...
+ * }
+ *
+ * @ExperimentalDateTime
+ * Date getDate() {
+ * DateProvider provider; // OK: the function is marked as experimental
+ * // ...
+ * }
+ *
+ * void displayDate() {
+ * System.out.println(getDate()); // Error: getDate() is experimental, acceptance is required
+ * }
+ * </code></pre>
+ *
+ */
+@Retention(CLASS)
+@Target({ANNOTATION_TYPE})
+public @interface Experimental {
+ /**
+ * Severity of the diagnostic that should be reported on usages of experimental API which did
+ * not explicitly accept the experimental aspect of that API either by using
+ * {@link UseExperimental} or by being annotated with the corresponding marker annotation.
+ */
+ enum Level {
+ /**
+ * Specifies that a warning should be reported on incorrect usages of this experimental API.
+ */
+ WARNING,
+
+ /**
+ * Specifies that an error should be reported on incorrect usages of this experimental API.
+ */
+ ERROR,
+ }
+
+ /**
+ * Defines the reporting level for incorrect usages of this experimental API.
+ */
+ Level level() default Level.ERROR;
+}
diff --git a/annotation/annotation-experimental/src/main/java/androidx/annotation/experimental/UseExperimental.java b/annotation/annotation-experimental/src/main/java/androidx/annotation/experimental/UseExperimental.java
new file mode 100644
index 0000000..2acece8
--- /dev/null
+++ b/annotation/annotation-experimental/src/main/java/androidx/annotation/experimental/UseExperimental.java
@@ -0,0 +1,41 @@
+/*
+ * 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.annotation.experimental;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PACKAGE;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Allows use of an experimental API denoted by the given markers in the annotated file,
+ * declaration, or expression. If a declaration is annotated with {@link UseExperimental}, its
+ * usages are <strong>not</strong> required to opt-in to that experimental API.
+ */
+@Retention(CLASS)
+@Target({TYPE, METHOD, CONSTRUCTOR, FIELD, PACKAGE})
+public @interface UseExperimental {
+ /**
+ * Defines the experimental API whose usage this annotation allows.
+ */
+ Class<?> markerClass();
+}
diff --git a/annotation/annotation-sampled/build.gradle b/annotation/annotation-sampled/build.gradle
new file mode 100644
index 0000000..64d858f
--- /dev/null
+++ b/annotation/annotation-sampled/build.gradle
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+import static androidx.build.dependencies.DependenciesKt.*
+
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+import androidx.build.Publish
+
+plugins {
+ id("AndroidXPlugin")
+ id("kotlin")
+}
+
+dependencies {
+ implementation(KOTLIN_STDLIB)
+}
+
+androidx {
+ publish = Publish.NONE
+ toolingProject = true
+}
diff --git a/annotation/annotation-sampled/src/main/java/androidx/annotation/Sampled.kt b/annotation/annotation-sampled/src/main/java/androidx/annotation/Sampled.kt
new file mode 100644
index 0000000..f03f55f
--- /dev/null
+++ b/annotation/annotation-sampled/src/main/java/androidx/annotation/Sampled.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.annotation
+
+/**
+ * Denotes that the annotated function is considered a sample function, and is linked to from the
+ * KDoc of a source module.
+ *
+ * There are corresponding lint checks ensuring that functions referred to from KDoc with a @sample
+ * tag are annotated with this annotation, and also to ensure that any functions annotated with this
+ * annotation are linked to from a @sample tag.
+ */
+@Target(AnnotationTarget.FUNCTION)
+@Retention(AnnotationRetention.SOURCE)
+annotation class Sampled
diff --git a/appcompat/api/api_lint.ignore b/appcompat/api/api_lint.ignore
new file mode 100644
index 0000000..7c227b9
--- /dev/null
+++ b/appcompat/api/api_lint.ignore
@@ -0,0 +1,31 @@
+// Baseline format: 1.0
+AcronymName: androidx.appcompat.widget.AppCompatImageButton#setImageURI(android.net.Uri):
+ Acronyms should not be capitalized in method names: was `setImageURI`, should this be `setImageUri`?
+AcronymName: androidx.appcompat.widget.AppCompatImageView#setImageURI(android.net.Uri):
+ Acronyms should not be capitalized in method names: was `setImageURI`, should this be `setImageUri`?
+
+
+ContextFirst: androidx.appcompat.app.AppCompatDelegate#createView(android.view.View, String, android.content.Context, android.util.AttributeSet) parameter #2:
+ Context is distinct, so it must be the first argument (method `createView`)
+
+
+ForbiddenSuperClass: androidx.appcompat.app.AppCompatActivity:
+ AppCompatActivity should not extend `Activity`. Activity subclasses are impossible to compose. Expose a composable API instead.
+
+
+ParcelConstructor: androidx.appcompat.widget.Toolbar.SavedState#SavedState(android.os.Parcel):
+ Parcelable inflation is exposed through CREATOR, not raw constructors, in androidx.appcompat.widget.Toolbar.SavedState
+
+
+ParcelCreator: androidx.appcompat.widget.Toolbar.SavedState:
+ Parcelable requires `public int describeContents()`; missing in androidx.appcompat.widget.Toolbar.SavedState
+
+
+ParcelNotFinal: androidx.appcompat.widget.Toolbar.SavedState:
+ Parcelable classes must be final: androidx.appcompat.widget.Toolbar.SavedState is not final
+
+
+VisiblySynchronized: androidx.appcompat.widget.AppCompatRatingBar#onMeasure(int, int):
+ Internal locks must not be exposed: method androidx.appcompat.widget.AppCompatRatingBar.onMeasure(int,int)
+VisiblySynchronized: androidx.appcompat.widget.AppCompatSeekBar#onDraw(android.graphics.Canvas):
+ Internal locks must not be exposed: method androidx.appcompat.widget.AppCompatSeekBar.onDraw(android.graphics.Canvas)
diff --git a/benchmark/api/api_lint.ignore b/benchmark/api/api_lint.ignore
new file mode 100644
index 0000000..0a0591c
--- /dev/null
+++ b/benchmark/api/api_lint.ignore
@@ -0,0 +1,7 @@
+// Baseline format: 1.0
+DocumentExceptions: androidx.benchmark.BenchmarkRule#getState():
+ Method BenchmarkRule.getState appears to be throwing java.lang.IllegalStateException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.benchmark.BenchmarkState#pauseTiming():
+ Method BenchmarkState.pauseTiming appears to be throwing java.lang.IllegalStateException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.benchmark.BenchmarkState#resumeTiming():
+ Method BenchmarkState.resumeTiming appears to be throwing java.lang.IllegalStateException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
diff --git a/browser/api/api_lint.ignore b/browser/api/api_lint.ignore
new file mode 100644
index 0000000..880cc30
--- /dev/null
+++ b/browser/api/api_lint.ignore
@@ -0,0 +1,73 @@
+// Baseline format: 1.0
+AcronymName: androidx.browser.customtabs.CustomTabsIntent#setAlwaysUseBrowserUI(android.content.Intent):
+ Acronyms should not be capitalized in method names: was `setAlwaysUseBrowserUI`, should this be `setAlwaysUseBrowserUi`?
+AcronymName: androidx.browser.customtabs.CustomTabsIntent#shouldAlwaysUseBrowserUI(android.content.Intent):
+ Acronyms should not be capitalized in method names: was `shouldAlwaysUseBrowserUI`, should this be `shouldAlwaysUseBrowserUi`?
+
+
+ActionValue: androidx.browser.browseractions.BrowserActionsIntent#ACTION_BROWSER_ACTIONS_OPEN:
+ Inconsistent action value; expected `androidx.browser.browseractions.action.BROWSER_ACTIONS_OPEN`, was `androidx.browser.browseractions.browser_action_open`
+ActionValue: androidx.browser.browseractions.BrowserActionsIntent#EXTRA_APP_ID:
+ Inconsistent extra value; expected `androidx.browser.browseractions.extra.APP_ID`, was `androidx.browser.browseractions.APP_ID`
+ActionValue: androidx.browser.customtabs.CustomTabsIntent#EXTRA_ACTION_BUTTON_BUNDLE:
+ Inconsistent extra value; expected `androidx.browser.customtabs.extra.ACTION_BUTTON_BUNDLE`, was `android.support.customtabs.extra.ACTION_BUTTON_BUNDLE`
+ActionValue: androidx.browser.customtabs.CustomTabsIntent#EXTRA_CLOSE_BUTTON_ICON:
+ Inconsistent extra value; expected `androidx.browser.customtabs.extra.CLOSE_BUTTON_ICON`, was `android.support.customtabs.extra.CLOSE_BUTTON_ICON`
+ActionValue: androidx.browser.customtabs.CustomTabsIntent#EXTRA_DEFAULT_SHARE_MENU_ITEM:
+ Inconsistent extra value; expected `androidx.browser.customtabs.extra.DEFAULT_SHARE_MENU_ITEM`, was `android.support.customtabs.extra.SHARE_MENU_ITEM`
+ActionValue: androidx.browser.customtabs.CustomTabsIntent#EXTRA_ENABLE_INSTANT_APPS:
+ Inconsistent extra value; expected `androidx.browser.customtabs.extra.ENABLE_INSTANT_APPS`, was `android.support.customtabs.extra.EXTRA_ENABLE_INSTANT_APPS`
+ActionValue: androidx.browser.customtabs.CustomTabsIntent#EXTRA_ENABLE_URLBAR_HIDING:
+ Inconsistent extra value; expected `androidx.browser.customtabs.extra.ENABLE_URLBAR_HIDING`, was `android.support.customtabs.extra.ENABLE_URLBAR_HIDING`
+ActionValue: androidx.browser.customtabs.CustomTabsIntent#EXTRA_EXIT_ANIMATION_BUNDLE:
+ Inconsistent extra value; expected `androidx.browser.customtabs.extra.EXIT_ANIMATION_BUNDLE`, was `android.support.customtabs.extra.EXIT_ANIMATION_BUNDLE`
+ActionValue: androidx.browser.customtabs.CustomTabsIntent#EXTRA_MENU_ITEMS:
+ Inconsistent extra value; expected `androidx.browser.customtabs.extra.MENU_ITEMS`, was `android.support.customtabs.extra.MENU_ITEMS`
+ActionValue: androidx.browser.customtabs.CustomTabsIntent#EXTRA_REMOTEVIEWS:
+ Inconsistent extra value; expected `androidx.browser.customtabs.extra.REMOTEVIEWS`, was `android.support.customtabs.extra.EXTRA_REMOTEVIEWS`
+ActionValue: androidx.browser.customtabs.CustomTabsIntent#EXTRA_REMOTEVIEWS_CLICKED_ID:
+ Inconsistent extra value; expected `androidx.browser.customtabs.extra.REMOTEVIEWS_CLICKED_ID`, was `android.support.customtabs.extra.EXTRA_REMOTEVIEWS_CLICKED_ID`
+ActionValue: androidx.browser.customtabs.CustomTabsIntent#EXTRA_REMOTEVIEWS_PENDINGINTENT:
+ Inconsistent extra value; expected `androidx.browser.customtabs.extra.REMOTEVIEWS_PENDINGINTENT`, was `android.support.customtabs.extra.EXTRA_REMOTEVIEWS_PENDINGINTENT`
+ActionValue: androidx.browser.customtabs.CustomTabsIntent#EXTRA_REMOTEVIEWS_VIEW_IDS:
+ Inconsistent extra value; expected `androidx.browser.customtabs.extra.REMOTEVIEWS_VIEW_IDS`, was `android.support.customtabs.extra.EXTRA_REMOTEVIEWS_VIEW_IDS`
+ActionValue: androidx.browser.customtabs.CustomTabsIntent#EXTRA_SECONDARY_TOOLBAR_COLOR:
+ Inconsistent extra value; expected `androidx.browser.customtabs.extra.SECONDARY_TOOLBAR_COLOR`, was `android.support.customtabs.extra.SECONDARY_TOOLBAR_COLOR`
+ActionValue: androidx.browser.customtabs.CustomTabsIntent#EXTRA_SESSION:
+ Inconsistent extra value; expected `androidx.browser.customtabs.extra.SESSION`, was `android.support.customtabs.extra.SESSION`
+ActionValue: androidx.browser.customtabs.CustomTabsIntent#EXTRA_TINT_ACTION_BUTTON:
+ Inconsistent extra value; expected `androidx.browser.customtabs.extra.TINT_ACTION_BUTTON`, was `android.support.customtabs.extra.TINT_ACTION_BUTTON`
+ActionValue: androidx.browser.customtabs.CustomTabsIntent#EXTRA_TITLE_VISIBILITY_STATE:
+ Inconsistent extra value; expected `androidx.browser.customtabs.extra.TITLE_VISIBILITY_STATE`, was `android.support.customtabs.extra.TITLE_VISIBILITY`
+ActionValue: androidx.browser.customtabs.CustomTabsIntent#EXTRA_TOOLBAR_COLOR:
+ Inconsistent extra value; expected `androidx.browser.customtabs.extra.TOOLBAR_COLOR`, was `android.support.customtabs.extra.TOOLBAR_COLOR`
+ActionValue: androidx.browser.customtabs.CustomTabsIntent#EXTRA_TOOLBAR_ITEMS:
+ Inconsistent extra value; expected `androidx.browser.customtabs.extra.TOOLBAR_ITEMS`, was `android.support.customtabs.extra.TOOLBAR_ITEMS`
+ActionValue: androidx.browser.customtabs.CustomTabsService#ACTION_CUSTOM_TABS_CONNECTION:
+ Inconsistent action value; expected `androidx.browser.customtabs.action.CUSTOM_TABS_CONNECTION`, was `android.support.customtabs.action.CustomTabsService`
+ActionValue: androidx.browser.customtabs.TrustedWebUtils#EXTRA_LAUNCH_AS_TRUSTED_WEB_ACTIVITY:
+ Inconsistent extra value; expected `androidx.browser.customtabs.extra.LAUNCH_AS_TRUSTED_WEB_ACTIVITY`, was `android.support.customtabs.extra.LAUNCH_AS_TRUSTED_WEB_ACTIVITY`
+
+
+AutoBoxing: androidx.browser.customtabs.CustomTabColorSchemeParams#navigationBarColor:
+ Must avoid boxed primitives (`java.lang.Integer`)
+AutoBoxing: androidx.browser.customtabs.CustomTabColorSchemeParams#secondaryToolbarColor:
+ Must avoid boxed primitives (`java.lang.Integer`)
+AutoBoxing: androidx.browser.customtabs.CustomTabColorSchemeParams#toolbarColor:
+ Must avoid boxed primitives (`java.lang.Integer`)
+
+
+CallbackMethodName: androidx.browser.customtabs.CustomTabsCallback:
+ Callback method names must follow the on<Something> style: extraCallback
+
+
+ConcreteCollection: androidx.browser.browseractions.BrowserActionsIntent#openBrowserAction(android.content.Context, android.net.Uri, int, java.util.ArrayList<androidx.browser.browseractions.BrowserActionItem>, android.app.PendingIntent) parameter #3:
+ Parameter type is concrete collection (`java.util.ArrayList`); must be higher-level interface
+ConcreteCollection: androidx.browser.browseractions.BrowserActionsIntent#parseBrowserActionItems(java.util.ArrayList<android.os.Bundle>) parameter #0:
+ Parameter type is concrete collection (`java.util.ArrayList`); must be higher-level interface
+ConcreteCollection: androidx.browser.browseractions.BrowserActionsIntent.Builder#setCustomItems(java.util.ArrayList<androidx.browser.browseractions.BrowserActionItem>) parameter #0:
+ Parameter type is concrete collection (`java.util.ArrayList`); must be higher-level interface
+
+
+IntentName: androidx.browser.browseractions.BrowserActionsIntent#KEY_ACTION:
+ Intent action constant name must be ACTION_FOO: KEY_ACTION
diff --git a/buildSrc/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt b/buildSrc/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt
index d0d4cb7..7170927 100644
--- a/buildSrc/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt
+++ b/buildSrc/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt
@@ -24,6 +24,12 @@
BanKeepAnnotation.ISSUE,
BanTargetApiAnnotation.ISSUE,
MissingTestSizeAnnotation.ISSUE,
+ SampledAnnotationEnforcer.MISSING_SAMPLED_ANNOTATION,
+ SampledAnnotationEnforcer.OBSOLETE_SAMPLED_ANNOTATION,
+ SampledAnnotationEnforcer.MISSING_SAMPLES_DIRECTORY,
+ SampledAnnotationEnforcer.UNRESOLVED_SAMPLE_LINK,
+ SampledAnnotationEnforcer.MULTIPLE_FUNCTIONS_FOUND,
+ SampledAnnotationEnforcer.INVALID_SAMPLES_LOCATION,
ObsoleteBuildCompatUsageDetector.ISSUE
)
}
diff --git a/buildSrc/lint-checks/src/main/java/androidx/build/lint/SampledAnnotationEnforcer.kt b/buildSrc/lint-checks/src/main/java/androidx/build/lint/SampledAnnotationEnforcer.kt
new file mode 100644
index 0000000..4df1375
--- /dev/null
+++ b/buildSrc/lint-checks/src/main/java/androidx/build/lint/SampledAnnotationEnforcer.kt
@@ -0,0 +1,530 @@
+/*
+ * 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.build.lint
+
+import androidx.build.lint.SampledAnnotationEnforcer.Companion.SAMPLED_ANNOTATION
+import androidx.build.lint.SampledAnnotationEnforcer.Companion.SAMPLES_DIRECTORY
+import androidx.build.lint.SampledAnnotationEnforcer.Companion.SAMPLE_KDOC_ANNOTATION
+import com.android.tools.lint.client.api.UElementHandler
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Context
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.intellij.psi.PsiDirectory
+import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiFile
+import org.jetbrains.kotlin.asJava.namedUnwrappedElement
+import org.jetbrains.kotlin.kdoc.psi.api.KDoc
+import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection
+import org.jetbrains.kotlin.psi.KtDeclaration
+import org.jetbrains.kotlin.psi.KtFile
+import org.jetbrains.kotlin.psi.KtNamedFunction
+import org.jetbrains.kotlin.psi.KtTreeVisitorVoid
+import org.jetbrains.kotlin.psi.findDocComment.findDocComment
+import org.jetbrains.kotlin.psi.psiUtil.safeNameForLazyResolve
+import org.jetbrains.kotlin.utils.addIfNotNull
+import org.jetbrains.uast.UDeclaration
+import org.jetbrains.uast.UElement
+import org.jetbrains.uast.UMethod
+import java.io.File
+
+/**
+ * Class containing two lint detectors responsible for enforcing @Sampled annotation usage when
+ * AndroidXExtension.enforceSampledAnnotation == true
+ *
+ * 1. KDocSampleLinkDetector, which enforces that any samples referenced from a @sample tag in KDoc
+ * are correctly annotated with @Sampled
+ *
+ * 2. SampledAnnotationDetector, which enforces that any sample functions annotated with @Sampled
+ * are linked to from KDoc in the parent module
+ *
+ * These lint checks make some assumptions about directory / module structure, and supports two
+ * such setups:
+ *
+ * 1. Module foo which has a 'samples' dir/module inside it
+ * 2. Module foo which has a 'samples' dir/module alongside it
+ *
+ * There are also some other tangentially related lint issues that can be reported, to ensure sample
+ * correctness:
+ *
+ * 1. Missing samples directory
+ * 2. No functions found matching a @sample link
+ * 3. Multiple functions found matching a @sample link
+ * 4. Function annotated with @Sampled does not live in a valid samples directory
+ */
+class SampledAnnotationEnforcer {
+
+ companion object {
+ // The name of the @sample tag in KDoc
+ const val SAMPLE_KDOC_ANNOTATION = "sample"
+ // The name of the @Sampled annotation that samples must be annotated with
+ const val SAMPLED_ANNOTATION = "Sampled"
+ // The name of the samples directory inside a project
+ const val SAMPLES_DIRECTORY = "samples"
+
+ val MISSING_SAMPLED_ANNOTATION = Issue.create(
+ "EnforceSampledAnnotation",
+ "Missing @$SAMPLED_ANNOTATION annotation",
+ "Functions referred to from KDoc with a @$SAMPLE_KDOC_ANNOTATION tag must " +
+ "be annotated with @$SAMPLED_ANNOTATION, to provide visibility at the sample " +
+ "site and ensure that it doesn't get changed accidentally.",
+ Category.CORRECTNESS, 5, Severity.ERROR,
+ Implementation(KDocSampleLinkDetector::class.java, Scope.JAVA_FILE_SCOPE)
+ )
+
+ val OBSOLETE_SAMPLED_ANNOTATION = Issue.create(
+ "EnforceSampledAnnotation",
+ "Obsolete @$SAMPLED_ANNOTATION annotation",
+ "This function is annotated with @$SAMPLED_ANNOTATION, but is not linked to " +
+ "from a @$SAMPLE_KDOC_ANNOTATION tag. Either remove this annotation, or add " +
+ "a valid @$SAMPLE_KDOC_ANNOTATION tag linking to it.",
+ Category.CORRECTNESS, 5, Severity.ERROR,
+ Implementation(SampledAnnotationDetector::class.java, Scope.JAVA_FILE_SCOPE)
+ )
+
+ val MISSING_SAMPLES_DIRECTORY = Issue.create(
+ "EnforceSampledAnnotation",
+ "Missing $SAMPLES_DIRECTORY directory",
+ "Couldn't find a valid $SAMPLES_DIRECTORY directory in this project.",
+ Category.CORRECTNESS, 5, Severity.ERROR,
+ Implementation(SampledAnnotationDetector::class.java, Scope.JAVA_FILE_SCOPE)
+ )
+
+ val UNRESOLVED_SAMPLE_LINK = Issue.create(
+ "EnforceSampledAnnotation",
+ "Unresolved @$SAMPLE_KDOC_ANNOTATION annotation",
+ "Couldn't find a valid function matching the function specified in the " +
+ "$SAMPLE_KDOC_ANNOTATION link.",
+ Category.CORRECTNESS, 5, Severity.ERROR,
+ Implementation(SampledAnnotationDetector::class.java, Scope.JAVA_FILE_SCOPE)
+ )
+
+ val MULTIPLE_FUNCTIONS_FOUND = Issue.create(
+ "EnforceSampledAnnotation",
+ "Multiple matching functions found",
+ "Found multiple functions matching the $SAMPLE_KDOC_ANNOTATION link.",
+ Category.CORRECTNESS, 5, Severity.ERROR,
+ Implementation(SampledAnnotationDetector::class.java, Scope.JAVA_FILE_SCOPE)
+ )
+
+ val INVALID_SAMPLES_LOCATION = Issue.create(
+ "EnforceSampledAnnotation",
+ "Invalid samples location",
+ "This function is annotated with @$SAMPLED_ANNOTATION, but is not inside a" +
+ "project/directory named $SAMPLES_DIRECTORY.",
+ Category.CORRECTNESS, 5, Severity.ERROR,
+ Implementation(SampledAnnotationDetector::class.java, Scope.JAVA_FILE_SCOPE)
+ )
+ }
+
+ /**
+ * Enforces that any @sample links in KDoc link to a function that is annotated with @Sampled
+ *
+ * Checks KDoc in all applicable UDeclarations - this includes classes, functions, fields...
+ *
+ * Also reports issues if there is no samples directory found, if there is no function matching
+ * a given @sample link, and if there are multiple functions matching a given @sample link.
+ */
+ class KDocSampleLinkDetector : Detector(), SourceCodeScanner {
+ // Cache containing every function inside a project's corresponding samples dir
+ private var sampleFunctionCache: List<KtNamedFunction>? = null
+
+ /**
+ * @return a list of all the functions found in the samples directory for this project, or
+ * `null` if there is no valid samples directory for this project
+ */
+ internal fun getSampleFunctionCache(context: JavaContext): List<KtNamedFunction>? {
+ if (sampleFunctionCache == null) {
+ val sampleDirectory = findSampleDirectory(context)
+ sampleFunctionCache = if (sampleDirectory == null) {
+ null
+ } else {
+ val allKtFiles = sampleDirectory.getAllKtFiles()
+ // Remove any functions without a valid fully qualified name, this includes
+ // things such as overridden functions in anonymous classes like a Runnable
+ allKtFiles.flatMap { it.getAllFunctions() }.filter {
+ it.fqName != null
+ }
+ }
+ }
+ return sampleFunctionCache
+ }
+
+ override fun getApplicableUastTypes(): List<Class<out UElement>>? =
+ listOf(UDeclaration::class.java)
+
+ override fun createUastHandler(context: JavaContext): UElementHandler? =
+ KDocSampleLinkHandler(context)
+
+ /**
+ * Clear caches before and after a project run, as they are only relevant per project
+ *
+ * Note: this isn't strictly needed, as normally a new detector will be instantiated per
+ * project, but if lint is set to run on dependencies, the same detector will end up
+ * being reused, and we can run into some caching issues. Safer just to always clear here as
+ * we really want to avoid false positives.
+ */
+ override fun beforeCheckEachProject(context: Context) {
+ sampleFunctionCache = null
+ }
+
+ override fun afterCheckEachProject(context: Context) {
+ sampleFunctionCache = null
+ }
+
+ private inner class KDocSampleLinkHandler(
+ private val context: JavaContext
+ ) : UElementHandler() {
+
+ // Cache containing all the KDoc elements we have found inside this Handler (created
+ // once per JavaContext - Java/Kotlin source file)
+ private val kdocCache = mutableListOf<KDoc>()
+
+ override fun visitDeclaration(node: UDeclaration) {
+ node.findKdoc()?.let { kdoc ->
+ // It's possible for different declarations to point to the same KDoc - for
+ // example if a class has some KDoc, we will visit it once for the constructor
+ // function and once for the class itself. If we have seen this KDoc before
+ // just skip it so we don't report the issue multiple times
+ if (kdoc !in kdocCache) {
+ kdocCache.add(kdoc)
+ handleSampleLink(kdoc, node.namedUnwrappedElement!!.name!!)
+ }
+ }
+ }
+
+ private fun handleSampleLink(kdoc: KDoc, sourceNodeName: String) {
+ val sections: List<KDocSection> = kdoc.children.mapNotNull { it as? KDocSection }
+
+ // map of a KDocTag (which contains the location used when reporting issues) to the
+ // method link specified in @sample
+ val sampleTags = sections.flatMap { section ->
+ section.findTagsByName(SAMPLE_KDOC_ANNOTATION)
+ .mapNotNull { sampleTag ->
+ val linkText = sampleTag.getSubjectLink()?.getLinkText()
+ if (linkText == null) {
+ null
+ } else {
+ sampleTag to linkText
+ }
+ }
+ }.distinct()
+
+ sampleTags.forEach { (docTag, link) ->
+
+ val allFunctions = getSampleFunctionCache(context)
+
+ if (allFunctions == null) {
+ context.report(
+ MISSING_SAMPLES_DIRECTORY,
+ docTag,
+ context.getNameLocation(docTag),
+ "Couldn't find a valid $SAMPLES_DIRECTORY directory in this project"
+ )
+ return@forEach
+ }
+
+ // We filtered out not-null fqNames when building the cache, so safe to !!
+ val matchingFunctions = allFunctions.filter {
+ link == it.fqName!!.asString()
+ }
+
+ when (matchingFunctions.size) {
+ 0 -> {
+ context.report(
+ UNRESOLVED_SAMPLE_LINK,
+ docTag,
+ context.getNameLocation(docTag),
+ "Couldn't find a valid function matching $link"
+ )
+ }
+ 1 -> {
+ val function = matchingFunctions.first()
+ if (!function.hasSampledAnnotation()) {
+ context.report(
+ MISSING_SAMPLED_ANNOTATION,
+ docTag,
+ context.getNameLocation(docTag),
+ "${function.name} is not annotated with @$SAMPLED_ANNOTATION" +
+ ", but is linked to from the KDoc of $sourceNodeName"
+ )
+ }
+ }
+ else -> {
+ context.report(
+ MULTIPLE_FUNCTIONS_FOUND,
+ docTag,
+ context.getNameLocation(docTag),
+ "Found multiple functions matching $link"
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks all functions annotated with @Sampled to ensure that they are linked from KDoc
+ *
+ * Also reports an issue if a function is annotated with @Sampled, and does not live in a
+ * valid samples directory/module.
+ */
+ class SampledAnnotationDetector : Detector(), SourceCodeScanner {
+ // Cache containing every link referenced from a @sample tag inside the parent project
+ private var sampleLinkCache: List<String>? = null
+
+ internal fun getSampleLinkCache(context: JavaContext): List<String> {
+ if (sampleLinkCache == null) {
+ sampleLinkCache = buildSampleLinkCache(context)
+ }
+ return sampleLinkCache!!
+ }
+
+ override fun getApplicableUastTypes(): List<Class<out UElement>>? =
+ listOf(UMethod::class.java)
+
+ override fun createUastHandler(context: JavaContext): UElementHandler? =
+ SampledAnnotationHandler(context)
+
+ /**
+ * Clear caches before and after a project run, as they are only relevant per project
+ *
+ * Note: this isn't strictly needed, as normally a new detector will be instantiated per
+ * project, but if lint is set to run on dependencies, the same detector will end up
+ * being reused, and we can run into some caching issues. Safer just to always clear here as
+ * we really want to avoid false positives.
+ */
+ override fun beforeCheckEachProject(context: Context) {
+ sampleLinkCache = null
+ }
+
+ override fun afterCheckEachProject(context: Context) {
+ sampleLinkCache = null
+ }
+
+ private inner class SampledAnnotationHandler(
+ private val context: JavaContext
+ ) : UElementHandler() {
+
+ override fun visitMethod(node: UMethod) {
+ val element = (node.sourceElement as? KtDeclaration) ?: return
+
+ if (element.annotationEntries.any {
+ it.shortName.safeNameForLazyResolve().identifier == SAMPLED_ANNOTATION
+ }) {
+ handleSampleCode(element, node)
+ }
+ }
+
+ private fun handleSampleCode(function: KtDeclaration, node: UMethod) {
+ val currentPath = context.psiFile!!.virtualFile.path
+
+ if (SAMPLES_DIRECTORY !in currentPath) {
+ context.report(
+ INVALID_SAMPLES_LOCATION,
+ node,
+ context.getNameLocation(node),
+ "${function.name} in $currentPath is annotated with @$SAMPLED_ANNOTATION" +
+ ", but is not inside a project/directory named $SAMPLES_DIRECTORY."
+ )
+ return
+ }
+
+ // The package name of the file we are in
+ val parentFqName = function.containingKtFile.packageFqName.asString()
+ // The full name of the current function that will be referenced in a @sample tag
+ val fullFqName = "$parentFqName.${function.name}"
+
+ if (getSampleLinkCache(context).none { it == fullFqName }) {
+ context.report(
+ OBSOLETE_SAMPLED_ANNOTATION,
+ node,
+ context.getNameLocation(node),
+ "${function.name} is annotated with @$SAMPLED_ANNOTATION, but is not " +
+ "linked to from a @$SAMPLE_KDOC_ANNOTATION tag."
+ )
+ }
+ }
+ }
+
+ /**
+ * At this point we are inside some sample module, which is depending on a module that
+ * would end up referencing the sample
+ *
+ * For example, we could be in :foo:integration-tests:sample, and we want to find the
+ * path for module :foo
+ */
+ private fun buildSampleLinkCache(context: JavaContext): List<String> {
+ val currentProjectPath = context.project.dir.absolutePath
+
+ // The paths of every module the current module depends on
+ val dependenciesPathList = context.project.directLibraries.map {
+ it.dir.absolutePath
+ }
+
+ // Try and find a common path, i.e if we are in a/b/foo/integration-tests/sample, we
+ // will match a/b/foo for the parent
+ var parentProjectPath = dependenciesPathList.find {
+ currentProjectPath.startsWith(it)
+ }
+
+ // If we haven't found a path, it might be that we are on the same top level, i.e
+ // we are in a/b/foo/integration-tests/sample, and the module is in a/b/foo/foo-xyz
+ // Try matching with the parent directory of each module.
+ parentProjectPath = parentProjectPath ?: dependenciesPathList.find {
+ currentProjectPath.startsWith(File(it).parent)
+ }
+
+ // There is no dependent module that exists above us, or alongside us, so throw
+ if (parentProjectPath == null) {
+ throw IllegalStateException("Couldn't find a parent project for " +
+ currentProjectPath
+ )
+ }
+
+ val parentProjectDirectory = navigateToDirectory(context, parentProjectPath)
+
+ return parentProjectDirectory.getAllKtFiles().flatMap { file ->
+ file.findAllSampleLinks()
+ }
+ }
+ }
+}
+
+/**
+ * @return the KDoc for the given section, or `null` if there is no corresponding KDoc
+ *
+ * This also filters out non-Kotlin declarations, so we don't bother looking for KDoc inside
+ * Java nodes for example.
+ */
+internal fun UDeclaration.findKdoc(): KDoc? {
+ // Unfortunate workaround as the KDoc cannot be returned from the node directly
+ // https://youtrack.jetbrains.com/issue/KT-22135
+ val ktDeclaration = sourceElement as? KtDeclaration ?: return null
+ return findDocComment(ktDeclaration)
+}
+
+/**
+ * @return whether this function is annotated with @Sampled
+ */
+internal fun KtNamedFunction.hasSampledAnnotation(): Boolean {
+ return modifierList?.annotationEntries?.any { annotation ->
+ annotation.shortName.safeNameForLazyResolve().identifier == SAMPLED_ANNOTATION
+ } ?: false
+}
+
+/**
+ * @return a list of all sample links found recursively inside the element
+ */
+internal fun PsiElement.findAllSampleLinks(): List<String> {
+ val sampleLinks = mutableListOf<String>()
+ if (this is KDoc) {
+ val sections: List<KDocSection> = this.children.mapNotNull { it as? KDocSection }
+
+ sections.forEach { section ->
+ section.findTagsByName(SAMPLE_KDOC_ANNOTATION).forEach { sampleTag ->
+ sampleTag.getSubjectLink()?.getLinkText()?.let { sampleLinks.add(it) }
+ }
+ }
+ }
+ children.forEach { sampleLinks.addAll(it.findAllSampleLinks()) }
+ return sampleLinks
+}
+
+/**
+ * @return a list of all files found recursively inside the directory
+ */
+internal fun PsiDirectory.getAllKtFiles(): List<PsiFile> {
+ val psiFiles = mutableListOf<PsiFile>()
+ accept(object : KtTreeVisitorVoid() {
+ override fun visitFile(file: PsiFile?) {
+ psiFiles.addIfNotNull(file as? KtFile)
+ }
+ })
+ return psiFiles
+}
+
+/**
+ * @return a list of all functions found recursively inside the element
+ */
+internal fun PsiElement.getAllFunctions(): List<KtNamedFunction> {
+ val functions = mutableListOf<KtNamedFunction>()
+ accept(object : KtTreeVisitorVoid() {
+ override fun visitDeclaration(dcl: KtDeclaration) {
+ functions.addIfNotNull(dcl as? KtNamedFunction)
+ }
+ })
+ return functions
+}
+
+/**
+ * @return the samples directory if it exists, otherwise returns null
+ *
+ * The samples directory could either be a direct child of the current module, or a
+ * sibling directory
+ *
+ * For example, if we are in a/b/foo, the samples directory could either be:
+ * a/b/foo/.../samples
+ * a/b/.../samples
+ *
+ * For efficiency, first we look inside a/b/foo, and then if that fails we look
+ * inside a/b
+ */
+internal fun findSampleDirectory(context: JavaContext): PsiDirectory? {
+ val currentProjectPath = context.project.dir.absolutePath
+ val currentProjectDir = navigateToDirectory(context, currentProjectPath)
+ fun PsiDirectory.searchForSampleDirectory(): PsiDirectory? {
+ if (name == SAMPLES_DIRECTORY) {
+ return this
+ }
+ subdirectories.forEach {
+ val dir = it.searchForSampleDirectory()
+ if (dir != null) {
+ return dir
+ }
+ }
+ return null
+ }
+
+ // Look inside a/b/foo
+ var sampleDir = currentProjectDir.searchForSampleDirectory()
+
+ // Try looking inside /a/b
+ if (sampleDir == null) {
+ sampleDir = currentProjectDir.parent!!.searchForSampleDirectory()
+ }
+
+ return sampleDir
+}
+
+/**
+ * @return the directory with the given [path], using [context] to get the current filesystem
+ */
+internal fun navigateToDirectory(context: JavaContext, path: String): PsiDirectory {
+ val file = context.psiFile
+ ?: throw IllegalStateException("Not linting a source file")
+ val filesystem = file.virtualFile.fileSystem
+ val virtualFile = filesystem.findFileByPath(path)
+ return file.manager.findDirectory(virtualFile!!)
+ ?: throw IllegalStateException("Couldn't find directory for $path")
+}
diff --git a/buildSrc/lint-checks/src/test/java/androidx/build/lint/SampledAnnotationEnforcerTest.kt b/buildSrc/lint-checks/src/test/java/androidx/build/lint/SampledAnnotationEnforcerTest.kt
new file mode 100644
index 0000000..96af3d6
--- /dev/null
+++ b/buildSrc/lint-checks/src/test/java/androidx/build/lint/SampledAnnotationEnforcerTest.kt
@@ -0,0 +1,530 @@
+/*
+ * 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.
+ */
+
+@file:Suppress("KDocUnresolvedReference")
+
+package androidx.build.lint
+
+import com.android.tools.lint.checks.infrastructure.ProjectDescription
+import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.lint.checks.infrastructure.TestFiles.kotlin
+import com.android.tools.lint.checks.infrastructure.TestLintResult
+import com.android.tools.lint.checks.infrastructure.TestLintTask.lint
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.junit.runners.Parameterized.Parameter
+import org.junit.runners.Parameterized.Parameters
+
+/**
+ * Test for [SampledAnnotationEnforcer]
+ *
+ * This tests (with Parameterized) the two following module setups:
+ *
+ * Module 'foo', which lives in foo
+ * Module 'foo:integration-tests:samples', which lives in foo/integration-tests/samples,
+ * and depends on 'foo'
+ *
+ * Module 'foo:foo', which lives in foo/foo
+ * Module 'foo:integration-tests:samples', which lives in foo/integration-tests/samples,
+ * and depends on 'foo:foo'
+ */
+@RunWith(Parameterized::class)
+class SampledAnnotationEnforcerTest {
+
+ companion object {
+ @JvmStatic
+ @Parameters(name = "sourceModule={0}")
+ fun moduleNames(): Array<String> {
+ return arrayOf("foo", "foo:foo")
+ }
+ }
+
+ // At runtime this contains one of the values listed in moduleNames()
+ @Parameter lateinit var fooModuleName: String
+
+ private val sampleModuleName = "foo:integration-tests:samples"
+
+ // The path to Bar.kt changes depending on what module we are in
+ private val barFilePath by lazy {
+ val prefix = if (fooModuleName == moduleNames()[0]) "" else "foo:foo/"
+ prefix + "src/foo/Bar.kt"
+ }
+
+ private val emptySampleFile = kotlin("""
+ package foo.samples
+ """)
+
+ private val unannotatedSampleFile = kotlin("""
+ package foo.samples
+
+ fun sampleBar() {}
+ """)
+
+ private val multipleMatchingSampleFile = kotlin("""
+ package foo.samples
+
+ fun sampleBar() {}
+
+ fun sampleBar() {}
+ """)
+
+ private val correctlyAnnotatedSampleFile = kotlin("""
+ package foo.samples
+
+ @Sampled
+ fun sampleBar() {}
+ """)
+
+ private fun checkKotlin(
+ fooFile: TestFile? = null,
+ sampleFile: TestFile? = null,
+ sampleModuleNameOverride: String? = null
+ ): TestLintResult {
+ val fooProject = ProjectDescription().apply {
+ name = fooModuleName
+ fooFile?.let { files = arrayOf(fooFile) }
+ }
+ val sampleProject = ProjectDescription().apply {
+ name = sampleModuleNameOverride ?: sampleModuleName
+ sampleFile?.let { files = arrayOf(sampleFile) }
+ dependsOn(fooProject)
+ }
+ return lint()
+ .projects(fooProject, sampleProject)
+ .allowMissingSdk(true)
+ .issues(
+ SampledAnnotationEnforcer.MISSING_SAMPLED_ANNOTATION,
+ SampledAnnotationEnforcer.OBSOLETE_SAMPLED_ANNOTATION,
+ SampledAnnotationEnforcer.MISSING_SAMPLES_DIRECTORY,
+ SampledAnnotationEnforcer.UNRESOLVED_SAMPLE_LINK,
+ SampledAnnotationEnforcer.MULTIPLE_FUNCTIONS_FOUND,
+ SampledAnnotationEnforcer.INVALID_SAMPLES_LOCATION
+ )
+ .run()
+ }
+
+ @Test
+ fun orphanedSampleFunction() {
+ val sampleFile = correctlyAnnotatedSampleFile
+
+ val path = if (fooModuleName == moduleNames()[0]) { "" } else { "foo" }
+
+ val expected =
+"$path:integration-tests:samples/src/foo/samples/test.kt:5: Error: sampleBar is annotated with" +
+""" @Sampled, but is not linked to from a @sample tag. [EnforceSampledAnnotation]
+ fun sampleBar() {}
+ ~~~~~~~~~
+1 errors, 0 warnings
+ """
+
+ checkKotlin(sampleFile = sampleFile)
+ .expect(expected)
+ }
+
+ @Test
+ fun invalidSampleLocation() {
+ val sampleFile = kotlin("""
+ package foo.wrong.location
+
+ @Sampled
+ fun sampleBar() {}
+ """)
+
+ val path = if (fooModuleName == moduleNames()[0]) { "" } else { "foo" }
+
+ val expected =
+"$path:integration-tests:wrong-location/src/foo/wrong/location/test.kt:5: Error: sampleBar in " +
+"/TESTROOT/foo:integration-tests:wrong-location/src/foo/wrong/location/test.kt is annotated " +
+"""with @Sampled, but is not inside a project/directory named samples. [EnforceSampledAnnotation]
+ fun sampleBar() {}
+ ~~~~~~~~~
+1 errors, 0 warnings
+ """
+
+ checkKotlin(
+ sampleFile = sampleFile,
+ sampleModuleNameOverride = "foo:integration-tests:wrong-location"
+ ).expect(expected)
+ }
+
+ @Test
+ fun missingSampleDirectory_Function() {
+ val fooFile = kotlin("""
+ package foo
+
+ class Bar {
+ /**
+ * @sample foo.samples.sampleBar
+ */
+ fun bar() {}
+ }
+ """)
+
+ val expected =
+ "$barFilePath:6: Error: Couldn't find a valid samples directory in this project" +
+""" [EnforceSampledAnnotation]
+ * @sample foo.samples.sampleBar
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+1 errors, 0 warnings
+ """
+
+ checkKotlin(fooFile = fooFile)
+ .expect(expected)
+ }
+
+ @Test
+ fun unresolvedSampleLink_Function() {
+ val fooFile = kotlin("""
+ package foo
+
+ class Bar {
+ /**
+ * @sample foo.samples.sampleBar
+ */
+ fun bar() {}
+ }
+ """)
+
+ val sampleFile = emptySampleFile
+
+ val expected =
+"$barFilePath:6: Error: Couldn't find a valid function matching foo.samples.sampleBar" +
+""" [EnforceSampledAnnotation]
+ * @sample foo.samples.sampleBar
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+1 errors, 0 warnings
+ """
+
+ checkKotlin(fooFile = fooFile, sampleFile = sampleFile)
+ .expect(expected)
+ }
+
+ @Test
+ fun unannotatedSampleFunction_Function() {
+ val fooFile = kotlin("""
+ package foo
+
+ class Bar {
+ /**
+ * @sample foo.samples.sampleBar
+ */
+ fun bar() {}
+ }
+ """)
+
+ val sampleFile = unannotatedSampleFile
+
+ val path = if (fooModuleName == moduleNames()[0]) { "" } else { "foo:foo/" }
+
+ val expected =
+"${path}src/foo/Bar.kt:6: Error: sampleBar is not annotated with @Sampled, but is linked to from" +
+""" the KDoc of bar [EnforceSampledAnnotation]
+ * @sample foo.samples.sampleBar
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+1 errors, 0 warnings
+ """
+
+ checkKotlin(fooFile = fooFile, sampleFile = sampleFile)
+ .expect(expected)
+ }
+
+ @Test
+ fun multipleMatchingSampleFunctions_Function() {
+ val fooFile = kotlin("""
+ package foo
+
+ class Bar {
+ /**
+ * @sample foo.samples.sampleBar
+ */
+ fun bar() {}
+ }
+ """)
+
+ val sampleFile = multipleMatchingSampleFile
+
+ val expected =
+"$barFilePath:6: Error: Found multiple functions matching foo.samples.sampleBar" +
+""" [EnforceSampledAnnotation]
+ * @sample foo.samples.sampleBar
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+1 errors, 0 warnings
+ """
+
+ checkKotlin(fooFile = fooFile, sampleFile = sampleFile)
+ .expect(expected)
+ }
+
+ @Test
+ fun correctlyAnnotatedSampleFunction_Function() {
+ val fooFile = kotlin("""
+ package foo
+
+ class Bar {
+ /**
+ * @sample foo.samples.sampleBar
+ */
+ fun bar() {}
+ }
+ """)
+
+ val sampleFile = correctlyAnnotatedSampleFile
+
+ checkKotlin(fooFile = fooFile, sampleFile = sampleFile)
+ .expectClean()
+ }
+
+ @Test
+ fun missingSampleDirectory_Class() {
+ val fooFile = kotlin("""
+ package foo
+
+ /**
+ * @sample foo.samples.sampleBar
+ */
+ class Bar {}
+ """)
+
+ val expected =
+"$barFilePath:5: Error: Couldn't find a valid samples directory in this project" +
+""" [EnforceSampledAnnotation]
+ * @sample foo.samples.sampleBar
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+1 errors, 0 warnings
+ """
+
+ checkKotlin(fooFile = fooFile)
+ .expect(expected)
+ }
+
+ @Test
+ fun unresolvedSampleLink_Class() {
+ val fooFile = kotlin("""
+ package foo
+
+ /**
+ * @sample foo.samples.sampleBar
+ */
+ class Bar {}
+ """)
+
+ val sampleFile = emptySampleFile
+
+ val expected =
+"$barFilePath:5: Error: Couldn't find a valid function matching foo.samples.sampleBar" +
+""" [EnforceSampledAnnotation]
+ * @sample foo.samples.sampleBar
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+1 errors, 0 warnings
+ """
+
+ checkKotlin(fooFile = fooFile, sampleFile = sampleFile)
+ .expect(expected)
+ }
+
+ @Test
+ fun unannotatedSampleFunction_Class() {
+ val fooFile = kotlin("""
+ package foo
+
+ /**
+ * @sample foo.samples.sampleBar
+ */
+ class Bar {}
+ """)
+
+ val sampleFile = unannotatedSampleFile
+
+ val expected =
+"$barFilePath:5: Error: sampleBar is not annotated with @Sampled, but is linked to from" +
+""" the KDoc of Bar [EnforceSampledAnnotation]
+ * @sample foo.samples.sampleBar
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+1 errors, 0 warnings
+ """
+
+ checkKotlin(fooFile = fooFile, sampleFile = sampleFile)
+ .expect(expected)
+ }
+
+ @Test
+ fun multipleMatchingSampleFunctions_Class() {
+ val fooFile = kotlin("""
+ package foo
+
+ /**
+ * @sample foo.samples.sampleBar
+ */
+ class Bar {}
+ """)
+
+ val sampleFile = multipleMatchingSampleFile
+
+ val expected =
+"$barFilePath:5: Error: Found multiple functions matching foo.samples.sampleBar" +
+""" [EnforceSampledAnnotation]
+ * @sample foo.samples.sampleBar
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+1 errors, 0 warnings
+ """
+
+ checkKotlin(fooFile = fooFile, sampleFile = sampleFile)
+ .expect(expected)
+ }
+
+ @Test
+ fun correctlyAnnotatedSampleFunction_Class() {
+ val fooFile = kotlin("""
+ package foo
+
+ /**
+ * @sample foo.samples.sampleBar
+ */
+ class Bar {}
+ """)
+
+ val sampleFile = correctlyAnnotatedSampleFile
+
+ checkKotlin(fooFile = fooFile, sampleFile = sampleFile)
+ .expectClean()
+ }
+
+ @Test
+ fun missingSampleDirectory_Field() {
+ val fooFile = kotlin("""
+ package foo
+
+ class Bar {
+ /**
+ * @sample foo.samples.sampleBar
+ */
+ const val bar = 0
+ }
+ """)
+
+ val expected =
+"$barFilePath:6: Error: Couldn't find a valid samples directory in this project" +
+""" [EnforceSampledAnnotation]
+ * @sample foo.samples.sampleBar
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+1 errors, 0 warnings
+ """
+
+ checkKotlin(fooFile = fooFile)
+ .expect(expected)
+ }
+
+ @Test
+ fun unresolvedSampleLink_Field() {
+ val fooFile = kotlin("""
+ package foo
+
+ class Bar {
+ /**
+ * @sample foo.samples.sampleBar
+ */
+ const val bar = 0
+ }
+ """)
+
+ val sampleFile = emptySampleFile
+
+ val expected =
+"$barFilePath:6: Error: Couldn't find a valid function matching foo.samples.sampleBar" +
+""" [EnforceSampledAnnotation]
+ * @sample foo.samples.sampleBar
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+1 errors, 0 warnings
+ """
+
+ checkKotlin(fooFile = fooFile, sampleFile = sampleFile)
+ .expect(expected)
+ }
+
+ @Test
+ fun unannotatedSampleFunction_Field() {
+ val fooFile = kotlin("""
+ package foo
+
+ class Bar {
+ /**
+ * @sample foo.samples.sampleBar
+ */
+ const val bar = 0
+ }
+ """)
+
+ val sampleFile = unannotatedSampleFile
+
+ val expected =
+"$barFilePath:6: Error: sampleBar is not annotated with @Sampled, but is linked to from" +
+""" the KDoc of bar [EnforceSampledAnnotation]
+ * @sample foo.samples.sampleBar
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+1 errors, 0 warnings
+ """
+
+ checkKotlin(fooFile = fooFile, sampleFile = sampleFile)
+ .expect(expected)
+ }
+
+ @Test
+ fun multipleMatchingSampleFunctions_Field() {
+ val fooFile = kotlin("""
+ package foo
+
+ class Bar {
+ /**
+ * @sample foo.samples.sampleBar
+ */
+ const val bar = 0
+ }
+ """)
+
+ val sampleFile = multipleMatchingSampleFile
+
+ val expected =
+"$barFilePath:6: Error: Found multiple functions matching foo.samples.sampleBar" +
+""" [EnforceSampledAnnotation]
+ * @sample foo.samples.sampleBar
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+1 errors, 0 warnings
+ """
+
+ checkKotlin(fooFile = fooFile, sampleFile = sampleFile)
+ .expect(expected)
+ }
+
+ @Test
+ fun correctlyAnnotatedSampleFunction_Field() {
+ val fooFile = kotlin("""
+ package foo
+
+ class Bar {
+ /**
+ * @sample foo.samples.sampleBar
+ */
+ const val bar = 0
+ }
+ """)
+
+ val sampleFile = correctlyAnnotatedSampleFile
+
+ checkKotlin(fooFile = fooFile, sampleFile = sampleFile)
+ .expectClean()
+ }
+}
diff --git a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
index cf15fc0..d0c7e6b 100644
--- a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
@@ -84,7 +84,8 @@
val PERSISTENCE = Version("2.0.1")
val PREFERENCE = Version("1.1.0-rc01")
val RECOMMENDATION = Version("1.1.0-alpha01")
- val RECYCLERVIEW = Version("1.1.0-alpha07")
+ val RECYCLERVIEW = Version("1.1.0-beta01")
+ val RECYCLERVIEW_SELECTION = Version("1.1.0-alpha07")
val REMOTECALLBACK = Version("1.0.0-alpha02")
val ROOM = Version("2.2.0-alpha01")
val SAVEDSTATE = Version("1.0.0-rc01")
diff --git a/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt b/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt
index adc1007..99f9209 100644
--- a/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt
@@ -29,7 +29,7 @@
*/
val RELEASE_RULE = docsRules("public", false) {
prebuilts(LibraryGroups.ACTIVITY, "1.0.0-beta01")
- prebuilts(LibraryGroups.ANNOTATION, "1.1.0")
+ prebuilts(LibraryGroups.ANNOTATION, "annotation", "1.1.0")
prebuilts(LibraryGroups.APPCOMPAT, "1.1.0-beta01")
prebuilts(LibraryGroups.ARCH_CORE, "2.1.0-rc01")
prebuilts(LibraryGroups.ASYNCLAYOUTINFLATER, "1.0.0")
diff --git a/buildSrc/src/main/kotlin/androidx/build/checkapi/ApiLocation.kt b/buildSrc/src/main/kotlin/androidx/build/checkapi/ApiLocation.kt
index 31ccc32..4002616 100644
--- a/buildSrc/src/main/kotlin/androidx/build/checkapi/ApiLocation.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/checkapi/ApiLocation.kt
@@ -23,6 +23,8 @@
// An ApiLocation contains the filepath of a public API and restricted API of a library
data class ApiLocation(
+ // file specifying the directory of API files for the library
+ val apiFileDirectory: File,
// file specifying the public API of the library
val publicApiFile: File,
// file specifying the restricted API (marked by the RestrictTo annotation) of the library
@@ -44,6 +46,7 @@
companion object {
fun fromPublicApiFile(f: File): ApiLocation {
return ApiLocation(
+ f.parentFile,
f,
File(f.parentFile, "restricted_" + f.name),
File(f.parentFile, "res-" + f.name)
@@ -52,21 +55,28 @@
}
}
-// An ApiViolationExclusions contains the paths of the API exclusions files for an API
-data class ApiViolationExclusions(
+// An ApiViolationBaselines contains the paths of the API baselines files for an API
+data class ApiViolationBaselines(
val publicApiFile: File,
- val restrictedApiFile: File
+ val restrictedApiFile: File,
+ val apiLintFile: File
) : Serializable {
fun files() = listOf(publicApiFile, restrictedApiFile)
companion object {
- fun fromApiLocation(apiLocation: ApiLocation): ApiViolationExclusions {
- val publicExclusionsFile =
+ fun fromApiLocation(apiLocation: ApiLocation): ApiViolationBaselines {
+ val publicBaselineFile =
File(apiLocation.publicApiFile.toString().removeSuffix(".txt") + ".ignore")
- val restrictedExclusionsFile =
+ val restrictedBaselineFile =
File(apiLocation.restrictedApiFile.toString().removeSuffix(".txt") + ".ignore")
- return ApiViolationExclusions(publicExclusionsFile, restrictedExclusionsFile)
+ val apiLintBaselineFile =
+ File(apiLocation.apiFileDirectory, "api_lint.ignore")
+ return ApiViolationBaselines(
+ publicBaselineFile,
+ restrictedBaselineFile,
+ apiLintBaselineFile
+ )
}
}
}
diff --git a/buildSrc/src/main/kotlin/androidx/build/metalava/CheckApiCompatibilityTask.kt b/buildSrc/src/main/kotlin/androidx/build/metalava/CheckApiCompatibilityTask.kt
index 3cbc3c2..0481504 100644
--- a/buildSrc/src/main/kotlin/androidx/build/metalava/CheckApiCompatibilityTask.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/metalava/CheckApiCompatibilityTask.kt
@@ -17,7 +17,7 @@
package androidx.build.metalava
import androidx.build.checkapi.ApiLocation
-import androidx.build.checkapi.ApiViolationExclusions
+import androidx.build.checkapi.ApiViolationBaselines
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFiles
@@ -32,7 +32,7 @@
abstract val referenceApi: Property<ApiLocation>
// Text file listing violations that should be ignored
@get:Input
- abstract val exclusions: Property<ApiViolationExclusions>
+ abstract val baselines: Property<ApiViolationBaselines>
// Whether to confirm that no restricted APIs were removed since the previous release
@get:Input
@@ -41,9 +41,9 @@
@InputFiles
fun getTaskInputs(): List<File> {
if (checkRestrictedAPIs) {
- return referenceApi.get().files() + exclusions.get().files()
+ return referenceApi.get().files() + baselines.get().files()
}
- return listOf(referenceApi.get().publicApiFile, exclusions.get().publicApiFile)
+ return listOf(referenceApi.get().publicApiFile, baselines.get().publicApiFile)
}
// Declaring outputs prevents Gradle from rerunning this task if the inputs haven't changed
@@ -56,17 +56,17 @@
fun exec() {
check(bootClasspath.isNotEmpty()) { "Android boot classpath not set." }
- checkApiFile(referenceApi.get().publicApiFile, exclusions.get().publicApiFile, false)
+ checkApiFile(referenceApi.get().publicApiFile, baselines.get().publicApiFile, false)
if (checkRestrictedAPIs) {
checkApiFile(referenceApi.get().restrictedApiFile,
- exclusions.get().restrictedApiFile,
+ baselines.get().restrictedApiFile,
true)
}
}
// Confirms that the public API of this library (or the restricted API, if <checkRestrictedAPIs> is set
- // is compatible with <apiFile> except for any exclusions listed in <exclusionsFile>
- fun checkApiFile(apiFile: File, exclusionsFile: File, checkRestrictedAPIs: Boolean) {
+ // is compatible with <apiFile> except for any baselines listed in <baselineFile>
+ fun checkApiFile(apiFile: File, baselineFile: File, checkRestrictedAPIs: Boolean) {
var args = listOf("--classpath",
(bootClasspath + dependencyClasspath!!.files).joinToString(File.pathSeparator),
@@ -79,8 +79,8 @@
"--warnings-as-errors",
"--format=v3"
)
- if (exclusionsFile.exists()) {
- args = args + listOf("--baseline", exclusionsFile.toString())
+ if (baselineFile.exists()) {
+ args = args + listOf("--baseline", baselineFile.toString())
}
if (checkRestrictedAPIs) {
args = args + listOf("--show-annotation", "androidx.annotation.RestrictTo")
diff --git a/buildSrc/src/main/kotlin/androidx/build/metalava/GenerateApiTask.kt b/buildSrc/src/main/kotlin/androidx/build/metalava/GenerateApiTask.kt
index 65877a1..39598e3 100644
--- a/buildSrc/src/main/kotlin/androidx/build/metalava/GenerateApiTask.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/metalava/GenerateApiTask.kt
@@ -17,8 +17,11 @@
package androidx.build.metalava
import androidx.build.checkapi.ApiLocation
+import androidx.build.checkapi.ApiViolationBaselines
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.InputFile
+import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.OutputFiles
import org.gradle.api.tasks.TaskAction
import java.io.File
@@ -30,8 +33,18 @@
abstract val apiLocation: Property<ApiLocation>
@get:Input
+ abstract val baselines: Property<ApiViolationBaselines>
+
+ @get:Input
var generateRestrictedAPIs = false
+ @Optional
+ @InputFile
+ fun getApiLintBaseline(): File? {
+ val baseline = baselines.get().apiLintFile
+ return if (baseline.exists()) baseline else null
+ }
+
@OutputFiles
fun getTaskOutputs(): List<File>? {
if (generateRestrictedAPIs) {
@@ -52,7 +65,7 @@
dependencyClasspath,
sourcePaths,
apiLocation.get().publicApiFile,
- false
+ GenerateApiMode.PublicApi(baselines.get().apiLintFile)
)
if (generateRestrictedAPIs) {
@@ -61,7 +74,7 @@
dependencyClasspath,
sourcePaths,
apiLocation.get().restrictedApiFile,
- true
+ GenerateApiMode.RestrictedApi
)
}
}
diff --git a/buildSrc/src/main/kotlin/androidx/build/metalava/IgnoreApiChangesTask.kt b/buildSrc/src/main/kotlin/androidx/build/metalava/IgnoreApiChangesTask.kt
deleted file mode 100644
index 2550f78..0000000
--- a/buildSrc/src/main/kotlin/androidx/build/metalava/IgnoreApiChangesTask.kt
+++ /dev/null
@@ -1,119 +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.build.metalava
-
-import androidx.build.checkapi.ApiLocation
-import androidx.build.checkapi.ApiViolationExclusions
-import com.google.common.io.Files
-import org.gradle.api.provider.Property
-import org.gradle.api.tasks.Input
-import org.gradle.api.tasks.InputFiles
-import org.gradle.api.tasks.OutputFiles
-import org.gradle.api.tasks.TaskAction
-import java.io.File
-
-// Updates an API tracking exceptions file (api/*.*.*.ignore) to match the current set of violations
-abstract class IgnoreApiChangesTask : MetalavaTask() {
- // The API that the library is supposed to be compatible with
- @get:Input
- abstract val referenceApi: Property<ApiLocation>
-
- // The exclusions files (api/*.*.*.ignore) to update
- @get:Input
- abstract val exclusions: Property<ApiViolationExclusions>
-
- // Whether to update the file having restricted APIs too
- @get:Input
- var processRestrictedAPIs = false
-
- // Path of temporary api-changes exemptions file
- abstract val intermediateExclusionsFile: Property<File>
-
- @InputFiles
- fun getTaskInputs(): List<File> {
- if (processRestrictedAPIs) {
- return referenceApi.get().files()
- }
- return listOf(referenceApi.get().publicApiFile)
- }
-
- // Declaring outputs prevents Gradle from rerunning this task if the inputs haven't changed
- @OutputFiles
- fun getTaskOutputs(): List<File>? {
- if (processRestrictedAPIs) {
- return exclusions.get().files()
- }
- return listOf(exclusions.get().publicApiFile)
- }
-
- @TaskAction
- fun exec() {
- check(bootClasspath.isNotEmpty()) { "Android boot classpath not set." }
-
- updateExclusions(referenceApi.get().publicApiFile, exclusions.get().publicApiFile, false)
- if (processRestrictedAPIs && referenceApi.get().restrictedApiFile.exists()) {
- updateExclusions(referenceApi.get().restrictedApiFile,
- exclusions.get().restrictedApiFile,
- true)
- }
- }
-
- // Updates the contents of exclusionsFile to specify an exception for every API present in apiFile but not
- // present in the current source path
- fun updateExclusions(apiFile: File, exclusionsFile: File, processRestrictedAPIs: Boolean) {
- val intermediateExclusions = intermediateExclusionsFile.get()
- intermediateExclusions.parentFile.mkdirs()
- intermediateExclusions.createNewFile()
-
- var args = listOf("--classpath",
- (bootClasspath + dependencyClasspath!!.files).joinToString(File.pathSeparator),
-
- "--source-path",
- sourcePaths.filter { it.exists() }.joinToString(File.pathSeparator),
-
- "--check-compatibility:api:released",
- apiFile.toString(),
-
- "--update-baseline",
- intermediateExclusions.toString(),
- "--baseline", intermediateExclusions.toString(),
- "--pass-baseline-updates",
-
- "--format=v3",
- "--omit-common-packages=yes"
- )
- if (processRestrictedAPIs) {
- args = args + listOf("--show-annotation", "androidx.annotation.RestrictTo")
- }
- runWithArgs(args)
-
- var moreThanHeader = false
- intermediateExclusions.forEachLine {
- if (!it.startsWith("// Baseline format: ")) {
- moreThanHeader = true
- return@forEachLine
- }
- }
- if (moreThanHeader) {
- Files.copy(intermediateExclusions, exclusionsFile)
- } else {
- if (exclusionsFile.exists()) {
- exclusionsFile.delete()
- }
- }
- }
-}
diff --git a/buildSrc/src/main/kotlin/androidx/build/metalava/MetalavaRunner.kt b/buildSrc/src/main/kotlin/androidx/build/metalava/MetalavaRunner.kt
index f2874d3..ab1575b 100644
--- a/buildSrc/src/main/kotlin/androidx/build/metalava/MetalavaRunner.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/metalava/MetalavaRunner.kt
@@ -38,29 +38,78 @@
it.args = listOf(
"--no-banner",
"--error",
- "DeprecationMismatch", // Enforce deprecation mismatch
- "--hide",
- "HiddenSuperclass" // We allow having a hidden parent class
+ "DeprecationMismatch" // Enforce deprecation mismatch
) + args
}
}
+val API_LINT_ARGS: List<String> = listOf(
+ "--api-lint",
+ "--hide",
+ listOf(
+ // The list of checks that are hidden as they are not useful in androidx
+ "Enum", // Enums are allowed to be use in androidx
+ "CallbackInterface", // With target Java 8, we have default methods
+ "HiddenSuperclass", // We allow having a hidden parent class
+ "ProtectedMember", // We allow using protected members in androidx
+ "ManagerLookup", // Managers in androidx are not the same as platfrom services
+ "ManagerConstructor",
+
+ // List of checks that have bugs, but should be enabled once fixed.
+ "GetterSetterNames", // b/135498039
+ "StaticUtils", // b/135489083
+ "AllUpper", // b/135708486
+ "StartWithLower", // b/135710527
+
+ // The list of checks that are API lint warnings and are yet to be enabled
+ "MinMaxConstant",
+ "IntentBuilderName",
+ "OnNameExpected",
+ "TopLevelBuilder",
+ "MissingBuild",
+ "BuilderSetStyle",
+ "SetterReturnsThis",
+ "PackageLayering",
+ "OverlappingConstants",
+ "IllegalStateException",
+ "ListenerLast",
+ "ExecutorRegistration",
+ "StreamFiles",
+ "ParcelableList",
+ "AbstractInner",
+ "NotCloseable",
+ "ArrayReturn",
+ "UserHandle",
+ "UserHandleName",
+ "MethodNameTense",
+ "UseIcu",
+ "NoByteOrShort",
+ "CommonArgsFirst",
+ "SamShouldBeLast",
+ "MissingJvmStatic"
+ ).joinToString()
+)
+
+sealed class GenerateApiMode {
+ class PublicApi(val apiLintBaseline: File) : GenerateApiMode()
+ object RestrictedApi : GenerateApiMode()
+}
+
fun Project.generateApi(
bootClasspath: Collection<File>,
dependencyClasspath: FileCollection,
sourcePaths: Collection<File>,
outputFile: File,
- includeRestrictedApis: Boolean
+ generateApiMode: GenerateApiMode
) {
-
- val tempOutputFile = if (includeRestrictedApis) {
+ val tempOutputFile = if (generateApiMode is GenerateApiMode.RestrictedApi) {
File(outputFile.path + ".tmp")
} else {
outputFile
}
// generate public API txt
- var args = listOf("--classpath",
+ val args = mutableListOf("--classpath",
(bootClasspath + dependencyClasspath.files).joinToString(File.pathSeparator),
"--source-path",
@@ -73,15 +122,23 @@
"--output-kotlin-nulls=yes"
)
- if (includeRestrictedApis) {
- args = args + listOf("--show-annotation", "androidx.annotation.RestrictTo")
+ when (generateApiMode) {
+ is GenerateApiMode.PublicApi -> {
+ args += API_LINT_ARGS
+ if (generateApiMode.apiLintBaseline.exists()) {
+ args += listOf("--baseline", generateApiMode.apiLintBaseline.toString())
+ }
+ }
+ is GenerateApiMode.RestrictedApi -> {
+ args += listOf("--show-annotation", "androidx.annotation.RestrictTo")
+ }
}
val metalavaConfiguration = getMetalavaConfiguration()
runMetalavaWithArgs(metalavaConfiguration, args)
- if (includeRestrictedApis) {
+ if (generateApiMode is GenerateApiMode.RestrictedApi) {
removeRestrictToLibraryLines(tempOutputFile, outputFile)
}
}
diff --git a/buildSrc/src/main/kotlin/androidx/build/metalava/MetalavaTasks.kt b/buildSrc/src/main/kotlin/androidx/build/metalava/MetalavaTasks.kt
index 94adfba..534bc88 100644
--- a/buildSrc/src/main/kotlin/androidx/build/metalava/MetalavaTasks.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/metalava/MetalavaTasks.kt
@@ -19,7 +19,7 @@
import androidx.build.AndroidXPlugin.Companion.BUILD_ON_SERVER_TASK
import androidx.build.AndroidXExtension
import androidx.build.checkapi.ApiLocation
-import androidx.build.checkapi.ApiViolationExclusions
+import androidx.build.checkapi.ApiViolationBaselines
import androidx.build.checkapi.getCurrentApiLocation
import androidx.build.checkapi.getRequiredCompatibilityApiLocation
import androidx.build.checkapi.hasApiFolder
@@ -112,6 +112,8 @@
val builtApiLocation = ApiLocation.fromPublicApiFile(
File(project.docsDir(), "release/${project.name}/current.txt"))
+ val baselines = ApiViolationBaselines.fromApiLocation(libraryVersionApi)
+
val generateApi = project.tasks.register("generateApi", GenerateApiTask::class.java) {
task ->
task.group = "API"
@@ -119,12 +121,12 @@
task.apiLocation.set(builtApiLocation)
task.configuration = metalavaConfiguration
task.generateRestrictedAPIs = extension.trackRestrictedAPIs
+ task.baselines.set(baselines)
task.dependsOn(metalavaConfiguration)
applyInputs(javaCompileInputs, task)
}
var checkApiRelease: TaskProvider<CheckApiCompatibilityTask>? = null
- val exclusions = ApiViolationExclusions.fromApiLocation(libraryVersionApi)
project.getRequiredCompatibilityApiLocation()?.let { lastReleasedApiFile ->
checkApiRelease = project.tasks.register(
@@ -133,7 +135,7 @@
) { task ->
task.configuration = metalavaConfiguration
task.referenceApi.set(lastReleasedApiFile)
- task.exclusions.set(exclusions)
+ task.baselines.set(baselines)
task.dependsOn(metalavaConfiguration)
task.checkRestrictedAPIs = extension.trackRestrictedAPIs
applyInputs(javaCompileInputs, task)
@@ -142,14 +144,21 @@
project.tasks.register("ignoreApiChanges", IgnoreApiChangesTask::class.java) { task ->
task.configuration = metalavaConfiguration
task.referenceApi.set(checkApiRelease!!.flatMap { it.referenceApi })
- task.exclusions.set(checkApiRelease!!.flatMap { it.exclusions })
- task.processRestrictedAPIs = extension.trackRestrictedAPIs
- task.intermediateExclusionsFile.set(
- File(project.docsDir(), "release/${project.name}/api-changes.ignore"))
+ task.baselines.set(checkApiRelease!!.flatMap { it.baselines })
+ task.processRestrictedApis = extension.trackRestrictedAPIs
applyInputs(javaCompileInputs, task)
}
}
+ project.tasks.register(
+ "updateApiLintBaseline",
+ UpdateApiLintBaselineTask::class.java
+ ) { task ->
+ task.configuration = metalavaConfiguration
+ task.baselines.set(baselines)
+ applyInputs(javaCompileInputs, task)
+ }
+
val checkApi =
project.tasks.register("checkApi", CheckApiEquivalenceTask::class.java) { task ->
task.group = "API"
diff --git a/buildSrc/src/main/kotlin/androidx/build/metalava/UpdateBaselineTasks.kt b/buildSrc/src/main/kotlin/androidx/build/metalava/UpdateBaselineTasks.kt
new file mode 100644
index 0000000..aff462b
--- /dev/null
+++ b/buildSrc/src/main/kotlin/androidx/build/metalava/UpdateBaselineTasks.kt
@@ -0,0 +1,160 @@
+/*
+ * 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.build.metalava
+
+import androidx.build.checkapi.ApiLocation
+import androidx.build.checkapi.ApiViolationBaselines
+import org.gradle.api.file.FileCollection
+import org.gradle.api.provider.Property
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.InputFiles
+import org.gradle.api.tasks.OutputFile
+import org.gradle.api.tasks.OutputFiles
+import org.gradle.api.tasks.TaskAction
+import java.io.File
+
+abstract class UpdateApiLintBaselineTask : MetalavaTask() {
+ init {
+ description = "Updates an API lint baseline file (api/api_lint.ignore) to match the " +
+ "current set of violations"
+ }
+
+ @get:Input
+ abstract val baselines: Property<ApiViolationBaselines>
+
+ @OutputFile
+ fun getApiLintBaseline(): File = baselines.get().apiLintFile
+
+ @TaskAction
+ fun updateBaseline() {
+ check(bootClasspath.isNotEmpty()) { "Android boot classpath not set." }
+ val args = getCommonBaselineUpdateArgs(
+ bootClasspath,
+ dependencyClasspath!!,
+ sourcePaths,
+ baselines.get().apiLintFile
+ )
+ args += API_LINT_ARGS
+ runWithArgs(args)
+ }
+}
+
+abstract class IgnoreApiChangesTask : MetalavaTask() {
+ init {
+ description = "Updates an API tracking baseline file (api/X.Y.Z.ignore) to match the " +
+ "current set of violations"
+ }
+
+ // The API that the library is supposed to be compatible with
+ @get:Input
+ abstract val referenceApi: Property<ApiLocation>
+
+ // The baseline files (api/*.*.*.ignore) to update
+ @get:Input
+ abstract val baselines: Property<ApiViolationBaselines>
+
+ // Whether to update the file having restricted APIs too
+ @get:Input
+ var processRestrictedApis = false
+
+ @InputFiles
+ fun getTaskInputs(): List<File> {
+ if (processRestrictedApis) {
+ return referenceApi.get().files()
+ }
+ return listOf(referenceApi.get().publicApiFile)
+ }
+
+ // Declaring outputs prevents Gradle from rerunning this task if the inputs haven't changed
+ @OutputFiles
+ fun getTaskOutputs(): List<File>? {
+ if (processRestrictedApis) {
+ return listOf(baselines.get().publicApiFile, baselines.get().restrictedApiFile)
+ }
+ return listOf(baselines.get().publicApiFile)
+ }
+
+ @TaskAction
+ fun exec() {
+ check(bootClasspath.isNotEmpty()) { "Android boot classpath not set." }
+
+ updateBaseline(
+ referenceApi.get().publicApiFile,
+ baselines.get().publicApiFile,
+ false
+ )
+ if (processRestrictedApis && referenceApi.get().restrictedApiFile.exists()) {
+ updateBaseline(
+ referenceApi.get().restrictedApiFile,
+ baselines.get().restrictedApiFile,
+ true
+ )
+ }
+ }
+
+ // Updates the contents of baselineFile to specify an exception for every API present in apiFile but not
+ // present in the current source path
+ private fun updateBaseline(
+ apiFile: File,
+ baselineFile: File,
+ processRestrictedApis: Boolean
+ ) {
+ val args = getCommonBaselineUpdateArgs(
+ bootClasspath,
+ dependencyClasspath!!,
+ sourcePaths,
+ baselineFile
+ )
+ args += listOf(
+ "--check-compatibility:api:released",
+ apiFile.toString()
+ )
+ if (processRestrictedApis) {
+ args += listOf(
+ "--show-annotation",
+ "androidx.annotation.RestrictTo"
+ )
+ }
+ runWithArgs(args)
+ }
+}
+
+private fun getCommonBaselineUpdateArgs(
+ bootClasspath: Collection<File>,
+ dependencyClasspath: FileCollection,
+ sourcePaths: Collection<File>,
+ baselineFile: File
+): MutableList<String> {
+ // Create the baseline file if it does exist, as Metalava cannot handle non-existent files.
+ baselineFile.createNewFile()
+ return mutableListOf(
+ "--classpath",
+ (bootClasspath + dependencyClasspath.files).joinToString(File.pathSeparator),
+
+ "--source-path",
+ sourcePaths.filter { it.exists() }.joinToString(File.pathSeparator),
+
+ "--update-baseline",
+ baselineFile.toString(),
+ "--baseline", baselineFile.toString(),
+ "--pass-baseline-updates",
+ "--delete-empty-baselines",
+
+ "--format=v3",
+ "--omit-common-packages=yes"
+ )
+}
diff --git a/camera/camera2/src/androidTest/java/androidx/camera/camera2/PreviewTest.java b/camera/camera2/src/androidTest/java/androidx/camera/camera2/PreviewTest.java
index 9abd37d..002859c 100644
--- a/camera/camera2/src/androidTest/java/androidx/camera/camera2/PreviewTest.java
+++ b/camera/camera2/src/androidTest/java/androidx/camera/camera2/PreviewTest.java
@@ -33,9 +33,10 @@
import android.util.Size;
import android.view.Surface;
+import androidx.annotation.NonNull;
import androidx.camera.camera2.impl.Camera2CameraControl;
import androidx.camera.core.AppConfig;
-import androidx.camera.core.CameraControl;
+import androidx.camera.core.CameraControlInternal;
import androidx.camera.core.CameraFactory;
import androidx.camera.core.CameraX;
import androidx.camera.core.CameraX.LensFacing;
@@ -88,7 +89,7 @@
private String mCameraId;
@Before
- public void setUp() {
+ public void setUp() {
assumeTrue(CameraUtil.deviceHasCamera());
// Instantiates OnPreviewOutputUpdateListener before each test run.
mMockListener = mock(OnPreviewOutputUpdateListener.class);
@@ -142,7 +143,7 @@
Preview useCase = new Preview(mDefaultConfig);
useCase.updateSuggestedResolution(Collections.singletonMap(mCameraId, DEFAULT_RESOLUTION));
- CameraControl cameraControl = mock(CameraControl.class);
+ CameraControlInternal cameraControl = mock(CameraControlInternal.class);
useCase.attachCameraControl(mCameraId, cameraControl);
Rect rect = new Rect(/*left=*/ 200, /*top=*/ 200, /*right=*/ 800, /*bottom=*/ 800);
@@ -162,7 +163,7 @@
Preview useCase = new Preview(mDefaultConfig);
useCase.updateSuggestedResolution(Collections.singletonMap(mCameraId, DEFAULT_RESOLUTION));
- CameraControl cameraControl = mock(CameraControl.class);
+ CameraControlInternal cameraControl = mock(CameraControlInternal.class);
useCase.attachCameraControl(mCameraId, cameraControl);
Rect rect = new Rect(/*left=*/ 200, /*top=*/ 200, /*right=*/ 800, /*bottom=*/ 800);
@@ -177,7 +178,7 @@
@UiThreadTest
public void torchModeCanBeSet() {
Preview useCase = new Preview(mDefaultConfig);
- CameraControl cameraControl = getFakeCameraControl();
+ CameraControlInternal cameraControl = getFakeCameraControl();
useCase.attachCameraControl(mCameraId, cameraControl);
useCase.enableTorch(true);
@@ -488,15 +489,17 @@
assertThat(surfaceTexture0).isNotNull();
}
- private CameraControl getFakeCameraControl() {
+ private CameraControlInternal getFakeCameraControl() {
return new Camera2CameraControl(
- new CameraControl.ControlUpdateListener() {
+ new CameraControlInternal.ControlUpdateListener() {
@Override
- public void onCameraControlUpdateSessionConfig(SessionConfig sessionConfig) {
+ public void onCameraControlUpdateSessionConfig(
+ @NonNull SessionConfig sessionConfig) {
}
@Override
- public void onCameraControlCaptureRequests(List<CaptureConfig> captureConfigs) {
+ public void onCameraControlCaptureRequests(
+ @NonNull List<CaptureConfig> captureConfigs) {
}
},
diff --git a/camera/camera2/src/androidTest/java/androidx/camera/camera2/impl/Camera2CameraControlTest.java b/camera/camera2/src/androidTest/java/androidx/camera/camera2/impl/Camera2CameraControlTest.java
index 529b367..7b425fd 100644
--- a/camera/camera2/src/androidTest/java/androidx/camera/camera2/impl/Camera2CameraControlTest.java
+++ b/camera/camera2/src/androidTest/java/androidx/camera/camera2/impl/Camera2CameraControlTest.java
@@ -37,7 +37,7 @@
import android.os.HandlerThread;
import androidx.camera.camera2.Camera2Config;
-import androidx.camera.core.CameraControl;
+import androidx.camera.core.CameraControlInternal;
import androidx.camera.core.CaptureConfig;
import androidx.camera.core.FlashMode;
import androidx.camera.core.SessionConfig;
@@ -63,7 +63,7 @@
private static final long NO_TIMEOUT = 0;
private Camera2CameraControl mCamera2CameraControl;
- private CameraControl.ControlUpdateListener mControlUpdateListener;
+ private CameraControlInternal.ControlUpdateListener mControlUpdateListener;
private ArgumentCaptor<SessionConfig> mSessionConfigArgumentCaptor =
ArgumentCaptor.forClass(SessionConfig.class);
@SuppressWarnings("unchecked")
@@ -74,7 +74,7 @@
@Before
public void setUp() throws InterruptedException {
- mControlUpdateListener = mock(CameraControl.ControlUpdateListener.class);
+ mControlUpdateListener = mock(CameraControlInternal.ControlUpdateListener.class);
mHandlerThread = new HandlerThread("ControlThread");
mHandlerThread.start();
mHandler = HandlerCompat.createAsync(mHandlerThread.getLooper());
diff --git a/camera/camera2/src/androidTest/java/androidx/camera/camera2/impl/CameraImplTest.java b/camera/camera2/src/androidTest/java/androidx/camera/camera2/impl/CameraImplTest.java
index bfdf6ca..feee7b7 100644
--- a/camera/camera2/src/androidTest/java/androidx/camera/camera2/impl/CameraImplTest.java
+++ b/camera/camera2/src/androidTest/java/androidx/camera/camera2/impl/CameraImplTest.java
@@ -603,7 +603,7 @@
}
}));
- mCamera.getCameraControl().submitCaptureRequests(
+ mCamera.getCameraControlInternal().submitCaptureRequests(
Arrays.asList(captureConfigBuilder.build()));
UseCase useCase2 = createUseCase();
@@ -650,7 +650,7 @@
}));
- mCamera.getCameraControl().submitCaptureRequests(
+ mCamera.getCameraControlInternal().submitCaptureRequests(
Arrays.asList(captureConfigBuilder.build()));
mCamera.removeOnlineUseCase(Arrays.asList(useCase1));
diff --git a/camera/camera2/src/main/java/androidx/camera/camera2/impl/Camera.java b/camera/camera2/src/main/java/androidx/camera/camera2/impl/Camera.java
index fd1415f..374cd65 100644
--- a/camera/camera2/src/main/java/androidx/camera/camera2/impl/Camera.java
+++ b/camera/camera2/src/main/java/androidx/camera/camera2/impl/Camera.java
@@ -35,7 +35,7 @@
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
import androidx.camera.core.BaseCamera;
-import androidx.camera.core.CameraControl;
+import androidx.camera.core.CameraControlInternal;
import androidx.camera.core.CameraDeviceStateCallbacks;
import androidx.camera.core.CameraInfo;
import androidx.camera.core.CameraInfoUnavailableException;
@@ -92,7 +92,7 @@
*/
final AtomicReference<State> mState = new AtomicReference<>(State.UNINITIALIZED);
/** The camera control shared across all use cases bound to this Camera. */
- private final CameraControl mCameraControl;
+ private final CameraControlInternal mCameraControlInternal;
private final StateCallback mStateCallback = new StateCallback();
/** Information about the characteristics of this camera */
// Nullable because this is lazily instantiated
@@ -128,7 +128,8 @@
ScheduledExecutorService executorScheduler = CameraXExecutors.newHandlerExecutor(mHandler);
mUseCaseAttachState = new UseCaseAttachState(cameraId);
mState.set(State.INITIALIZED);
- mCameraControl = new Camera2CameraControl(this, executorScheduler, executorScheduler);
+ mCameraControlInternal = new Camera2CameraControl(this, executorScheduler,
+ executorScheduler);
mCaptureSession = new CaptureSession(mHandler);
}
@@ -614,8 +615,8 @@
}
if (validatingBuilder.isValid()) {
- // Apply CameraControl's SessionConfig to let CameraControl be able to control
- // Repeating Request and process results.
+ // Apply CameraControlInternal's SessionConfig to let CameraControlInternal be able
+ // to control Repeating Request and process results.
validatingBuilder.add(mCameraControlSessionConfig);
SessionConfig sessionConfig = validatingBuilder.build();
@@ -756,8 +757,8 @@
/** Returns the Camera2CameraControl attached to Camera */
@Override
- public CameraControl getCameraControl() {
- return mCameraControl;
+ public CameraControlInternal getCameraControlInternal() {
+ return mCameraControlInternal;
}
/**
@@ -801,14 +802,14 @@
/** {@inheritDoc} */
@Override
- public void onCameraControlUpdateSessionConfig(SessionConfig sessionConfig) {
+ public void onCameraControlUpdateSessionConfig(@NonNull SessionConfig sessionConfig) {
mCameraControlSessionConfig = sessionConfig;
updateCaptureSessionConfig();
}
/** {@inheritDoc} */
@Override
- public void onCameraControlCaptureRequests(List<CaptureConfig> captureConfigs) {
+ public void onCameraControlCaptureRequests(@NonNull List<CaptureConfig> captureConfigs) {
submitCaptureRequests(captureConfigs);
}
diff --git a/camera/camera2/src/main/java/androidx/camera/camera2/impl/Camera2CameraControl.java b/camera/camera2/src/main/java/androidx/camera/camera2/impl/Camera2CameraControl.java
index 79e4a31..da2118e 100644
--- a/camera/camera2/src/main/java/androidx/camera/camera2/impl/Camera2CameraControl.java
+++ b/camera/camera2/src/main/java/androidx/camera/camera2/impl/Camera2CameraControl.java
@@ -34,7 +34,7 @@
import androidx.annotation.VisibleForTesting;
import androidx.annotation.WorkerThread;
import androidx.camera.camera2.Camera2Config;
-import androidx.camera.core.CameraControl;
+import androidx.camera.core.CameraControlInternal;
import androidx.camera.core.CaptureConfig;
import androidx.camera.core.Config;
import androidx.camera.core.FlashMode;
@@ -53,12 +53,12 @@
import java.util.concurrent.TimeUnit;
/**
- * A Camera2 implementation for CameraControl interface
+ * A Camera2 implementation for CameraControlInternal interface
*
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public final class Camera2CameraControl implements CameraControl {
+public final class Camera2CameraControl implements CameraControlInternal {
private static final long DEFAULT_FOCUS_TIMEOUT_MS = 5000;
private static final String TAG = "Camera2CameraControl";
@SuppressWarnings("WeakerAccess") /* synthetic accessor */
@@ -129,7 +129,7 @@
/** {@inheritDoc} */
@Override
- public void setCropRegion(final Rect crop) {
+ public void setCropRegion(@Nullable final Rect crop) {
mExecutor.execute(new Runnable() {
@Override
public void run() {
@@ -176,6 +176,7 @@
});
}
+ @NonNull
@Override
public FlashMode getFlashMode() {
return mFlashMode;
@@ -183,7 +184,7 @@
/** {@inheritDoc} */
@Override
- public void setFlashMode(FlashMode flashMode) {
+ public void setFlashMode(@NonNull FlashMode flashMode) {
// update mFlashMode immediately so that following getFlashMode() returns correct value.
mFlashMode = flashMode;
@@ -266,7 +267,7 @@
/** {@inheritDoc} */
@Override
- public void submitCaptureRequests(final List<CaptureConfig> captureConfigs) {
+ public void submitCaptureRequests(@NonNull final List<CaptureConfig> captureConfigs) {
mExecutor.execute(new Runnable() {
@Override
public void run() {
diff --git a/camera/core/src/androidTest/java/androidx/camera/core/CameraXTest.java b/camera/core/src/androidTest/java/androidx/camera/core/CameraXTest.java
index 65c1cf1..c6a18c2 100644
--- a/camera/core/src/androidTest/java/androidx/camera/core/CameraXTest.java
+++ b/camera/core/src/androidTest/java/androidx/camera/core/CameraXTest.java
@@ -26,6 +26,7 @@
import android.os.HandlerThread;
import android.util.Size;
+import androidx.annotation.NonNull;
import androidx.camera.core.CameraX.ErrorCode;
import androidx.camera.core.CameraX.ErrorListener;
import androidx.camera.core.CameraX.LensFacing;
@@ -61,7 +62,7 @@
static {
String cameraId = sCameraFactory.cameraIdForLensFacing(LensFacing.BACK);
sCameraFactory.insertCamera(cameraId,
- new FakeCamera(new FakeCameraInfo(), mock(CameraControl.class)));
+ new FakeCamera(new FakeCameraInfo(), mock(CameraControlInternal.class)));
}
private String mCameraId;
@@ -212,7 +213,8 @@
CameraX.bindToLifecycle(mLifecycle, fakeUseCase);
- assertThat(fakeUseCase.getCameraControl(mCameraId)).isEqualTo(mCamera.getCameraControl());
+ assertThat(fakeUseCase.getCameraControl(mCameraId)).isEqualTo(
+ mCamera.getCameraControlInternal());
}
@Test
@@ -235,12 +237,12 @@
CameraX.unbind(fakeUseCase);
- // after unbind, Camera's CameraControl should be detached from Usecase
+ // after unbind, Camera's CameraControlInternal should be detached from Usecase
assertThat(fakeUseCase.getCameraControl(mCameraId)).isNotEqualTo(
- mCamera.getCameraControl());
- // UseCase still gets a non-null default CameraControl that does nothing.
+ mCamera.getCameraControlInternal());
+ // UseCase still gets a non-null default CameraControlInternal that does nothing.
assertThat(fakeUseCase.getCameraControl(mCameraId)).isEqualTo(
- CameraControl.DEFAULT_EMPTY_INSTANCE);
+ CameraControlInternal.DEFAULT_EMPTY_INSTANCE);
}
@Test
@@ -280,7 +282,7 @@
}
@Override
- public void onError(ErrorCode errorCode, String message) {
+ public void onError(@NonNull ErrorCode errorCode, @NonNull String message) {
mCount.getAndIncrement();
mLatch.countDown();
}
diff --git a/camera/core/src/androidTest/java/androidx/camera/core/ErrorHandlerTest.java b/camera/core/src/androidTest/java/androidx/camera/core/ErrorHandlerTest.java
index 854daf1..5083503 100644
--- a/camera/core/src/androidTest/java/androidx/camera/core/ErrorHandlerTest.java
+++ b/camera/core/src/androidTest/java/androidx/camera/core/ErrorHandlerTest.java
@@ -21,6 +21,7 @@
import android.os.Handler;
import android.os.HandlerThread;
+import androidx.annotation.NonNull;
import androidx.camera.core.CameraX.ErrorCode;
import androidx.camera.core.CameraX.ErrorListener;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -99,7 +100,7 @@
}
@Override
- public void onError(ErrorCode errorCode, String message) {
+ public void onError(@NonNull ErrorCode errorCode, @NonNull String message) {
mCount.getAndIncrement();
mLatch.countDown();
}
diff --git a/camera/core/src/main/java/androidx/camera/core/BaseCamera.java b/camera/core/src/main/java/androidx/camera/core/BaseCamera.java
index be4031d..8c7d14a 100644
--- a/camera/core/src/main/java/androidx/camera/core/BaseCamera.java
+++ b/camera/core/src/main/java/androidx/camera/core/BaseCamera.java
@@ -28,7 +28,7 @@
*/
@RestrictTo(Scope.LIBRARY_GROUP)
public interface BaseCamera extends UseCase.StateChangeListener,
- CameraControl.ControlUpdateListener {
+ CameraControlInternal.ControlUpdateListener {
/**
* Open the camera asynchronously.
*
@@ -65,8 +65,8 @@
*/
void removeOnlineUseCase(Collection<UseCase> useCases);
- /** Returns the global CameraControl attached to this camera. */
- CameraControl getCameraControl();
+ /** Returns the global CameraControlInternal attached to this camera. */
+ CameraControlInternal getCameraControlInternal();
/** Returns an interface to retrieve characteristics of the camera. */
CameraInfo getCameraInfo() throws CameraInfoUnavailableException;
diff --git a/camera/core/src/main/java/androidx/camera/core/CameraControl.java b/camera/core/src/main/java/androidx/camera/core/CameraControl.java
index d8e733d..6d07f6f 100644
--- a/camera/core/src/main/java/androidx/camera/core/CameraControl.java
+++ b/camera/core/src/main/java/androidx/camera/core/CameraControl.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -16,166 +16,10 @@
package androidx.camera.core;
-import android.annotation.SuppressLint;
-import android.graphics.Rect;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.annotation.RestrictTo.Scope;
-
-import java.util.List;
-import java.util.concurrent.Executor;
-
/**
- * The CameraControl Interface.
+ * An interface for controlling camera's zoom, focus and metering across all use cases.
*
- * <p>CameraControl is used for global camera operations like zoom, focus, flash and triggering
- * AF/AE.
- *
- * @hide
+ * <p>Applications can retrieve the interface via CameraX.getCameraControl.
*/
-@RestrictTo(Scope.LIBRARY_GROUP)
public interface CameraControl {
- /**
- * Set the desired crop region of the sensor to read out for all capture requests.
- *
- * <p>This crop region can be used to implement digital zoom. It is applied to every single and
- * re peating requests.
- *
- * @param crop rectangle with dimensions in sensor pixel coordinate.
- */
- void setCropRegion(Rect crop);
-
- /**
- * Adjusts the camera output according to the properties in some local regions with a callback
- * called once focus scan has completed.
- *
- * <p>The auto-focus (AF), auto-exposure (AE) and auto-whitebalance (AWB) properties will be
- * recalculated from the local regions.
- *
- * @param focus rectangle with dimensions in sensor coordinate frame for focus
- * @param metering rectangle with dimensions in sensor coordinate frame for metering
- * @param executor the executor which will be used to call the listener.
- * @param listener listener for when focus has completed.
- */
- @SuppressLint("LambdaLast") // Remove after https://issuetracker.google.com/135275901
- void focus(
- @NonNull Rect focus,
- @NonNull Rect metering,
- @NonNull Executor executor,
- @NonNull OnFocusListener listener);
-
- /**
- * Adjusts the camera output according to the properties in some local regions.
- *
- * <p>The auto-focus (AF), auto-exposure (AE) and auto-whitebalance (AWB) properties will be
- * recalculated from the local regions.
- *
- * @param focus rectangle with dimensions in sensor coordinate frame for focus
- * @param metering rectangle with dimensions in sensor coordinate frame for metering
- */
- void focus(@NonNull Rect focus, @NonNull Rect metering);
-
- /** Returns the current flash mode. */
- FlashMode getFlashMode();
-
- /**
- * Sets current flash mode
- *
- * @param flashMode the {@link FlashMode}.
- */
- void setFlashMode(FlashMode flashMode);
-
- /**
- * Enable the torch or disable the torch
- *
- * @param torch true to open the torch, false to close it.
- */
- void enableTorch(boolean torch);
-
- /** Returns if current torch is enabled or not. */
- boolean isTorchOn();
-
- /** Returns if the focus is currently locked or not. */
- boolean isFocusLocked();
-
- /** Performs a AF trigger. */
- void triggerAf();
-
- /** Performs a AE Precapture trigger. */
- void triggerAePrecapture();
-
- /** Cancel AF trigger AND/OR AE Precapture trigger.* */
- void cancelAfAeTrigger(boolean cancelAfTrigger, boolean cancelAePrecaptureTrigger);
-
- /**
- * Performs capture requests.
- */
- void submitCaptureRequests(List<CaptureConfig> captureConfigs);
-
- CameraControl DEFAULT_EMPTY_INSTANCE = new CameraControl() {
- @Override
- public void setCropRegion(Rect crop) {
- }
-
- @Override
- public void focus(Rect focus, Rect metering, @Nullable Executor executor,
- @Nullable OnFocusListener listener) {
- }
-
- @Override
- public void focus(Rect focus, Rect metering) {
- }
-
- @Override
- public FlashMode getFlashMode() {
- return null;
- }
-
- @Override
- public void setFlashMode(FlashMode flashMode) {
- }
-
- @Override
- public void enableTorch(boolean torch) {
- }
-
- @Override
- public boolean isTorchOn() {
- return false;
- }
-
- @Override
- public boolean isFocusLocked() {
- return false;
- }
-
- @Override
- public void triggerAf() {
- }
-
- @Override
- public void triggerAePrecapture() {
- }
-
- @Override
- public void cancelAfAeTrigger(boolean cancelAfTrigger, boolean cancelAePrecaptureTrigger) {
-
- }
-
- @Override
- public void submitCaptureRequests(List<CaptureConfig> captureConfigs) {
- }
- };
-
- /** Listener called when CameraControl need to notify event. */
- interface ControlUpdateListener {
-
- /** Called when CameraControl has updated session configuration. */
- void onCameraControlUpdateSessionConfig(SessionConfig sessionConfig);
-
- /** Called when CameraControl need to send capture requests. */
- void onCameraControlCaptureRequests(List<CaptureConfig> captureConfigs);
- }
}
diff --git a/camera/core/src/main/java/androidx/camera/core/CameraControlInternal.java b/camera/core/src/main/java/androidx/camera/core/CameraControlInternal.java
new file mode 100644
index 0000000..1e01a70
--- /dev/null
+++ b/camera/core/src/main/java/androidx/camera/core/CameraControlInternal.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 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.camera.core;
+
+import android.annotation.SuppressLint;
+import android.graphics.Rect;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.RestrictTo.Scope;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * The CameraControlInternal Interface.
+ *
+ * <p>CameraControlInternal is used for global camera operations like zoom, focus, flash and
+ * triggering
+ * AF/AE.
+ *
+ * @hide
+ */
+@RestrictTo(Scope.LIBRARY_GROUP)
+public interface CameraControlInternal extends CameraControl {
+ /**
+ * Set the desired crop region of the sensor to read out for all capture requests.
+ *
+ * <p>This crop region can be used to implement digital zoom. It is applied to every single and
+ * re peating requests.
+ *
+ * @param crop rectangle with dimensions in sensor pixel coordinate.
+ */
+ void setCropRegion(@Nullable Rect crop);
+
+ /**
+ * Adjusts the camera output according to the properties in some local regions with a callback
+ * called once focus scan has completed.
+ *
+ * <p>The auto-focus (AF), auto-exposure (AE) and auto-whitebalance (AWB) properties will be
+ * recalculated from the local regions.
+ *
+ * @param focus rectangle with dimensions in sensor coordinate frame for focus
+ * @param metering rectangle with dimensions in sensor coordinate frame for metering
+ * @param executor the executor which will be used to call the listener.
+ * @param listener listener for when focus has completed.
+ */
+ @SuppressLint("LambdaLast")
+ // Remove after https://issuetracker.google.com/135275901
+ void focus(
+ @NonNull Rect focus,
+ @NonNull Rect metering,
+ @NonNull Executor executor,
+ @NonNull OnFocusListener listener);
+
+ /**
+ * Adjusts the camera output according to the properties in some local regions.
+ *
+ * <p>The auto-focus (AF), auto-exposure (AE) and auto-whitebalance (AWB) properties will be
+ * recalculated from the local regions.
+ *
+ * @param focus rectangle with dimensions in sensor coordinate frame for focus
+ * @param metering rectangle with dimensions in sensor coordinate frame for metering
+ */
+ void focus(@NonNull Rect focus, @NonNull Rect metering);
+
+ /** Returns the current flash mode. */
+ @NonNull
+ FlashMode getFlashMode();
+
+ /**
+ * Sets current flash mode
+ *
+ * @param flashMode the {@link FlashMode}.
+ */
+ void setFlashMode(@NonNull FlashMode flashMode);
+
+ /**
+ * Enable the torch or disable the torch
+ *
+ * @param torch true to open the torch, false to close it.
+ */
+ void enableTorch(boolean torch);
+
+ /** Returns if current torch is enabled or not. */
+ boolean isTorchOn();
+
+ /** Returns if the focus is currently locked or not. */
+ boolean isFocusLocked();
+
+ /** Performs a AF trigger. */
+ void triggerAf();
+
+ /** Performs a AE Precapture trigger. */
+ void triggerAePrecapture();
+
+ /** Cancel AF trigger AND/OR AE Precapture trigger.* */
+ void cancelAfAeTrigger(boolean cancelAfTrigger, boolean cancelAePrecaptureTrigger);
+
+ /**
+ * Performs capture requests.
+ */
+ void submitCaptureRequests(@NonNull List<CaptureConfig> captureConfigs);
+
+ CameraControlInternal DEFAULT_EMPTY_INSTANCE = new CameraControlInternal() {
+ @Override
+ public void setCropRegion(@Nullable Rect crop) {
+ }
+
+ @Override
+ public void focus(@NonNull Rect focus, @NonNull Rect metering, @Nullable Executor executor,
+ @Nullable OnFocusListener listener) {
+ }
+
+ @Override
+ public void focus(@NonNull Rect focus, @NonNull Rect metering) {
+ }
+
+ @NonNull
+ @Override
+ public FlashMode getFlashMode() {
+ return FlashMode.OFF;
+ }
+
+ @Override
+ public void setFlashMode(@NonNull FlashMode flashMode) {
+ }
+
+ @Override
+ public void enableTorch(boolean torch) {
+ }
+
+ @Override
+ public boolean isTorchOn() {
+ return false;
+ }
+
+ @Override
+ public boolean isFocusLocked() {
+ return false;
+ }
+
+ @Override
+ public void triggerAf() {
+ }
+
+ @Override
+ public void triggerAePrecapture() {
+ }
+
+ @Override
+ public void cancelAfAeTrigger(boolean cancelAfTrigger, boolean cancelAePrecaptureTrigger) {
+
+ }
+
+ @Override
+ public void submitCaptureRequests(@NonNull List<CaptureConfig> captureConfigs) {
+ }
+ };
+
+ /** Listener called when CameraControlInternal need to notify event. */
+ interface ControlUpdateListener {
+
+ /** Called when CameraControlInternal has updated session configuration. */
+ void onCameraControlUpdateSessionConfig(@NonNull SessionConfig sessionConfig);
+
+ /** Called when CameraControlInternal need to send capture requests. */
+ void onCameraControlCaptureRequests(@NonNull List<CaptureConfig> captureConfigs);
+ }
+}
diff --git a/camera/core/src/main/java/androidx/camera/core/CameraX.java b/camera/core/src/main/java/androidx/camera/core/CameraX.java
index bba9340..8992048 100644
--- a/camera/core/src/main/java/androidx/camera/core/CameraX.java
+++ b/camera/core/src/main/java/androidx/camera/core/CameraX.java
@@ -21,6 +21,7 @@
import android.util.Size;
import androidx.annotation.MainThread;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
@@ -315,6 +316,25 @@
return INSTANCE.getCameraRepository().getCamera(cameraId).getCameraInfo();
}
+
+ /**
+ * Returns the camera control for the camera with the given lens facing.
+ *
+ * @param lensFacing the lens facing of the camera
+ * @return the {@link CameraControl}.
+ * @throws CameraInfoUnavailableException if unable to access cameras, perhaps due to
+ * insufficient permissions.
+ * @hide
+ */
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ public static CameraControl getCameraControl(LensFacing lensFacing)
+ throws CameraInfoUnavailableException {
+
+ String cameraId = getCameraWithLensFacing(lensFacing);
+ return (CameraControl) INSTANCE.getCameraRepository().getCamera(
+ cameraId).getCameraControlInternal();
+ }
+
/**
* Returns the {@link CameraDeviceSurfaceManager} which can be used to query for valid surface
* configurations.
@@ -422,7 +442,7 @@
}
useCase.addStateChangeListener(camera);
- useCase.attachCameraControl(cameraId, camera.getCameraControl());
+ useCase.attachCameraControl(cameraId, camera.getCameraControlInternal());
}
@@ -607,6 +627,6 @@
* @param error the type of error that occurred
* @param message detailed message of the error condition
*/
- void onError(ErrorCode error, String message);
+ void onError(@NonNull ErrorCode error, @NonNull String message);
}
}
diff --git a/camera/core/src/main/java/androidx/camera/core/ErrorHandler.java b/camera/core/src/main/java/androidx/camera/core/ErrorHandler.java
index e4438fb..aa6d3f3 100644
--- a/camera/core/src/main/java/androidx/camera/core/ErrorHandler.java
+++ b/camera/core/src/main/java/androidx/camera/core/ErrorHandler.java
@@ -21,6 +21,7 @@
import android.util.Log;
import androidx.annotation.GuardedBy;
+import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
import androidx.camera.core.CameraX.ErrorCode;
@@ -85,7 +86,7 @@
/** An error listener which logs the error message and returns. */
static final class PrintingErrorListener implements ErrorListener {
@Override
- public void onError(ErrorCode error, String message) {
+ public void onError(@NonNull ErrorCode error, @NonNull String message) {
Log.e(TAG, "ErrorHandler occurred: " + error + " with message: " + message);
}
}
diff --git a/camera/core/src/main/java/androidx/camera/core/ImageCapture.java b/camera/core/src/main/java/androidx/camera/core/ImageCapture.java
index 98d38688..f411721 100644
--- a/camera/core/src/main/java/androidx/camera/core/ImageCapture.java
+++ b/camera/core/src/main/java/androidx/camera/core/ImageCapture.java
@@ -232,12 +232,12 @@
return null;
}
- private CameraControl getCurrentCameraControl() {
+ private CameraControlInternal getCurrentCameraControl() {
String cameraId = getCameraIdUnchecked(mConfig.getLensFacing());
return getCameraControl(cameraId);
}
- /** Configures flash mode to CameraControl once it is ready. */
+ /** Configures flash mode to CameraControlInternal once it is ready. */
@Override
protected void onCameraControlReady(String cameraId) {
getCameraControl(cameraId).setFlashMode(mFlashMode);
diff --git a/camera/core/src/main/java/androidx/camera/core/Preview.java b/camera/core/src/main/java/androidx/camera/core/Preview.java
index a5154e7..b71e601 100644
--- a/camera/core/src/main/java/androidx/camera/core/Preview.java
+++ b/camera/core/src/main/java/androidx/camera/core/Preview.java
@@ -242,9 +242,9 @@
}
}
- // TODO: Timeout may be exposed as a PreviewConfig(moved to CameraControl)
+ // TODO: Timeout may be exposed as a PreviewConfig(moved to CameraControlInternal)
- private CameraControl getCurrentCameraControl() {
+ private CameraControlInternal getCurrentCameraControl() {
PreviewConfig config = (PreviewConfig) getUseCaseConfig();
String cameraId = getCameraIdUnchecked(config.getLensFacing());
return getCameraControl(cameraId);
diff --git a/camera/core/src/main/java/androidx/camera/core/UseCase.java b/camera/core/src/main/java/androidx/camera/core/UseCase.java
index 3259e01..5cc74ec 100644
--- a/camera/core/src/main/java/androidx/camera/core/UseCase.java
+++ b/camera/core/src/main/java/androidx/camera/core/UseCase.java
@@ -51,11 +51,11 @@
private final Set<StateChangeListener> mListeners = new HashSet<>();
/**
- * A map of camera id and CameraControl. A CameraControl will be attached into the usecase after
- * usecase is bound to lifecycle. It is used for controlling zoom/focus/flash/triggering Af or
- * AE.
+ * A map of camera id and CameraControlInternal. A CameraControlInternal will be attached
+ * into the usecase after usecase is bound to lifecycle. It is used for controlling
+ * zoom/focus/flash/triggering Af or AE.
*/
- private final Map<String, CameraControl> mAttachedCameraControlMap = new HashMap<>();
+ private final Map<String, CameraControlInternal> mAttachedCameraControlMap = new HashMap<>();
/**
* A map of the names of the {@link android.hardware.camera2.CameraDevice} to the {@link
@@ -149,7 +149,7 @@
* <p>This is called during initialization of the class. Subclassess can override this method to
* modify the behavior of combining user-supplied values and default values.
*
- * @param userConfig The user-supplied configuration.
+ * @param userConfig The user-supplied configuration.
* @param defaultConfigBuilder A builder containing use-case default values.
* @return The configuration that will be used by this use case.
* @hide
@@ -214,17 +214,17 @@
}
/**
- * Attach a CameraControl to this use case.
+ * Attach a CameraControlInternal to this use case.
*
* @hide
*/
@RestrictTo(Scope.LIBRARY_GROUP)
- public final void attachCameraControl(String cameraId, CameraControl cameraControl) {
+ public final void attachCameraControl(String cameraId, CameraControlInternal cameraControl) {
mAttachedCameraControlMap.put(cameraId, cameraControl);
onCameraControlReady(cameraId);
}
- /** Detach a CameraControl from this use case. */
+ /** Detach a CameraControlInternal from this use case. */
final void detachCameraControl(String cameraId) {
mAttachedCameraControlMap.remove(cameraId);
}
@@ -263,6 +263,7 @@
/**
* Notify all {@link StateChangeListener} that are listening to this UseCase that it has
* transitioned to an active state.
+ *
* @hide
*/
@RestrictTo(Scope.LIBRARY_GROUP)
@@ -274,6 +275,7 @@
/**
* Notify all {@link StateChangeListener} that are listening to this UseCase that it has
* transitioned to an inactive state.
+ *
* @hide
*/
@RestrictTo(Scope.LIBRARY_GROUP)
@@ -285,6 +287,7 @@
/**
* Notify all {@link StateChangeListener} that are listening to this UseCase that the
* settings have been updated.
+ *
* @hide
*/
@RestrictTo(Scope.LIBRARY_GROUP)
@@ -297,6 +300,7 @@
/**
* Notify all {@link StateChangeListener} that are listening to this UseCase that the use
* case needs to be completely reset.
+ *
* @hide
*/
@RestrictTo(Scope.LIBRARY_GROUP)
@@ -309,6 +313,7 @@
/**
* Notify all {@link StateChangeListener} that are listening to this UseCase of its current
* state.
+ *
* @hide
*/
@RestrictTo(Scope.LIBRARY_GROUP)
@@ -346,7 +351,6 @@
* Retrieves the configuration used by this use case.
*
* @return the configuration used by this use case.
- *
* @hide
*/
@RestrictTo(Scope.LIBRARY_GROUP)
@@ -359,7 +363,6 @@
*
* @param cameraId the camera id for the desired surface.
* @return the currently attached surface resolution for the given camera id.
- *
* @hide
*/
@RestrictTo(Scope.LIBRARY_GROUP)
@@ -401,9 +404,9 @@
Map<String, Size> suggestedResolutionMap);
/**
- * Called when CameraControl is attached into the UseCase. UseCase may need to override this
- * method to configure the CameraControl here. Ex. Setting correct flash mode by
- * CameraControl.setFlashMode to enable correct AE mode and flash state.
+ * Called when CameraControlInternal is attached into the UseCase. UseCase may need to
+ * override this method to configure the CameraControlInternal here. Ex. Setting correct flash
+ * mode by CameraControlInternal.setFlashMode to enable correct AE mode and flash state.
*
* @hide
*/
@@ -426,15 +429,15 @@
}
/**
- * Retrieves a previously attached {@link CameraControl}.
+ * Retrieves a previously attached {@link CameraControlInternal}.
*
* @hide
*/
@RestrictTo(Scope.LIBRARY_GROUP)
- protected CameraControl getCameraControl(String cameraId) {
- CameraControl cameraControl = mAttachedCameraControlMap.get(cameraId);
+ protected CameraControlInternal getCameraControl(String cameraId) {
+ CameraControlInternal cameraControl = mAttachedCameraControlMap.get(cameraId);
if (cameraControl == null) {
- return CameraControl.DEFAULT_EMPTY_INSTANCE;
+ return CameraControlInternal.DEFAULT_EMPTY_INSTANCE;
}
return cameraControl;
}
@@ -514,6 +517,7 @@
/**
* Called when use case was bound to the life cycle.
+ *
* @param cameraId that current used.
*/
void onBind(String cameraId);
diff --git a/camera/testing/src/main/java/androidx/camera/testing/CameraUtil.java b/camera/testing/src/main/java/androidx/camera/testing/CameraUtil.java
index 9f70fcb..09c3e56 100644
--- a/camera/testing/src/main/java/androidx/camera/testing/CameraUtil.java
+++ b/camera/testing/src/main/java/androidx/camera/testing/CameraUtil.java
@@ -141,7 +141,7 @@
UseCase... useCases) {
camera.addOnlineUseCase(Arrays.asList(useCases));
for (UseCase useCase : useCases) {
- useCase.attachCameraControl(cameraId, camera.getCameraControl());
+ useCase.attachCameraControl(cameraId, camera.getCameraControlInternal());
camera.onUseCaseActive(useCase);
}
}
diff --git a/camera/testing/src/main/java/androidx/camera/testing/fakes/FakeCamera.java b/camera/testing/src/main/java/androidx/camera/testing/fakes/FakeCamera.java
index 98c1feb..6dd28a4 100644
--- a/camera/testing/src/main/java/androidx/camera/testing/fakes/FakeCamera.java
+++ b/camera/testing/src/main/java/androidx/camera/testing/fakes/FakeCamera.java
@@ -20,9 +20,10 @@
import android.util.Log;
import android.view.Surface;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.camera.core.BaseCamera;
-import androidx.camera.core.CameraControl;
+import androidx.camera.core.CameraControlInternal;
import androidx.camera.core.CameraInfo;
import androidx.camera.core.CaptureConfig;
import androidx.camera.core.DeferrableSurface;
@@ -42,7 +43,7 @@
public class FakeCamera implements BaseCamera {
private static final String TAG = "FakeCamera";
private static final String DEFAULT_CAMERA_ID = "0";
- private final CameraControl mCameraControl;
+ private final CameraControlInternal mCameraControlInternal;
private final CameraInfo mCameraInfo;
private String mCameraId;
private UseCaseAttachState mUseCaseAttachState;
@@ -63,17 +64,18 @@
this(cameraId, new FakeCameraInfo(), /*cameraControl=*/null);
}
- public FakeCamera(CameraInfo cameraInfo, @Nullable CameraControl cameraControl) {
+ public FakeCamera(CameraInfo cameraInfo, @Nullable CameraControlInternal cameraControl) {
this(DEFAULT_CAMERA_ID, cameraInfo, cameraControl);
}
public FakeCamera(String cameraId,
CameraInfo cameraInfo,
- @Nullable CameraControl cameraControl) {
+ @Nullable CameraControlInternal cameraControl) {
mCameraInfo = cameraInfo;
mCameraId = cameraId;
mUseCaseAttachState = new UseCaseAttachState(cameraId);
- mCameraControl = cameraControl == null ? new FakeCameraControl(this) : cameraControl;
+ mCameraControlInternal = cameraControl == null ? new FakeCameraControl(this)
+ : cameraControl;
}
@Override
@@ -183,10 +185,11 @@
updateCaptureSessionConfig();
}
- // Returns fixed CameraControl instance in order to verify the instance is correctly attached.
+ // Returns fixed CameraControlInternal instance in order to verify the instance is correctly
+ // attached.
@Override
- public CameraControl getCameraControl() {
- return mCameraControl;
+ public CameraControlInternal getCameraControlInternal() {
+ return mCameraControlInternal;
}
@Override
@@ -195,13 +198,13 @@
}
@Override
- public void onCameraControlUpdateSessionConfig(SessionConfig sessionConfig) {
+ public void onCameraControlUpdateSessionConfig(@NonNull SessionConfig sessionConfig) {
mCameraControlSessionConfig = sessionConfig;
updateCaptureSessionConfig();
}
@Override
- public void onCameraControlCaptureRequests(List<CaptureConfig> captureConfigs) {
+ public void onCameraControlCaptureRequests(@NonNull List<CaptureConfig> captureConfigs) {
Log.d(TAG, "Capture requests submitted:\n " + TextUtils.join("\n ", captureConfigs));
}
@@ -233,8 +236,8 @@
validatingBuilder = mUseCaseAttachState.getActiveAndOnlineBuilder();
if (validatingBuilder.isValid()) {
- // Apply CameraControl's SessionConfig to let CameraControl be able to control
- // Repeating Request and process results.
+ // Apply CameraControlInternal's SessionConfig to let CameraControlInternal be able
+ // to control Repeating Request and process results.
validatingBuilder.add(mCameraControlSessionConfig);
mSessionConfig = validatingBuilder.build();
diff --git a/camera/testing/src/main/java/androidx/camera/testing/fakes/FakeCameraControl.java b/camera/testing/src/main/java/androidx/camera/testing/fakes/FakeCameraControl.java
index 77cbcc69..683f5fa 100644
--- a/camera/testing/src/main/java/androidx/camera/testing/fakes/FakeCameraControl.java
+++ b/camera/testing/src/main/java/androidx/camera/testing/fakes/FakeCameraControl.java
@@ -21,7 +21,8 @@
import android.util.Log;
import androidx.annotation.NonNull;
-import androidx.camera.core.CameraControl;
+import androidx.annotation.Nullable;
+import androidx.camera.core.CameraControlInternal;
import androidx.camera.core.CaptureConfig;
import androidx.camera.core.FlashMode;
import androidx.camera.core.OnFocusListener;
@@ -31,9 +32,9 @@
import java.util.concurrent.Executor;
/**
- * A fake implementation for the CameraControl interface.
+ * A fake implementation for the CameraControlInternal interface.
*/
-public final class FakeCameraControl implements CameraControl {
+public final class FakeCameraControl implements CameraControlInternal {
private static final String TAG = "FakeCameraControl";
private final ControlUpdateListener mControlUpdateListener;
private final SessionConfig.Builder mSessionConfigBuilder = new SessionConfig.Builder();
@@ -46,7 +47,7 @@
}
@Override
- public void setCropRegion(final Rect crop) {
+ public void setCropRegion(@Nullable final Rect crop) {
Log.d(TAG, "setCropRegion(" + crop + ")");
}
@@ -65,13 +66,14 @@
Log.d(TAG, "focus(\n " + focus + ",\n " + metering + ")");
}
+ @NonNull
@Override
public FlashMode getFlashMode() {
return mFlashMode;
}
@Override
- public void setFlashMode(FlashMode flashMode) {
+ public void setFlashMode(@NonNull FlashMode flashMode) {
mFlashMode = flashMode;
Log.d(TAG, "setFlashMode(" + mFlashMode + ")");
}
@@ -110,7 +112,7 @@
}
@Override
- public void submitCaptureRequests(List<CaptureConfig> captureConfigs) {
+ public void submitCaptureRequests(@NonNull List<CaptureConfig> captureConfigs) {
mControlUpdateListener.onCameraControlCaptureRequests(captureConfigs);
}
diff --git a/car/core/api/api_lint.ignore b/car/core/api/api_lint.ignore
new file mode 100644
index 0000000..ce9444c
--- /dev/null
+++ b/car/core/api/api_lint.ignore
@@ -0,0 +1,5 @@
+// Baseline format: 1.0
+KotlinOperator: androidx.car.widget.ListItemProvider#get(int):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.car.widget.ListItemProvider.ListProvider#get(int):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
diff --git a/collection/collection/api/api_lint.ignore b/collection/collection/api/api_lint.ignore
new file mode 100644
index 0000000..589b899
--- /dev/null
+++ b/collection/collection/api/api_lint.ignore
@@ -0,0 +1,55 @@
+// Baseline format: 1.0
+KotlinOperator: androidx.collection.ArraySet#contains(Object):
+ Method can be invoked as a "in" operator from Kotlin: `contains` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.collection.CircularArray#get(int):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.collection.CircularIntArray#get(int):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.collection.LongSparseArray#get(long):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.collection.LongSparseArray#get(long, E):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.collection.LruCache#get(K):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.collection.SimpleArrayMap#get(Object):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.collection.SparseArrayCompat#get(int):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.collection.SparseArrayCompat#get(int, E):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+
+
+NoClone: androidx.collection.LongSparseArray#clone():
+ Provide an explicit copy constructor instead of implementing `clone()`
+NoClone: androidx.collection.SparseArrayCompat#clone():
+ Provide an explicit copy constructor instead of implementing `clone()`
+
+
+VisiblySynchronized: androidx.collection.LruCache#createCount():
+ Internal locks must not be exposed: method androidx.collection.LruCache.createCount()
+VisiblySynchronized: androidx.collection.LruCache#evictionCount():
+ Internal locks must not be exposed: method androidx.collection.LruCache.evictionCount()
+VisiblySynchronized: androidx.collection.LruCache#get(K):
+ Internal locks must not be exposed (synchronizing on this or class is still externally observable): method androidx.collection.LruCache.get(K)
+VisiblySynchronized: androidx.collection.LruCache#hitCount():
+ Internal locks must not be exposed: method androidx.collection.LruCache.hitCount()
+VisiblySynchronized: androidx.collection.LruCache#maxSize():
+ Internal locks must not be exposed: method androidx.collection.LruCache.maxSize()
+VisiblySynchronized: androidx.collection.LruCache#missCount():
+ Internal locks must not be exposed: method androidx.collection.LruCache.missCount()
+VisiblySynchronized: androidx.collection.LruCache#put(K, V):
+ Internal locks must not be exposed (synchronizing on this or class is still externally observable): method androidx.collection.LruCache.put(K,V)
+VisiblySynchronized: androidx.collection.LruCache#putCount():
+ Internal locks must not be exposed: method androidx.collection.LruCache.putCount()
+VisiblySynchronized: androidx.collection.LruCache#remove(K):
+ Internal locks must not be exposed (synchronizing on this or class is still externally observable): method androidx.collection.LruCache.remove(K)
+VisiblySynchronized: androidx.collection.LruCache#resize(int):
+ Internal locks must not be exposed (synchronizing on this or class is still externally observable): method androidx.collection.LruCache.resize(int)
+VisiblySynchronized: androidx.collection.LruCache#size():
+ Internal locks must not be exposed: method androidx.collection.LruCache.size()
+VisiblySynchronized: androidx.collection.LruCache#snapshot():
+ Internal locks must not be exposed: method androidx.collection.LruCache.snapshot()
+VisiblySynchronized: androidx.collection.LruCache#toString():
+ Internal locks must not be exposed: method androidx.collection.LruCache.toString()
+VisiblySynchronized: androidx.collection.LruCache#trimToSize(int):
+ Internal locks must not be exposed (synchronizing on this or class is still externally observable): method androidx.collection.LruCache.trimToSize(int)
diff --git a/concurrent/futures/api/api_lint.ignore b/concurrent/futures/api/api_lint.ignore
new file mode 100644
index 0000000..2539658
--- /dev/null
+++ b/concurrent/futures/api/api_lint.ignore
@@ -0,0 +1,7 @@
+// Baseline format: 1.0
+GenericException: androidx.concurrent.futures.CallbackToFutureAdapter.Resolver#attachCompleter(androidx.concurrent.futures.CallbackToFutureAdapter.Completer<T>):
+ Methods must not throw generic exceptions (`java.lang.Exception`)
+
+
+PairedRegistration: androidx.concurrent.futures.CallbackToFutureAdapter.Completer#addCancellationListener(Runnable, java.util.concurrent.Executor):
+ Found addCancellationListener but not removeCancellationListener in androidx.concurrent.futures.CallbackToFutureAdapter.Completer
diff --git a/content/api/api_lint.ignore b/content/api/api_lint.ignore
new file mode 100644
index 0000000..4bd59a5b
--- /dev/null
+++ b/content/api/api_lint.ignore
@@ -0,0 +1,9 @@
+// Baseline format: 1.0
+ActionValue: androidx.contentpager.content.ContentPager#EXTRA_HONORED_ARGS:
+ Inconsistent extra value; expected `androidx.contentpager.content.extra.HONORED_ARGS`, was `android.content.extra.HONORED_ARGS`
+ActionValue: androidx.contentpager.content.ContentPager#EXTRA_REQUESTED_LIMIT:
+ Inconsistent extra value; expected `androidx.contentpager.content.extra.REQUESTED_LIMIT`, was `android-support:extra-ignored-limit`
+ActionValue: androidx.contentpager.content.ContentPager#EXTRA_SUGGESTED_LIMIT:
+ Inconsistent extra value; expected `androidx.contentpager.content.extra.SUGGESTED_LIMIT`, was `android-support:extra-suggested-limit`
+ActionValue: androidx.contentpager.content.ContentPager#EXTRA_TOTAL_COUNT:
+ Inconsistent extra value; expected `androidx.contentpager.content.extra.TOTAL_COUNT`, was `android.content.extra.TOTAL_COUNT`
diff --git a/coordinatorlayout/api/api_lint.ignore b/coordinatorlayout/api/api_lint.ignore
new file mode 100644
index 0000000..129ea6f
--- /dev/null
+++ b/coordinatorlayout/api/api_lint.ignore
@@ -0,0 +1,7 @@
+// Baseline format: 1.0
+ParcelCreator: androidx.coordinatorlayout.widget.CoordinatorLayout.SavedState:
+ Parcelable requires `public int describeContents()`; missing in androidx.coordinatorlayout.widget.CoordinatorLayout.SavedState
+
+
+ParcelNotFinal: androidx.coordinatorlayout.widget.CoordinatorLayout.SavedState:
+ Parcelable classes must be final: androidx.coordinatorlayout.widget.CoordinatorLayout.SavedState is not final
diff --git a/core/core-ktx/api/api_lint.ignore b/core/core-ktx/api/api_lint.ignore
new file mode 100644
index 0000000..642bfa7
--- /dev/null
+++ b/core/core-ktx/api/api_lint.ignore
@@ -0,0 +1,33 @@
+// Baseline format: 1.0
+AcronymName: androidx.core.database.sqlite.SQLiteDatabaseKt:
+ Acronyms should not be capitalized in class names: was `SQLiteDatabaseKt`, should this be `SqLiteDatabaseKt`?
+
+
+AutoBoxing: androidx.core.database.CursorKt#getDoubleOrNull(android.database.Cursor, int):
+ Must avoid boxed primitives (`java.lang.Double`)
+AutoBoxing: androidx.core.database.CursorKt#getFloatOrNull(android.database.Cursor, int):
+ Must avoid boxed primitives (`java.lang.Float`)
+AutoBoxing: androidx.core.database.CursorKt#getIntOrNull(android.database.Cursor, int):
+ Must avoid boxed primitives (`java.lang.Integer`)
+AutoBoxing: androidx.core.database.CursorKt#getLongOrNull(android.database.Cursor, int):
+ Must avoid boxed primitives (`java.lang.Long`)
+AutoBoxing: androidx.core.database.CursorKt#getShortOrNull(android.database.Cursor, int):
+ Must avoid boxed primitives (`java.lang.Short`)
+
+
+DocumentExceptions: androidx.core.graphics.RegionKt#iterator(android.graphics.Region):
+ Method RegionKt.iterator appears to be throwing java.lang.IndexOutOfBoundsException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.core.view.MenuKt#iterator(android.view.Menu):
+ Method MenuKt.iterator appears to be throwing java.lang.IndexOutOfBoundsException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.core.view.ViewGroupKt#iterator(android.view.ViewGroup):
+ Method ViewGroupKt.iterator appears to be throwing java.lang.IndexOutOfBoundsException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+
+
+PairedRegistration: androidx.core.animation.AnimatorKt#addListener(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>):
+ Found addListener but not removeListener in androidx.core.animation.AnimatorKt
+PairedRegistration: androidx.core.animation.AnimatorKt#addPauseListener(android.animation.Animator, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>, kotlin.jvm.functions.Function1<? super android.animation.Animator,kotlin.Unit>):
+ Found addPauseListener but not removePauseListener in androidx.core.animation.AnimatorKt
+PairedRegistration: androidx.core.transition.TransitionKt#addListener(android.transition.Transition, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit>, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit>, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit>, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit>, kotlin.jvm.functions.Function1<? super android.transition.Transition,kotlin.Unit>):
+ Found addListener but not removeListener in androidx.core.transition.TransitionKt
+PairedRegistration: androidx.core.widget.TextViewKt#addTextChangedListener(android.widget.TextView, kotlin.jvm.functions.Function4<? super java.lang.CharSequence,? super java.lang.Integer,? super java.lang.Integer,? super java.lang.Integer,kotlin.Unit>, kotlin.jvm.functions.Function4<? super java.lang.CharSequence,? super java.lang.Integer,? super java.lang.Integer,? super java.lang.Integer,kotlin.Unit>, kotlin.jvm.functions.Function1<? super android.text.Editable,kotlin.Unit>):
+ Found addTextChangedListener but not removeTextChangedListener in androidx.core.widget.TextViewKt
diff --git a/core/core/api/api_lint.ignore b/core/core/api/api_lint.ignore
new file mode 100644
index 0000000..e607711
--- /dev/null
+++ b/core/core/api/api_lint.ignore
@@ -0,0 +1,191 @@
+// Baseline format: 1.0
+AcronymName: androidx.core.database.sqlite.SQLiteCursorCompat:
+ Acronyms should not be capitalized in class names: was `SQLiteCursorCompat`, should this be `SqLiteCursorCompat`?
+AcronymName: androidx.core.graphics.ColorUtils#blendARGB(int, int, float):
+ Acronyms should not be capitalized in method names: was `blendARGB`, should this be `blendArgb`?
+AcronymName: androidx.core.graphics.ColorUtils#blendHSL(float[], float[], float, float[]):
+ Acronyms should not be capitalized in method names: was `blendHSL`, should this be `blendHsl`?
+AcronymName: androidx.core.graphics.ColorUtils#blendLAB(double[], double[], double, double[]):
+ Acronyms should not be capitalized in method names: was `blendLAB`, should this be `blendLab`?
+AcronymName: androidx.core.graphics.ColorUtils#colorToHSL(int, float[]):
+ Acronyms should not be capitalized in method names: was `colorToHSL`, should this be `colorToHsl`?
+AcronymName: androidx.core.graphics.ColorUtils#colorToLAB(int, double[]):
+ Acronyms should not be capitalized in method names: was `colorToLAB`, should this be `colorToLab`?
+AcronymName: androidx.core.graphics.ColorUtils#colorToXYZ(int, double[]):
+ Acronyms should not be capitalized in method names: was `colorToXYZ`, should this be `colorToXyz`?
+AcronymName: androidx.core.text.ICUCompat:
+ Acronyms should not be capitalized in class names: was `ICUCompat`, should this be `IcuCompat`?
+AcronymName: androidx.core.view.accessibility.AccessibilityViewCommand.MoveHtmlArguments#getHTMLElement():
+ Acronyms should not be capitalized in method names: was `getHTMLElement`, should this be `getHtmlElement`?
+
+
+ActionValue: androidx.core.app.ActivityOptionsCompat#EXTRA_USAGE_TIME_REPORT:
+ Inconsistent extra value; expected `androidx.core.app.extra.USAGE_TIME_REPORT`, was `android.activity.usage_time`
+ActionValue: androidx.core.app.ActivityOptionsCompat#EXTRA_USAGE_TIME_REPORT_PACKAGES:
+ Inconsistent extra value; expected `androidx.core.app.extra.USAGE_TIME_REPORT_PACKAGES`, was `android.usage_time_packages`
+ActionValue: androidx.core.app.NotificationCompat#EXTRA_AUDIO_CONTENTS_URI:
+ Inconsistent extra value; expected `androidx.core.app.extra.AUDIO_CONTENTS_URI`, was `android.audioContents`
+ActionValue: androidx.core.app.NotificationCompat#EXTRA_BACKGROUND_IMAGE_URI:
+ Inconsistent extra value; expected `androidx.core.app.extra.BACKGROUND_IMAGE_URI`, was `android.backgroundImageUri`
+ActionValue: androidx.core.app.NotificationCompat#EXTRA_BIG_TEXT:
+ Inconsistent extra value; expected `androidx.core.app.extra.BIG_TEXT`, was `android.bigText`
+ActionValue: androidx.core.app.NotificationCompat#EXTRA_COMPACT_ACTIONS:
+ Inconsistent extra value; expected `androidx.core.app.extra.COMPACT_ACTIONS`, was `android.compactActions`
+ActionValue: androidx.core.app.NotificationCompat#EXTRA_CONVERSATION_TITLE:
+ Inconsistent extra value; expected `androidx.core.app.extra.CONVERSATION_TITLE`, was `android.conversationTitle`
+ActionValue: androidx.core.app.NotificationCompat#EXTRA_HIDDEN_CONVERSATION_TITLE:
+ Inconsistent extra value; expected `androidx.core.app.extra.HIDDEN_CONVERSATION_TITLE`, was `android.hiddenConversationTitle`
+ActionValue: androidx.core.app.NotificationCompat#EXTRA_INFO_TEXT:
+ Inconsistent extra value; expected `androidx.core.app.extra.INFO_TEXT`, was `android.infoText`
+ActionValue: androidx.core.app.NotificationCompat#EXTRA_IS_GROUP_CONVERSATION:
+ Inconsistent extra value; expected `androidx.core.app.extra.IS_GROUP_CONVERSATION`, was `android.isGroupConversation`
+ActionValue: androidx.core.app.NotificationCompat#EXTRA_LARGE_ICON:
+ Inconsistent extra value; expected `androidx.core.app.extra.LARGE_ICON`, was `android.largeIcon`
+ActionValue: androidx.core.app.NotificationCompat#EXTRA_LARGE_ICON_BIG:
+ Inconsistent extra value; expected `androidx.core.app.extra.LARGE_ICON_BIG`, was `android.largeIcon.big`
+ActionValue: androidx.core.app.NotificationCompat#EXTRA_MEDIA_SESSION:
+ Inconsistent extra value; expected `androidx.core.app.extra.MEDIA_SESSION`, was `android.mediaSession`
+ActionValue: androidx.core.app.NotificationCompat#EXTRA_MESSAGES:
+ Inconsistent extra value; expected `androidx.core.app.extra.MESSAGES`, was `android.messages`
+ActionValue: androidx.core.app.NotificationCompat#EXTRA_MESSAGING_STYLE_USER:
+ Inconsistent extra value; expected `androidx.core.app.extra.MESSAGING_STYLE_USER`, was `android.messagingStyleUser`
+ActionValue: androidx.core.app.NotificationCompat#EXTRA_PEOPLE:
+ Inconsistent extra value; expected `androidx.core.app.extra.PEOPLE`, was `android.people`
+ActionValue: androidx.core.app.NotificationCompat#EXTRA_PICTURE:
+ Inconsistent extra value; expected `androidx.core.app.extra.PICTURE`, was `android.picture`
+ActionValue: androidx.core.app.NotificationCompat#EXTRA_PROGRESS:
+ Inconsistent extra value; expected `androidx.core.app.extra.PROGRESS`, was `android.progress`
+ActionValue: androidx.core.app.NotificationCompat#EXTRA_PROGRESS_INDETERMINATE:
+ Inconsistent extra value; expected `androidx.core.app.extra.PROGRESS_INDETERMINATE`, was `android.progressIndeterminate`
+ActionValue: androidx.core.app.NotificationCompat#EXTRA_PROGRESS_MAX:
+ Inconsistent extra value; expected `androidx.core.app.extra.PROGRESS_MAX`, was `android.progressMax`
+ActionValue: androidx.core.app.NotificationCompat#EXTRA_REMOTE_INPUT_HISTORY:
+ Inconsistent extra value; expected `androidx.core.app.extra.REMOTE_INPUT_HISTORY`, was `android.remoteInputHistory`
+ActionValue: androidx.core.app.NotificationCompat#EXTRA_SELF_DISPLAY_NAME:
+ Inconsistent extra value; expected `androidx.core.app.extra.SELF_DISPLAY_NAME`, was `android.selfDisplayName`
+ActionValue: androidx.core.app.NotificationCompat#EXTRA_SHOW_CHRONOMETER:
+ Inconsistent extra value; expected `androidx.core.app.extra.SHOW_CHRONOMETER`, was `android.showChronometer`
+ActionValue: androidx.core.app.NotificationCompat#EXTRA_SHOW_WHEN:
+ Inconsistent extra value; expected `androidx.core.app.extra.SHOW_WHEN`, was `android.showWhen`
+ActionValue: androidx.core.app.NotificationCompat#EXTRA_SMALL_ICON:
+ Inconsistent extra value; expected `androidx.core.app.extra.SMALL_ICON`, was `android.icon`
+ActionValue: androidx.core.app.NotificationCompat#EXTRA_SUB_TEXT:
+ Inconsistent extra value; expected `androidx.core.app.extra.SUB_TEXT`, was `android.subText`
+ActionValue: androidx.core.app.NotificationCompat#EXTRA_SUMMARY_TEXT:
+ Inconsistent extra value; expected `androidx.core.app.extra.SUMMARY_TEXT`, was `android.summaryText`
+ActionValue: androidx.core.app.NotificationCompat#EXTRA_TEMPLATE:
+ Inconsistent extra value; expected `androidx.core.app.extra.TEMPLATE`, was `android.template`
+ActionValue: androidx.core.app.NotificationCompat#EXTRA_TEXT:
+ Inconsistent extra value; expected `androidx.core.app.extra.TEXT`, was `android.text`
+ActionValue: androidx.core.app.NotificationCompat#EXTRA_TEXT_LINES:
+ Inconsistent extra value; expected `androidx.core.app.extra.TEXT_LINES`, was `android.textLines`
+ActionValue: androidx.core.app.NotificationCompat#EXTRA_TITLE:
+ Inconsistent extra value; expected `androidx.core.app.extra.TITLE`, was `android.title`
+ActionValue: androidx.core.app.NotificationCompat#EXTRA_TITLE_BIG:
+ Inconsistent extra value; expected `androidx.core.app.extra.TITLE_BIG`, was `android.title.big`
+ActionValue: androidx.core.app.NotificationCompatExtras#EXTRA_ACTION_EXTRAS:
+ Inconsistent extra value; expected `androidx.core.app.extra.ACTION_EXTRAS`, was `android.support.actionExtras`
+ActionValue: androidx.core.app.NotificationCompatExtras#EXTRA_GROUP_KEY:
+ Inconsistent extra value; expected `androidx.core.app.extra.GROUP_KEY`, was `android.support.groupKey`
+ActionValue: androidx.core.app.NotificationCompatExtras#EXTRA_GROUP_SUMMARY:
+ Inconsistent extra value; expected `androidx.core.app.extra.GROUP_SUMMARY`, was `android.support.isGroupSummary`
+ActionValue: androidx.core.app.NotificationCompatExtras#EXTRA_LOCAL_ONLY:
+ Inconsistent extra value; expected `androidx.core.app.extra.LOCAL_ONLY`, was `android.support.localOnly`
+ActionValue: androidx.core.app.NotificationCompatExtras#EXTRA_REMOTE_INPUTS:
+ Inconsistent extra value; expected `androidx.core.app.extra.REMOTE_INPUTS`, was `android.support.remoteInputs`
+ActionValue: androidx.core.app.NotificationCompatExtras#EXTRA_SORT_KEY:
+ Inconsistent extra value; expected `androidx.core.app.extra.SORT_KEY`, was `android.support.sortKey`
+ActionValue: androidx.core.app.NotificationManagerCompat#ACTION_BIND_SIDE_CHANNEL:
+ Inconsistent action value; expected `androidx.core.app.action.BIND_SIDE_CHANNEL`, was `android.support.BIND_NOTIFICATION_SIDE_CHANNEL`
+ActionValue: androidx.core.app.NotificationManagerCompat#EXTRA_USE_SIDE_CHANNEL:
+ Inconsistent extra value; expected `androidx.core.app.extra.USE_SIDE_CHANNEL`, was `android.support.useSideChannel`
+ActionValue: androidx.core.app.RemoteInput#EXTRA_RESULTS_DATA:
+ Inconsistent extra value; expected `androidx.core.app.extra.RESULTS_DATA`, was `android.remoteinput.resultsData`
+ActionValue: androidx.core.app.ShareCompat#EXTRA_CALLING_ACTIVITY:
+ Inconsistent extra value; expected `androidx.core.app.extra.CALLING_ACTIVITY`, was `androidx.core.app.EXTRA_CALLING_ACTIVITY`
+ActionValue: androidx.core.app.ShareCompat#EXTRA_CALLING_ACTIVITY_INTEROP:
+ Inconsistent extra value; expected `androidx.core.app.extra.CALLING_ACTIVITY_INTEROP`, was `android.support.v4.app.EXTRA_CALLING_ACTIVITY`
+ActionValue: androidx.core.app.ShareCompat#EXTRA_CALLING_PACKAGE:
+ Inconsistent extra value; expected `androidx.core.app.extra.CALLING_PACKAGE`, was `androidx.core.app.EXTRA_CALLING_PACKAGE`
+ActionValue: androidx.core.app.ShareCompat#EXTRA_CALLING_PACKAGE_INTEROP:
+ Inconsistent extra value; expected `androidx.core.app.extra.CALLING_PACKAGE_INTEROP`, was `android.support.v4.app.EXTRA_CALLING_PACKAGE`
+ActionValue: androidx.core.content.IntentCompat#EXTRA_HTML_TEXT:
+ Inconsistent extra value; expected `androidx.core.content.extra.HTML_TEXT`, was `android.intent.extra.HTML_TEXT`
+ActionValue: androidx.core.content.IntentCompat#EXTRA_START_PLAYBACK:
+ Inconsistent extra value; expected `androidx.core.content.extra.START_PLAYBACK`, was `android.intent.extra.START_PLAYBACK`
+ActionValue: androidx.core.content.pm.ShortcutManagerCompat#EXTRA_SHORTCUT_ID:
+ Inconsistent extra value; expected `androidx.core.content.pm.extra.SHORTCUT_ID`, was `android.intent.extra.shortcut.ID`
+ActionValue: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#ACTION_ARGUMENT_COLUMN_INT:
+ Inconsistent action value; expected `androidx.core.view.accessibility.action.ARGUMENT_COLUMN_INT`, was `android.view.accessibility.action.ARGUMENT_COLUMN_INT`
+ActionValue: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN:
+ Inconsistent action value; expected `androidx.core.view.accessibility.action.ARGUMENT_EXTEND_SELECTION_BOOLEAN`, was `ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN`
+ActionValue: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#ACTION_ARGUMENT_HTML_ELEMENT_STRING:
+ Inconsistent action value; expected `androidx.core.view.accessibility.action.ARGUMENT_HTML_ELEMENT_STRING`, was `ACTION_ARGUMENT_HTML_ELEMENT_STRING`
+ActionValue: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT:
+ Inconsistent action value; expected `androidx.core.view.accessibility.action.ARGUMENT_MOVEMENT_GRANULARITY_INT`, was `ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT`
+ActionValue: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#ACTION_ARGUMENT_MOVE_WINDOW_X:
+ Inconsistent action value; expected `androidx.core.view.accessibility.action.ARGUMENT_MOVE_WINDOW_X`, was `ACTION_ARGUMENT_MOVE_WINDOW_X`
+ActionValue: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#ACTION_ARGUMENT_MOVE_WINDOW_Y:
+ Inconsistent action value; expected `androidx.core.view.accessibility.action.ARGUMENT_MOVE_WINDOW_Y`, was `ACTION_ARGUMENT_MOVE_WINDOW_Y`
+ActionValue: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#ACTION_ARGUMENT_PROGRESS_VALUE:
+ Inconsistent action value; expected `androidx.core.view.accessibility.action.ARGUMENT_PROGRESS_VALUE`, was `android.view.accessibility.action.ARGUMENT_PROGRESS_VALUE`
+ActionValue: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#ACTION_ARGUMENT_ROW_INT:
+ Inconsistent action value; expected `androidx.core.view.accessibility.action.ARGUMENT_ROW_INT`, was `android.view.accessibility.action.ARGUMENT_ROW_INT`
+ActionValue: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#ACTION_ARGUMENT_SELECTION_END_INT:
+ Inconsistent action value; expected `androidx.core.view.accessibility.action.ARGUMENT_SELECTION_END_INT`, was `ACTION_ARGUMENT_SELECTION_END_INT`
+ActionValue: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#ACTION_ARGUMENT_SELECTION_START_INT:
+ Inconsistent action value; expected `androidx.core.view.accessibility.action.ARGUMENT_SELECTION_START_INT`, was `ACTION_ARGUMENT_SELECTION_START_INT`
+ActionValue: androidx.core.view.accessibility.AccessibilityNodeInfoCompat#ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE:
+ Inconsistent action value; expected `androidx.core.view.accessibility.action.ARGUMENT_SET_TEXT_CHARSEQUENCE`, was `ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE`
+
+
+BannedThrow: androidx.core.widget.TextViewCompat#setAutoSizeTextTypeUniformWithConfiguration(android.widget.TextView, int, int, int, int):
+ Methods must not mention RuntimeException subclasses in throws clauses (was `java.lang.IllegalArgumentException`)
+BannedThrow: androidx.core.widget.TextViewCompat#setAutoSizeTextTypeUniformWithPresetSizes(android.widget.TextView, int[], int):
+ Methods must not mention RuntimeException subclasses in throws clauses (was `java.lang.IllegalArgumentException`)
+
+
+ContextFirst: androidx.core.view.LayoutInflaterFactory#onCreateView(android.view.View, String, android.content.Context, android.util.AttributeSet) parameter #2:
+ Context is distinct, so it must be the first argument (method `onCreateView`)
+ContextFirst: androidx.core.view.ViewConfigurationCompat#getScaledHorizontalScrollFactor(android.view.ViewConfiguration, android.content.Context) parameter #1:
+ Context is distinct, so it must be the first argument (method `getScaledHorizontalScrollFactor`)
+ContextFirst: androidx.core.view.ViewConfigurationCompat#getScaledVerticalScrollFactor(android.view.ViewConfiguration, android.content.Context) parameter #1:
+ Context is distinct, so it must be the first argument (method `getScaledVerticalScrollFactor`)
+ContextFirst: androidx.core.view.ViewConfigurationCompat#shouldShowMenuShortcutsWhenKeyboardPresent(android.view.ViewConfiguration, android.content.Context) parameter #1:
+ Context is distinct, so it must be the first argument (method `shouldShowMenuShortcutsWhenKeyboardPresent`)
+
+
+KotlinOperator: androidx.core.os.LocaleListCompat#get(int):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+
+
+ListenerInterface: androidx.core.view.OneShotPreDrawListener:
+ Listeners should be an interface, or otherwise renamed Callback: OneShotPreDrawListener
+
+
+NoClone: androidx.core.app.NotificationCompat.Action.WearableExtender#clone():
+ Provide an explicit copy constructor instead of implementing `clone()`
+NoClone: androidx.core.app.NotificationCompat.WearableExtender#clone():
+ Provide an explicit copy constructor instead of implementing `clone()`
+
+
+PairedRegistration: androidx.core.view.OneShotPreDrawListener#removeListener():
+ Found removeListener but not addListener in androidx.core.view.OneShotPreDrawListener
+
+
+ServiceName: androidx.core.app.NotificationCompat#CATEGORY_SERVICE:
+ Inconsistent service value; expected `CATEGORY`, was `service`
+
+
+VisiblySynchronized: androidx.core.os.CancellationSignal#cancel():
+ Internal locks must not be exposed (synchronizing on this or class is still externally observable): method androidx.core.os.CancellationSignal.cancel()
+VisiblySynchronized: androidx.core.os.CancellationSignal#getCancellationSignalObject():
+ Internal locks must not be exposed (synchronizing on this or class is still externally observable): method androidx.core.os.CancellationSignal.getCancellationSignalObject()
+VisiblySynchronized: androidx.core.os.CancellationSignal#isCanceled():
+ Internal locks must not be exposed (synchronizing on this or class is still externally observable): method androidx.core.os.CancellationSignal.isCanceled()
+VisiblySynchronized: androidx.core.os.CancellationSignal#setOnCancelListener(androidx.core.os.CancellationSignal.OnCancelListener):
+ Internal locks must not be exposed (synchronizing on this or class is still externally observable): method androidx.core.os.CancellationSignal.setOnCancelListener(androidx.core.os.CancellationSignal.OnCancelListener)
+VisiblySynchronized: androidx.core.widget.ContentLoadingProgressBar#hide():
+ Internal locks must not be exposed: method androidx.core.widget.ContentLoadingProgressBar.hide()
+VisiblySynchronized: androidx.core.widget.ContentLoadingProgressBar#show():
+ Internal locks must not be exposed: method androidx.core.widget.ContentLoadingProgressBar.show()
diff --git a/cursoradapter/api/api_lint.ignore b/cursoradapter/api/api_lint.ignore
new file mode 100644
index 0000000..7b5962b
--- /dev/null
+++ b/cursoradapter/api/api_lint.ignore
@@ -0,0 +1,5 @@
+// Baseline format: 1.0
+ContextFirst: androidx.cursoradapter.widget.CursorAdapter#bindView(android.view.View, android.content.Context, android.database.Cursor) parameter #1:
+ Context is distinct, so it must be the first argument (method `bindView`)
+ContextFirst: androidx.cursoradapter.widget.SimpleCursorAdapter#bindView(android.view.View, android.content.Context, android.database.Cursor) parameter #1:
+ Context is distinct, so it must be the first argument (method `bindView`)
diff --git a/customview/api/api_lint.ignore b/customview/api/api_lint.ignore
new file mode 100644
index 0000000..b809558
--- /dev/null
+++ b/customview/api/api_lint.ignore
@@ -0,0 +1,11 @@
+// Baseline format: 1.0
+CallbackMethodName: androidx.customview.widget.ViewDragHelper.Callback:
+ Callback method names must follow the on<Something> style: getOrderedChildIndex
+
+
+ParcelConstructor: androidx.customview.view.AbsSavedState#AbsSavedState(android.os.Parcel):
+ Parcelable inflation is exposed through CREATOR, not raw constructors, in androidx.customview.view.AbsSavedState
+
+
+ParcelNotFinal: androidx.customview.view.AbsSavedState:
+ Parcelable classes must be final: androidx.customview.view.AbsSavedState is not final
diff --git a/drawerlayout/api/api_lint.ignore b/drawerlayout/api/api_lint.ignore
new file mode 100644
index 0000000..e4e6148
--- /dev/null
+++ b/drawerlayout/api/api_lint.ignore
@@ -0,0 +1,15 @@
+// Baseline format: 1.0
+ConcreteCollection: androidx.drawerlayout.widget.DrawerLayout#addFocusables(java.util.ArrayList<android.view.View>, int, int) parameter #0:
+ Parameter type is concrete collection (`java.util.ArrayList`); must be higher-level interface
+
+
+ListenerInterface: androidx.drawerlayout.widget.DrawerLayout.SimpleDrawerListener:
+ Listeners should be an interface, or otherwise renamed Callback: SimpleDrawerListener
+
+
+ParcelCreator: androidx.drawerlayout.widget.DrawerLayout.SavedState:
+ Parcelable requires `public int describeContents()`; missing in androidx.drawerlayout.widget.DrawerLayout.SavedState
+
+
+ParcelNotFinal: androidx.drawerlayout.widget.DrawerLayout.SavedState:
+ Parcelable classes must be final: androidx.drawerlayout.widget.DrawerLayout.SavedState is not final
diff --git a/enterprise/feedback/api/api_lint.ignore b/enterprise/feedback/api/api_lint.ignore
new file mode 100644
index 0000000..4350e62
--- /dev/null
+++ b/enterprise/feedback/api/api_lint.ignore
@@ -0,0 +1,5 @@
+// Baseline format: 1.0
+VisiblySynchronized: androidx.enterprise.feedback.SingletonKeyedAppStatesReporter#getInstance(android.content.Context):
+ Internal locks must not be exposed (synchronizing on this or class is still externally observable): method androidx.enterprise.feedback.SingletonKeyedAppStatesReporter.getInstance(android.content.Context)
+VisiblySynchronized: androidx.enterprise.feedback.SingletonKeyedAppStatesReporter#initialize(android.content.Context, java.util.concurrent.Executor):
+ Internal locks must not be exposed (synchronizing on this or class is still externally observable): method androidx.enterprise.feedback.SingletonKeyedAppStatesReporter.initialize(android.content.Context,java.util.concurrent.Executor)
diff --git a/exifinterface/api/api_lint.ignore b/exifinterface/api/api_lint.ignore
new file mode 100644
index 0000000..e0f65e9
--- /dev/null
+++ b/exifinterface/api/api_lint.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+NoClone: androidx.exifinterface.media.ExifInterface#ExifInterface(java.io.FileDescriptor) parameter #0:
+ Must use ParcelFileDescriptor instead of FileDescriptor in parameter fileDescriptor in androidx.exifinterface.media.ExifInterface(java.io.FileDescriptor fileDescriptor)
diff --git a/exifinterface/build.gradle b/exifinterface/build.gradle
index 7d77583..c33cacb 100644
--- a/exifinterface/build.gradle
+++ b/exifinterface/build.gradle
@@ -8,6 +8,13 @@
id("com.android.library")
}
+android {
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_1_7
+ targetCompatibility = JavaVersion.VERSION_1_7
+ }
+}
+
dependencies {
api("androidx.annotation:annotation:1.1.0")
diff --git a/fragment/fragment/api/api_lint.ignore b/fragment/fragment/api/api_lint.ignore
new file mode 100644
index 0000000..ad3841c
--- /dev/null
+++ b/fragment/fragment/api/api_lint.ignore
@@ -0,0 +1,35 @@
+// Baseline format: 1.0
+ContextFirst: androidx.fragment.app.FragmentActivity#onCreateView(String, android.content.Context, android.util.AttributeSet) parameter #1:
+ Context is distinct, so it must be the first argument (method `onCreateView`)
+ContextFirst: androidx.fragment.app.FragmentActivity#onCreateView(android.view.View, String, android.content.Context, android.util.AttributeSet) parameter #2:
+ Context is distinct, so it must be the first argument (method `onCreateView`)
+ContextFirst: androidx.fragment.app.FragmentController#onCreateView(android.view.View, String, android.content.Context, android.util.AttributeSet) parameter #2:
+ Context is distinct, so it must be the first argument (method `onCreateView`)
+ContextFirst: androidx.fragment.app.FragmentManager.FragmentLifecycleCallbacks#onFragmentAttached(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.content.Context) parameter #2:
+ Context is distinct, so it must be the first argument (method `onFragmentAttached`)
+ContextFirst: androidx.fragment.app.FragmentManager.FragmentLifecycleCallbacks#onFragmentPreAttached(androidx.fragment.app.FragmentManager, androidx.fragment.app.Fragment, android.content.Context) parameter #2:
+ Context is distinct, so it must be the first argument (method `onFragmentPreAttached`)
+
+
+ForbiddenSuperClass: androidx.fragment.app.FragmentActivity:
+ FragmentActivity should not extend `Activity`. Activity subclasses are impossible to compose. Expose a composable API instead.
+
+
+NoClone: androidx.fragment.app.Fragment#dump(String, java.io.FileDescriptor, java.io.PrintWriter, String[]) parameter #1:
+ Must use ParcelFileDescriptor instead of FileDescriptor in parameter fd in androidx.fragment.app.Fragment.dump(String prefix, java.io.FileDescriptor fd, java.io.PrintWriter writer, String[] args)
+NoClone: androidx.fragment.app.FragmentActivity#dump(String, java.io.FileDescriptor, java.io.PrintWriter, String[]) parameter #1:
+ Must use ParcelFileDescriptor instead of FileDescriptor in parameter fd in androidx.fragment.app.FragmentActivity.dump(String prefix, java.io.FileDescriptor fd, java.io.PrintWriter writer, String[] args)
+NoClone: androidx.fragment.app.FragmentController#dumpLoaders(String, java.io.FileDescriptor, java.io.PrintWriter, String[]) parameter #1:
+ Must use ParcelFileDescriptor instead of FileDescriptor in parameter fd in androidx.fragment.app.FragmentController.dumpLoaders(String prefix, java.io.FileDescriptor fd, java.io.PrintWriter writer, String[] args)
+NoClone: androidx.fragment.app.FragmentHostCallback#onDump(String, java.io.FileDescriptor, java.io.PrintWriter, String[]) parameter #1:
+ Must use ParcelFileDescriptor instead of FileDescriptor in parameter fd in androidx.fragment.app.FragmentHostCallback.onDump(String prefix, java.io.FileDescriptor fd, java.io.PrintWriter writer, String[] args)
+NoClone: androidx.fragment.app.FragmentManager#dump(String, java.io.FileDescriptor, java.io.PrintWriter, String[]) parameter #1:
+ Must use ParcelFileDescriptor instead of FileDescriptor in parameter fd in androidx.fragment.app.FragmentManager.dump(String prefix, java.io.FileDescriptor fd, java.io.PrintWriter writer, String[] args)
+
+
+ParcelNotFinal: androidx.fragment.app.Fragment.SavedState:
+ Parcelable classes must be final: androidx.fragment.app.Fragment.SavedState is not final
+
+
+SingularCallback: androidx.fragment.app.FragmentManager.FragmentLifecycleCallbacks:
+ Callback class names should be singular: FragmentLifecycleCallbacks
diff --git a/heifwriter/api/api_lint.ignore b/heifwriter/api/api_lint.ignore
new file mode 100644
index 0000000..3dd94ae
--- /dev/null
+++ b/heifwriter/api/api_lint.ignore
@@ -0,0 +1,17 @@
+// Baseline format: 1.0
+GenericException: androidx.heifwriter.HeifWriter#stop(long):
+ Methods must not throw generic exceptions (`java.lang.Exception`)
+
+
+NoClone: androidx.heifwriter.HeifWriter.Builder#Builder(java.io.FileDescriptor, int, int, int) parameter #0:
+ Must use ParcelFileDescriptor instead of FileDescriptor in parameter fd in androidx.heifwriter.HeifWriter.Builder(java.io.FileDescriptor fd, int width, int height, int inputMode)
+
+
+VisiblySynchronized: androidx.heifwriter.HeifWriter#addBitmap(android.graphics.Bitmap):
+ Internal locks must not be exposed (synchronizing on this or class is still externally observable): method androidx.heifwriter.HeifWriter.addBitmap(android.graphics.Bitmap)
+VisiblySynchronized: androidx.heifwriter.HeifWriter#addYuvBuffer(int, byte[]):
+ Internal locks must not be exposed (synchronizing on this or class is still externally observable): method androidx.heifwriter.HeifWriter.addYuvBuffer(int,byte[])
+VisiblySynchronized: androidx.heifwriter.HeifWriter#setInputEndOfStreamTimestamp(long):
+ Internal locks must not be exposed (synchronizing on this or class is still externally observable): method androidx.heifwriter.HeifWriter.setInputEndOfStreamTimestamp(long)
+VisiblySynchronized: androidx.heifwriter.HeifWriter#stop(long):
+ Internal locks must not be exposed (synchronizing on this or class is still externally observable): method androidx.heifwriter.HeifWriter.stop(long)
diff --git a/jetifier/jetifier/migration.config b/jetifier/jetifier/migration.config
index 0ce56a0..1472240 100644
--- a/jetifier/jetifier/migration.config
+++ b/jetifier/jetifier/migration.config
@@ -949,6 +949,10 @@
"from": "androidx/swiperefreshlayout/widget/annotations",
"to": "android/support/v4/widget/annotations"
},
+ {
+ "from": "androidx/annotation/experimental/(.*)",
+ "to": "ignore"
+ }
],
"packageMap": [
{
@@ -1286,6 +1290,10 @@
{
"from": "androidx/biometric",
"to": "androidx/biometric"
+ },
+ {
+ "from": "androidx/annotation/experimental",
+ "to": "androidx/annotation/experimental"
}
],
"pomRules": [
@@ -3064,7 +3072,31 @@
"artifactId": "camera-testing",
"version": "{newCameraVersion}"
}
- }
+ },
+ {
+ "from": {
+ "groupId": "androidx.annotation",
+ "artifactId": "annotation-experimental",
+ "version": "{newSlVersion}"
+ },
+ "to": {
+ "groupId": "androidx.annotation",
+ "artifactId": "annotation-experimental",
+ "version": "{newSlVersion}"
+ }
+ },
+ {
+ "from": {
+ "groupId": "androidx.annotation",
+ "artifactId": "annotation-experimental-lint",
+ "version": "{newSlVersion}"
+ },
+ "to": {
+ "groupId": "androidx.annotation",
+ "artifactId": "annotation-experimental-lint",
+ "version": "{newSlVersion}"
+ }
+ }
],
"versions": {
"latestReleased": {
diff --git a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/metainf/MetaInfTransformer.kt b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/metainf/MetaInfTransformer.kt
index bba3289..82c658e 100644
--- a/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/metainf/MetaInfTransformer.kt
+++ b/jetifier/jetifier/processor/src/main/kotlin/com/android/tools/build/jetifier/processor/transform/metainf/MetaInfTransformer.kt
@@ -42,7 +42,8 @@
"androidx.car_car-moderator.version",
"androidx.activity_activity-ktx.version",
"androidx.lifecycle_lifecycle-runtime-ktx.version",
- "androidx.dynamicanimation_dynamicanimation-ktx.version"
+ "androidx.dynamicanimation_dynamicanimation-ktx.version",
+ "androidx.annotation_annotation-experimental.version"
)
}
@@ -83,4 +84,4 @@
val newPath = Paths.get(dirPath, newFileName)
file.updateRelativePath(newPath)
}
-}
\ No newline at end of file
+}
diff --git a/leanback-preference/api/api_lint.ignore b/leanback-preference/api/api_lint.ignore
new file mode 100644
index 0000000..1ec1d8d
--- /dev/null
+++ b/leanback-preference/api/api_lint.ignore
@@ -0,0 +1,5 @@
+// Baseline format: 1.0
+ActionValue: androidx.leanback.preference.LeanbackEditTextPreferenceDialogFragmentCompat#EXTRA_IME_OPTIONS:
+ Inconsistent extra value; expected `androidx.leanback.preference.extra.IME_OPTIONS`, was `ime_option`
+ActionValue: androidx.leanback.preference.LeanbackEditTextPreferenceDialogFragmentCompat#EXTRA_INPUT_TYPE:
+ Inconsistent extra value; expected `androidx.leanback.preference.extra.INPUT_TYPE`, was `input_type`
diff --git a/leanback/api/api_lint.ignore b/leanback/api/api_lint.ignore
new file mode 100644
index 0000000..b8d6059
--- /dev/null
+++ b/leanback/api/api_lint.ignore
@@ -0,0 +1,93 @@
+// Baseline format: 1.0
+ActionValue: androidx.leanback.app.GuidedStepFragment#EXTRA_UI_STYLE:
+ Inconsistent extra value; expected `androidx.leanback.app.extra.UI_STYLE`, was `uiStyle`
+ActionValue: androidx.leanback.app.GuidedStepSupportFragment#EXTRA_UI_STYLE:
+ Inconsistent extra value; expected `androidx.leanback.app.extra.UI_STYLE`, was `uiStyle`
+
+
+AutoBoxing: androidx.leanback.widget.Parallax.FloatProperty#get(androidx.leanback.widget.Parallax):
+ Must avoid boxed primitives (`java.lang.Float`)
+AutoBoxing: androidx.leanback.widget.Parallax.FloatProperty#set(androidx.leanback.widget.Parallax, Float) parameter #1:
+ Must avoid boxed primitives (`java.lang.Float`)
+AutoBoxing: androidx.leanback.widget.Parallax.IntProperty#get(androidx.leanback.widget.Parallax):
+ Must avoid boxed primitives (`java.lang.Integer`)
+AutoBoxing: androidx.leanback.widget.Parallax.IntProperty#set(androidx.leanback.widget.Parallax, Integer) parameter #1:
+ Must avoid boxed primitives (`java.lang.Integer`)
+AutoBoxing: androidx.leanback.widget.ParallaxTarget#directUpdate(Number) parameter #0:
+ Must avoid boxed primitives (`java.lang.Number`)
+AutoBoxing: androidx.leanback.widget.ParallaxTarget.DirectPropertyTarget#directUpdate(Number) parameter #0:
+ Must avoid boxed primitives (`java.lang.Number`)
+
+
+CallbackMethodName: androidx.leanback.widget.DiffCallback:
+ Callback method names must follow the on<Something> style: areItemsTheSame
+CallbackMethodName: androidx.leanback.widget.GuidedActionDiffCallback:
+ Callback method names must follow the on<Something> style: getInstance
+CallbackMethodName: androidx.leanback.widget.SearchBar.SearchBarPermissionListener:
+ Listener method names must follow the on<Something> style: requestAudioPermission
+
+
+CallbackName: androidx.leanback.widget.ObjectAdapter.DataObserver:
+ Class should be named DataCallback
+
+
+ConcreteCollection: androidx.leanback.widget.ItemBridgeAdapter#getPresenterMapper():
+ Return type is concrete collection (`java.util.ArrayList`); must be higher-level interface
+ConcreteCollection: androidx.leanback.widget.ItemBridgeAdapter#setPresenterMapper(java.util.ArrayList<androidx.leanback.widget.Presenter>) parameter #0:
+ Parameter type is concrete collection (`java.util.ArrayList`); must be higher-level interface
+
+
+ContextFirst: androidx.leanback.widget.GuidedAction.BuilderBase#iconResourceId(int, android.content.Context) parameter #1:
+ Context is distinct, so it must be the first argument (method `iconResourceId`)
+ContextFirst: androidx.leanback.widget.PlaybackControlsRow.ThumbsAction#ThumbsAction(int, android.content.Context, int, int) parameter #1:
+ Context is distinct, so it must be the first argument (method `ThumbsAction`)
+
+
+FractionFloat: androidx.leanback.widget.Parallax.FloatProperty#atFraction(float):
+ Fractions must use floats, was `androidx.leanback.widget.Parallax.PropertyMarkerValue` in `atFraction`
+FractionFloat: androidx.leanback.widget.Parallax.IntProperty#atFraction(float):
+ Fractions must use floats, was `androidx.leanback.widget.Parallax.PropertyMarkerValue` in `atFraction`
+
+
+KotlinOperator: androidx.leanback.widget.ArrayObjectAdapter#get(int):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.leanback.widget.CursorObjectAdapter#get(int):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.leanback.widget.ObjectAdapter#get(int):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.leanback.widget.Parallax.FloatProperty#get(androidx.leanback.widget.Parallax):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.leanback.widget.Parallax.FloatProperty#set(androidx.leanback.widget.Parallax, Float):
+ Method can be invoked with an indexing operator from Kotlin: `set` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.leanback.widget.Parallax.IntProperty#get(androidx.leanback.widget.Parallax):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.leanback.widget.Parallax.IntProperty#set(androidx.leanback.widget.Parallax, Integer):
+ Method can be invoked with an indexing operator from Kotlin: `set` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.leanback.widget.SparseArrayObjectAdapter#get(int):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.leanback.widget.SparseArrayObjectAdapter#set(int, Object):
+ Method can be invoked with an indexing operator from Kotlin: `set` (this is usually desirable; just make sure it makes sense for this type of object)
+
+
+ListenerInterface: androidx.leanback.app.BrowseSupportFragment.BrowseTransitionListener:
+ Listeners should be an interface, or otherwise renamed Callback: BrowseTransitionListener
+ListenerInterface: androidx.leanback.widget.DetailsOverviewRow.Listener:
+ Listeners should be an interface, or otherwise renamed Callback: Listener
+ListenerInterface: androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter.Listener:
+ Listeners should be an interface, or otherwise renamed Callback: Listener
+ListenerInterface: androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter.ViewHolder.DetailsOverviewRowListener:
+ Listeners should be an interface, or otherwise renamed Callback: DetailsOverviewRowListener
+ListenerInterface: androidx.leanback.widget.ItemBridgeAdapter.AdapterListener:
+ Listeners should be an interface, or otherwise renamed Callback: AdapterListener
+ListenerInterface: androidx.leanback.widget.OnChildViewHolderSelectedListener:
+ Listeners should be an interface, or otherwise renamed Callback: OnChildViewHolderSelectedListener
+
+
+RegistrationName: androidx.leanback.media.PlaybackGlue#addPlayerCallback(androidx.leanback.media.PlaybackGlue.PlayerCallback):
+ Callback methods should be named register/unregister; was addPlayerCallback
+RegistrationName: androidx.leanback.media.PlaybackGlue#removePlayerCallback(androidx.leanback.media.PlaybackGlue.PlayerCallback):
+ Callback methods should be named register/unregister; was removePlayerCallback
+
+
+SingletonConstructor: androidx.leanback.widget.GuidedActionDiffCallback#GuidedActionDiffCallback():
+ Singleton classes should use `getInstance()` methods: `GuidedActionDiffCallback`
diff --git a/lifecycle/lifecycle-common/api/api_lint.ignore b/lifecycle/lifecycle-common/api/api_lint.ignore
new file mode 100644
index 0000000..b22f657
--- /dev/null
+++ b/lifecycle/lifecycle-common/api/api_lint.ignore
@@ -0,0 +1,7 @@
+// Baseline format: 1.0
+CallbackName: androidx.lifecycle.DefaultLifecycleObserver:
+ Class should be named DefaultLifecycleCallback
+CallbackName: androidx.lifecycle.LifecycleEventObserver:
+ Class should be named LifecycleEventCallback
+CallbackName: androidx.lifecycle.LifecycleObserver:
+ Class should be named LifecycleCallback
diff --git a/lifecycle/lifecycle-livedata-core/api/api_lint.ignore b/lifecycle/lifecycle-livedata-core/api/api_lint.ignore
new file mode 100644
index 0000000..f68178d
--- /dev/null
+++ b/lifecycle/lifecycle-livedata-core/api/api_lint.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+CallbackName: androidx.lifecycle.Observer:
+ Class should be named Callback
diff --git a/lifecycle/lifecycle-viewmodel-savedstate/api/api_lint.ignore b/lifecycle/lifecycle-viewmodel-savedstate/api/api_lint.ignore
new file mode 100644
index 0000000..420fd20
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel-savedstate/api/api_lint.ignore
@@ -0,0 +1,7 @@
+// Baseline format: 1.0
+KotlinOperator: androidx.lifecycle.SavedStateHandle#contains(String):
+ Method can be invoked as a "in" operator from Kotlin: `contains` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.lifecycle.SavedStateHandle#get(String):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.lifecycle.SavedStateHandle#set(String, T):
+ Method can be invoked with an indexing operator from Kotlin: `set` (this is usually desirable; just make sure it makes sense for this type of object)
diff --git a/lifecycle/lifecycle-viewmodel/api/api_lint.ignore b/lifecycle/lifecycle-viewmodel/api/api_lint.ignore
new file mode 100644
index 0000000..8582fa1
--- /dev/null
+++ b/lifecycle/lifecycle-viewmodel/api/api_lint.ignore
@@ -0,0 +1,9 @@
+// Baseline format: 1.0
+KotlinOperator: androidx.lifecycle.ViewModelProvider#get(Class<T>):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.lifecycle.ViewModelProvider#get(String, Class<T>):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+
+
+SingletonConstructor: androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory#AndroidViewModelFactory(android.app.Application):
+ Singleton classes should use `getInstance()` methods: `AndroidViewModelFactory`
diff --git a/loader/loader/api/api_lint.ignore b/loader/loader/api/api_lint.ignore
new file mode 100644
index 0000000..cb37084
--- /dev/null
+++ b/loader/loader/api/api_lint.ignore
@@ -0,0 +1,37 @@
+// Baseline format: 1.0
+CallbackName: androidx.loader.content.Loader.ForceLoadContentObserver:
+ Class should be named ForceLoadContentCallback
+
+
+NoClone: androidx.loader.app.LoaderManager#dump(String, java.io.FileDescriptor, java.io.PrintWriter, String[]) parameter #1:
+ Must use ParcelFileDescriptor instead of FileDescriptor in parameter fd in androidx.loader.app.LoaderManager.dump(String prefix, java.io.FileDescriptor fd, java.io.PrintWriter writer, String[] args)
+NoClone: androidx.loader.content.AsyncTaskLoader#dump(String, java.io.FileDescriptor, java.io.PrintWriter, String[]) parameter #1:
+ Must use ParcelFileDescriptor instead of FileDescriptor in parameter fd in androidx.loader.content.AsyncTaskLoader.dump(String prefix, java.io.FileDescriptor fd, java.io.PrintWriter writer, String[] args)
+NoClone: androidx.loader.content.CursorLoader#dump(String, java.io.FileDescriptor, java.io.PrintWriter, String[]) parameter #1:
+ Must use ParcelFileDescriptor instead of FileDescriptor in parameter fd in androidx.loader.content.CursorLoader.dump(String prefix, java.io.FileDescriptor fd, java.io.PrintWriter writer, String[] args)
+NoClone: androidx.loader.content.Loader#dump(String, java.io.FileDescriptor, java.io.PrintWriter, String[]) parameter #1:
+ Must use ParcelFileDescriptor instead of FileDescriptor in parameter fd in androidx.loader.content.Loader.dump(String prefix, java.io.FileDescriptor fd, java.io.PrintWriter writer, String[] args)
+
+
+RegistrationName: androidx.loader.content.Loader#registerListener(int, androidx.loader.content.Loader.OnLoadCompleteListener<D>):
+ Listener methods should be named add/remove; was registerListener
+RegistrationName: androidx.loader.content.Loader#registerOnLoadCanceledListener(androidx.loader.content.Loader.OnLoadCanceledListener<D>):
+ Listener methods should be named add/remove; was registerOnLoadCanceledListener
+RegistrationName: androidx.loader.content.Loader#unregisterListener(androidx.loader.content.Loader.OnLoadCompleteListener<D>):
+ Listener methods should be named add/remove; was unregisterListener
+RegistrationName: androidx.loader.content.Loader#unregisterOnLoadCanceledListener(androidx.loader.content.Loader.OnLoadCanceledListener<D>):
+ Listener methods should be named add/remove; was unregisterOnLoadCanceledListener
+
+
+SingletonConstructor: androidx.loader.app.LoaderManager#LoaderManager():
+ Singleton classes should use `getInstance()` methods: `LoaderManager`
+
+
+SingularCallback: androidx.loader.app.LoaderManager.LoaderCallbacks:
+ Callback class names should be singular: LoaderCallbacks
+
+
+VisiblySynchronized: androidx.loader.content.CursorLoader#cancelLoadInBackground():
+ Internal locks must not be exposed (synchronizing on this or class is still externally observable): method androidx.loader.content.CursorLoader.cancelLoadInBackground()
+VisiblySynchronized: androidx.loader.content.CursorLoader#loadInBackground():
+ Internal locks must not be exposed (synchronizing on this or class is still externally observable): method androidx.loader.content.CursorLoader.loadInBackground()
diff --git a/media/api/api_lint.ignore b/media/api/api_lint.ignore
new file mode 100644
index 0000000..aa6beea
--- /dev/null
+++ b/media/api/api_lint.ignore
@@ -0,0 +1,57 @@
+// Baseline format: 1.0
+ActionValue: android.support.v4.media.MediaBrowserCompat#EXTRA_DOWNLOAD_PROGRESS:
+ Inconsistent extra value; expected `android.support.v4.media.extra.DOWNLOAD_PROGRESS`, was `android.media.browse.extra.DOWNLOAD_PROGRESS`
+ActionValue: android.support.v4.media.MediaBrowserCompat#EXTRA_MEDIA_ID:
+ Inconsistent extra value; expected `android.support.v4.media.extra.MEDIA_ID`, was `android.media.browse.extra.MEDIA_ID`
+ActionValue: android.support.v4.media.MediaBrowserCompat#EXTRA_PAGE:
+ Inconsistent extra value; expected `android.support.v4.media.extra.PAGE`, was `android.media.browse.extra.PAGE`
+ActionValue: android.support.v4.media.MediaBrowserCompat#EXTRA_PAGE_SIZE:
+ Inconsistent extra value; expected `android.support.v4.media.extra.PAGE_SIZE`, was `android.media.browse.extra.PAGE_SIZE`
+ActionValue: android.support.v4.media.MediaDescriptionCompat#EXTRA_BT_FOLDER_TYPE:
+ Inconsistent extra value; expected `android.support.v4.media.extra.BT_FOLDER_TYPE`, was `android.media.extra.BT_FOLDER_TYPE`
+ActionValue: android.support.v4.media.MediaDescriptionCompat#EXTRA_DOWNLOAD_STATUS:
+ Inconsistent extra value; expected `android.support.v4.media.extra.DOWNLOAD_STATUS`, was `android.media.extra.DOWNLOAD_STATUS`
+ActionValue: android.support.v4.media.session.MediaControllerCompat.TransportControls#EXTRA_LEGACY_STREAM_TYPE:
+ Inconsistent extra value; expected `android.support.v4.media.session.extra.LEGACY_STREAM_TYPE`, was `android.media.session.extra.LEGACY_STREAM_TYPE`
+ActionValue: androidx.media.MediaBrowserServiceCompat.BrowserRoot#EXTRA_OFFLINE:
+ Inconsistent extra value; expected `androidx.media.extra.OFFLINE`, was `android.service.media.extra.OFFLINE`
+ActionValue: androidx.media.MediaBrowserServiceCompat.BrowserRoot#EXTRA_RECENT:
+ Inconsistent extra value; expected `androidx.media.extra.RECENT`, was `android.service.media.extra.RECENT`
+ActionValue: androidx.media.MediaBrowserServiceCompat.BrowserRoot#EXTRA_SUGGESTED:
+ Inconsistent extra value; expected `androidx.media.extra.SUGGESTED`, was `android.service.media.extra.SUGGESTED`
+
+
+CallbackMethodName: android.support.v4.media.session.MediaControllerCompat.Callback:
+ Callback method names must follow the on<Something> style: binderDied
+
+
+ContextNameSuffix: androidx.media.MediaBrowserServiceCompat:
+ Inconsistent class name; should be `<Foo>Service`, was `MediaBrowserServiceCompat`
+
+
+IntentName: android.support.v4.media.MediaBrowserCompat#CUSTOM_ACTION_DOWNLOAD:
+ Intent action constant name must be ACTION_FOO: CUSTOM_ACTION_DOWNLOAD
+IntentName: android.support.v4.media.MediaBrowserCompat#CUSTOM_ACTION_REMOVE_DOWNLOADED_FILE:
+ Intent action constant name must be ACTION_FOO: CUSTOM_ACTION_REMOVE_DOWNLOADED_FILE
+
+
+InterfaceConstant: androidx.media.MediaBrowserServiceCompat#SERVICE_INTERFACE:
+ Inconsistent interface constant; expected 'MediaBrowserServiceCompat'`
+
+
+NoClone: androidx.media.MediaBrowserServiceCompat#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]) parameter #0:
+ Must use ParcelFileDescriptor instead of FileDescriptor in parameter fd in androidx.media.MediaBrowserServiceCompat.dump(java.io.FileDescriptor fd, java.io.PrintWriter writer, String[] args)
+
+
+ParcelConstructor: android.support.v4.media.session.ParcelableVolumeInfo#ParcelableVolumeInfo(android.os.Parcel):
+ Parcelable inflation is exposed through CREATOR, not raw constructors, in android.support.v4.media.session.ParcelableVolumeInfo
+
+
+ParcelNotFinal: android.support.v4.media.MediaBrowserCompat.MediaItem:
+ Parcelable classes must be final: android.support.v4.media.MediaBrowserCompat.MediaItem is not final
+ParcelNotFinal: android.support.v4.media.session.ParcelableVolumeInfo:
+ Parcelable classes must be final: android.support.v4.media.session.ParcelableVolumeInfo is not final
+
+
+RethrowRemoteException: android.support.v4.media.session.MediaControllerCompat#MediaControllerCompat(android.content.Context, android.support.v4.media.session.MediaSessionCompat.Token):
+ Methods calling into system server should rethrow `RemoteException` as `RuntimeException` (but do not list it in the throws clause)
diff --git a/media2/common/api/api_lint.ignore b/media2/common/api/api_lint.ignore
new file mode 100644
index 0000000..42cac4b
--- /dev/null
+++ b/media2/common/api/api_lint.ignore
@@ -0,0 +1,7 @@
+// Baseline format: 1.0
+CallbackMethodName: androidx.media2.common.DataSourceCallback:
+ Callback method names must follow the on<Something> style: readAt
+
+
+IntentName: androidx.media2.common.MediaMetadata#METADATA_KEY_EXTRAS:
+ Intent extra constant name must be EXTRA_FOO: METADATA_KEY_EXTRAS
diff --git a/media2/integration-tests/testapp/src/main/java/androidx/media2/integration/testapp/VideoSessionService.java b/media2/integration-tests/testapp/src/main/java/androidx/media2/integration/testapp/VideoSessionService.java
index a5b550d..13d9145 100644
--- a/media2/integration-tests/testapp/src/main/java/androidx/media2/integration/testapp/VideoSessionService.java
+++ b/media2/integration-tests/testapp/src/main/java/androidx/media2/integration/testapp/VideoSessionService.java
@@ -16,7 +16,12 @@
package androidx.media2.integration.testapp;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.media.MediaMetadataRetriever;
import android.net.Uri;
+import android.os.AsyncTask;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -40,6 +45,7 @@
private MediaPlayer mMediaPlayer;
private MediaSession mMediaSession;
+ private UriMediaItem mCurrentItem;
private AudioAttributesCompat mAudioAttributes;
@Override
@@ -99,17 +105,102 @@
@Override
public MediaItem onCreateMediaItem(@NonNull MediaSession session,
@NonNull MediaSession.ControllerInfo controller, @NonNull String mediaId) {
+ // TODO: Need to check if current media item uri is equal to the given media id.
MediaMetadata metadata = new MediaMetadata.Builder()
.putString(MediaMetadata.METADATA_KEY_MEDIA_ID, mediaId)
.build();
- UriMediaItem mediaItem = new UriMediaItem.Builder(Uri.parse(mediaId))
+ mCurrentItem = new UriMediaItem.Builder(Uri.parse(mediaId))
.setMetadata(metadata)
.build();
+ MetadataExtractTask task = new MetadataExtractTask(mCurrentItem,
+ VideoSessionService.this);
+ task.execute();
// TODO: Temporary fix for multiple calls of setMediaItem not working properly.
// (b/135728285)
mMediaPlayer.reset();
mMediaPlayer.setAudioAttributes(mAudioAttributes);
- return mediaItem;
+ return mCurrentItem;
+ }
+ }
+
+ private class MetadataExtractTask extends AsyncTask<Void, Void, MediaMetadata> {
+ private MediaItem mItem;
+ private Context mContext;
+
+ MetadataExtractTask(MediaItem mediaItem, Context context) {
+ mItem = mediaItem;
+ mContext = context;
+ }
+
+ @Override
+ protected MediaMetadata doInBackground(Void... params) {
+ return extractMetadata(mItem);
+ }
+
+ @Override
+ protected void onPostExecute(MediaMetadata metadata) {
+ if (metadata != null) {
+ mItem.setMetadata(metadata);
+ }
+ }
+
+ MediaMetadata extractMetadata(MediaItem mediaItem) {
+ MediaMetadataRetriever retriever = null;
+ try {
+ if (mediaItem == null) {
+ return null;
+ } else if (mediaItem instanceof UriMediaItem) {
+ Uri uri = ((UriMediaItem) mediaItem).getUri();
+ retriever = new MediaMetadataRetriever();
+ retriever.setDataSource(mContext, uri);
+ }
+ } catch (IllegalArgumentException e) {
+ retriever = null;
+ }
+
+ // Do not extract metadata of a media item which is not the current item.
+ if (mediaItem != mCurrentItem) {
+ if (retriever != null) {
+ retriever.release();
+ }
+ return null;
+ }
+ String title = extractString(retriever, MediaMetadataRetriever.METADATA_KEY_TITLE);
+ String musicArtistText = extractString(retriever,
+ MediaMetadataRetriever.METADATA_KEY_ARTIST);
+ Bitmap musicAlbumBitmap = extractAlbumArt(retriever);
+
+ if (retriever != null) {
+ retriever.release();
+ }
+
+ // Set duration and title values as MediaMetadata for MediaControlView
+ MediaMetadata.Builder builder = new MediaMetadata.Builder(mCurrentItem.getMetadata());
+
+ builder.putString(MediaMetadata.METADATA_KEY_TITLE, title);
+ builder.putString(MediaMetadata.METADATA_KEY_ARTIST, musicArtistText);
+ builder.putString(
+ MediaMetadata.METADATA_KEY_MEDIA_ID, mediaItem.getMediaId());
+ builder.putLong(MediaMetadata.METADATA_KEY_PLAYABLE, 1);
+ builder.putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, musicAlbumBitmap);
+ return builder.build();
+ }
+
+ private String extractString(MediaMetadataRetriever retriever, int intKey) {
+ if (retriever != null) {
+ return retriever.extractMetadata(intKey);
+ }
+ return null;
+ }
+
+ private Bitmap extractAlbumArt(MediaMetadataRetriever retriever) {
+ if (retriever != null) {
+ byte[] album = retriever.getEmbeddedPicture();
+ if (album != null) {
+ return BitmapFactory.decodeByteArray(album, 0, album.length);
+ }
+ }
+ return null;
}
}
}
diff --git a/media2/player/api/api_lint.ignore b/media2/player/api/api_lint.ignore
new file mode 100644
index 0000000..821e3a3
--- /dev/null
+++ b/media2/player/api/api_lint.ignore
@@ -0,0 +1,11 @@
+// Baseline format: 1.0
+AutoBoxing: androidx.media2.player.PlaybackParams#getAudioFallbackMode():
+ Must avoid boxed primitives (`java.lang.Integer`)
+AutoBoxing: androidx.media2.player.PlaybackParams#getPitch():
+ Must avoid boxed primitives (`java.lang.Float`)
+AutoBoxing: androidx.media2.player.PlaybackParams#getSpeed():
+ Must avoid boxed primitives (`java.lang.Float`)
+
+
+GenericException: androidx.media2.player.MediaPlayer#close():
+ Methods must not throw generic exceptions (`java.lang.Exception`)
diff --git a/media2/session/api/api_lint.ignore b/media2/session/api/api_lint.ignore
new file mode 100644
index 0000000..3cd34b0
--- /dev/null
+++ b/media2/session/api/api_lint.ignore
@@ -0,0 +1,5 @@
+// Baseline format: 1.0
+InterfaceConstant: androidx.media2.session.MediaLibraryService#SERVICE_INTERFACE:
+ Inconsistent interface constant; expected 'MediaLibraryService'`
+InterfaceConstant: androidx.media2.session.MediaSessionService#SERVICE_INTERFACE:
+ Inconsistent interface constant; expected 'MediaSessionService'`
diff --git a/media2/widget/src/androidTest/java/androidx/media2/widget/MediaControlView_WithControllerTest.java b/media2/widget/src/androidTest/java/androidx/media2/widget/MediaControlView_WithControllerTest.java
new file mode 100644
index 0000000..8d7b96f
--- /dev/null
+++ b/media2/widget/src/androidTest/java/androidx/media2/widget/MediaControlView_WithControllerTest.java
@@ -0,0 +1,40 @@
+/*
+ * 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.media2.widget;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.media2.common.MediaItem;
+import androidx.media2.session.MediaController;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.LargeTest;
+
+import org.junit.runner.RunWith;
+
+/**
+ * Test {@link MediaControlView} with a {@link MediaController}.
+ * Please place actual test cases in {@link MediaControlView_WithSthTestBase}.
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class MediaControlView_WithControllerTest extends MediaControlView_WithSthTestBase {
+ @Override
+ PlayerWrapper createPlayerWrapper(@NonNull PlayerWrapper.PlayerCallback callback,
+ @Nullable MediaItem item) {
+ return createPlayerWrapperOfController(callback, item);
+ }
+}
diff --git a/media2/widget/src/androidTest/java/androidx/media2/widget/MediaControlView_WithPlayerTest.java b/media2/widget/src/androidTest/java/androidx/media2/widget/MediaControlView_WithPlayerTest.java
index 4c91946..10bc874 100644
--- a/media2/widget/src/androidTest/java/androidx/media2/widget/MediaControlView_WithPlayerTest.java
+++ b/media2/widget/src/androidTest/java/androidx/media2/widget/MediaControlView_WithPlayerTest.java
@@ -16,338 +16,25 @@
package androidx.media2.widget;
-import static androidx.test.espresso.Espresso.onView;
-import static androidx.test.espresso.action.ViewActions.click;
-import static androidx.test.espresso.assertion.ViewAssertions.matches;
-import static androidx.test.espresso.matcher.RootMatchers.isPlatformPopup;
-import static androidx.test.espresso.matcher.ViewMatchers.isClickable;
-import static androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
-import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static androidx.test.espresso.matcher.ViewMatchers.withId;
-import static androidx.test.espresso.matcher.ViewMatchers.withText;
-
-import static org.hamcrest.CoreMatchers.allOf;
-import static org.hamcrest.CoreMatchers.not;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-
-import android.net.Uri;
-
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.media2.common.MediaItem;
-import androidx.media2.common.MediaMetadata;
import androidx.media2.common.SessionPlayer;
-import androidx.media2.common.SessionPlayer.TrackInfo;
-import androidx.media2.player.MediaPlayer;
-import androidx.media2.widget.test.R;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
-import androidx.test.rule.ActivityTestRule;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
import org.junit.runner.RunWith;
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
/**
* Test {@link MediaControlView} with a {@link SessionPlayer}.
+ * Please place actual test cases in {@link MediaControlView_WithSthTestBase}.
*/
@RunWith(AndroidJUnit4.class)
@LargeTest
-public class MediaControlView_WithPlayerTest extends MediaWidgetTestBase {
- private static final long FFWD_MS = 30000L;
- private static final long REW_MS = 10000L;
-
- private SessionPlayer mPlayer;
- private MediaControlViewTestActivity mActivity;
- private MediaControlView mMediaControlView;
- private MediaItem mFileSchemeMediaItem;
-
- @Rule
- public ActivityTestRule<MediaControlViewTestActivity> mActivityRule =
- new ActivityTestRule<>(MediaControlViewTestActivity.class);
-
- @Before
- public void setup() throws Throwable {
- mPlayer = new MediaPlayer(mContext);
- mActivity = mActivityRule.getActivity();
- mMediaControlView = mActivity.findViewById(R.id.mediacontrolview);
- mActivityRule.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mMediaControlView.setPlayer(mPlayer);
- }
- });
-
- Uri fileSchemeUri = Uri.parse("android.resource://" + mContext.getPackageName() + "/"
- + R.raw.test_file_scheme_video);
- mFileSchemeMediaItem = createTestMediaItem(fileSchemeUri);
-
- setKeepScreenOn(mActivityRule);
- checkAttachedToWindow(mMediaControlView);
- }
-
- @After
- public void tearDown() throws Throwable {
- mActivityRule.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- try {
- mPlayer.close();
- } catch (Exception ex) {
- // ignore
- }
- }
- });
- }
-
- @Test
- public void testPlayPauseButtonClick() throws Throwable {
- final CountDownLatch latchForPausedState = new CountDownLatch(1);
- final CountDownLatch latchForPlayingState = new CountDownLatch(1);
- registerCallback(new SessionPlayer.PlayerCallback() {
- @Override
- public void onPlayerStateChanged(@NonNull SessionPlayer player, int state) {
- if (state == SessionPlayer.PLAYER_STATE_PAUSED) {
- latchForPausedState.countDown();
- } else if (state == SessionPlayer.PLAYER_STATE_PLAYING) {
- latchForPlayingState.countDown();
- }
- }
- });
- setAndPrepare(mFileSchemeMediaItem);
- assertTrue(latchForPausedState.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
- onView(allOf(withId(R.id.pause), isCompletelyDisplayed())).perform(click());
- assertTrue(latchForPlayingState.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
- }
-
- @Test
- public void testFfwdButtonClick() throws Throwable {
- final CountDownLatch latchForPausedState = new CountDownLatch(1);
- final CountDownLatch latchForFfwd = new CountDownLatch(1);
- registerCallback(new SessionPlayer.PlayerCallback() {
- @Override
- public void onSeekCompleted(@NonNull SessionPlayer player, long position) {
- if (position >= FFWD_MS) {
- latchForFfwd.countDown();
- }
- }
-
- @Override
- public void onPlayerStateChanged(@NonNull SessionPlayer player, int state) {
- if (state == SessionPlayer.PLAYER_STATE_PAUSED) {
- latchForPausedState.countDown();
- }
- }
- });
- setAndPrepare(mFileSchemeMediaItem);
- assertTrue(latchForPausedState.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
- onView(allOf(withId(R.id.ffwd), isCompletelyDisplayed())).perform(click());
- assertTrue(latchForFfwd.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
- }
-
- @Test
- public void testRewButtonClick() throws Throwable {
- final CountDownLatch latchForFfwd = new CountDownLatch(1);
- final CountDownLatch latchForRew = new CountDownLatch(1);
- registerCallback(new SessionPlayer.PlayerCallback() {
- long mExpectedPosition = FFWD_MS;
- final long mDelta = 1000L;
-
- @Override
- public void onPlayerStateChanged(@NonNull SessionPlayer player, int state) {
- if (state == SessionPlayer.PLAYER_STATE_PAUSED) {
- mExpectedPosition = FFWD_MS;
- player.seekTo(mExpectedPosition);
- }
- }
-
- @Override
- public void onSeekCompleted(@NonNull SessionPlayer player, long position) {
- // Ignore the initial seek. Internal MediaPlayer behavior can be changed.
- if (position == 0 && mExpectedPosition == FFWD_MS) {
- return;
- }
- assertTrue(equalsSeekPosition(mExpectedPosition, position, mDelta));
- if (mExpectedPosition == FFWD_MS) {
- mExpectedPosition = position - REW_MS;
- latchForFfwd.countDown();
- } else {
- latchForRew.countDown();
- }
- }
-
- private boolean equalsSeekPosition(long expected, long actual, long delta) {
- return (actual < expected + delta) && (actual > expected - delta);
- }
- });
- setAndPrepare(mFileSchemeMediaItem);
- assertTrue(latchForFfwd.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
- onView(allOf(withId(R.id.rew), isCompletelyDisplayed())).perform(click());
- assertTrue(latchForRew.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
- }
-
- @Test
- public void testSetMetadataForNonMusicFile() throws Throwable {
- final String title = "BigBuckBunny";
- final CountDownLatch latch = new CountDownLatch(1);
- final MediaMetadata metadata = new MediaMetadata.Builder()
- .putString(MediaMetadata.METADATA_KEY_TITLE, title).build();
- registerCallback(new SessionPlayer.PlayerCallback() {
- @Override
- public void onCurrentMediaItemChanged(@NonNull SessionPlayer player,
- @NonNull MediaItem item) {
- assertNotNull(item);
- assertNotNull(item.getMetadata());
- assertEquals(title, metadata.getString(MediaMetadata.METADATA_KEY_TITLE));
- latch.countDown();
- }
- });
- mFileSchemeMediaItem.setMetadata(metadata);
- mPlayer.setMediaItem(mFileSchemeMediaItem);
- assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
- onView(withId(R.id.title_text)).check(matches(withText(title)));
- }
-
- @Test
- public void testButtonVisibilityForMusicFile() throws Throwable {
- Uri uri = Uri.parse("android.resource://" + mContext.getPackageName() + "/"
- + R.raw.test_music);
- final MediaItem uriMediaItem = createTestMediaItem(uri);
-
- final CountDownLatch latch = new CountDownLatch(1);
- registerCallback(new SessionPlayer.PlayerCallback() {
- @Override
- public void onTrackInfoChanged(@NonNull SessionPlayer player,
- @NonNull List<TrackInfo> trackInfos) {
- latch.countDown();
- }
- });
- setAndPrepare(uriMediaItem);
- assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
- onView(withId(R.id.subtitle)).check(matches(not(isDisplayed())));
- }
-
- @Test
- public void testUpdateAndSelectSubtitleTrack() throws Throwable {
- Uri uri = Uri.parse("android.resource://" + mContext.getPackageName() + "/"
- + R.raw.testvideo_with_2_subtitle_tracks);
-
- final String subtitleTrackOffText = mContext.getResources().getString(
- R.string.MediaControlView_subtitle_off_text);
- final String subtitleTrack1Text = mContext.getResources().getString(
- R.string.MediaControlView_subtitle_track_number_text, 1);
-
- final MediaItem mediaItem = createTestMediaItem(uri);
-
- final CountDownLatch latchForTrackUpdate = new CountDownLatch(1);
- final CountDownLatch latchForSubtitleSelect = new CountDownLatch(1);
- final CountDownLatch latchForSubtitleDeselect = new CountDownLatch(1);
- registerCallback(new SessionPlayer.PlayerCallback() {
- private TrackInfo mFirstSubtitleTrack;
-
- @Override
- public void onTrackInfoChanged(@NonNull SessionPlayer player,
- @NonNull List<TrackInfo> trackInfos) {
- if (mFirstSubtitleTrack != null) {
- return;
- }
- assertNotNull(trackInfos);
- for (int i = 0; i < trackInfos.size(); i++) {
- TrackInfo trackInfo = trackInfos.get(i);
- if (trackInfo.getTrackType() == TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE) {
- mFirstSubtitleTrack = trackInfo;
- latchForTrackUpdate.countDown();
- break;
- }
- }
- }
-
- @Override
- public void onTrackSelected(@NonNull SessionPlayer player,
- @NonNull TrackInfo trackInfo) {
- assertEquals(mFirstSubtitleTrack, trackInfo);
- latchForSubtitleSelect.countDown();
- }
-
- @Override
- public void onTrackDeselected(@NonNull SessionPlayer player,
- @NonNull TrackInfo trackInfo) {
- assertEquals(mFirstSubtitleTrack, trackInfo);
- latchForSubtitleDeselect.countDown();
- }
- });
- // MediaPlayer needs a surface to be set in order to produce subtitle tracks
- mPlayer.setSurfaceInternal(mActivity.getSurfaceHolder().getSurface());
- setAndPrepare(mediaItem);
- assertTrue(latchForTrackUpdate.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
-
- onView(withId(R.id.subtitle)).check(matches(isClickable()));
- onView(withId(R.id.subtitle)).perform(click());
- onView(withText(subtitleTrack1Text)).inRoot(isPlatformPopup())
- .check(matches(isCompletelyDisplayed()));
- onView(withText(subtitleTrack1Text)).inRoot(isPlatformPopup()).perform(click());
- assertTrue(latchForSubtitleSelect.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
-
- onView(withId(R.id.subtitle)).check(matches(isClickable()));
- onView(withId(R.id.subtitle)).perform(click());
- onView(withText(subtitleTrackOffText)).inRoot(isPlatformPopup())
- .check(matches(isCompletelyDisplayed()));
- onView(withText(subtitleTrackOffText)).inRoot(isPlatformPopup()).perform(click());
- assertTrue(latchForSubtitleDeselect.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
- }
-
- @Test
- public void testCheckMediaItemIsFromHttp() throws Throwable {
- testCheckMediaItemIsFromNetwork(Uri.parse("http://localhost/dummy.mp4"), true);
- }
-
- @Test
- public void testCheckMediaItemIsFromHttps() throws Throwable {
- testCheckMediaItemIsFromNetwork(Uri.parse("https://localhost/dummy.mp4"), true);
- }
-
- @Test
- public void testCheckMediaItemIsFromRtsp() throws Throwable {
- testCheckMediaItemIsFromNetwork(Uri.parse("rtsp://localhost/dummy.mp4"), true);
- }
-
- @Test
- public void testCheckMediaItemIsFromFile() throws Throwable {
- testCheckMediaItemIsFromNetwork(Uri.parse("file:///dummy.mp4"), false);
- }
-
- private void testCheckMediaItemIsFromNetwork(Uri uri, boolean isNetwork) throws Throwable {
- final MediaItem mediaItem = createTestMediaItem(uri);
- final CountDownLatch latch = new CountDownLatch(1);
-
- registerCallback(new SessionPlayer.PlayerCallback() {
- @Override
- public void onCurrentMediaItemChanged(@NonNull SessionPlayer player,
- @NonNull MediaItem item) {
- assertSame(mediaItem, item);
- latch.countDown();
- }
- });
-
- mPlayer.setMediaItem(mediaItem);
- assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
- assertEquals(mMediaControlView.isCurrentMediaItemFromNetwork(), isNetwork);
- }
-
- private void registerCallback(SessionPlayer.PlayerCallback callback) {
- mPlayer.registerPlayerCallback(mMainHandlerExecutor, callback);
- }
-
- private void setAndPrepare(MediaItem item) {
- mPlayer.setMediaItem(item);
- mPlayer.prepare();
+public class MediaControlView_WithPlayerTest extends MediaControlView_WithSthTestBase {
+ @Override
+ PlayerWrapper createPlayerWrapper(@NonNull PlayerWrapper.PlayerCallback callback,
+ @Nullable MediaItem item) {
+ return createPlayerWrapperOfPlayer(callback, item);
}
}
diff --git a/media2/widget/src/androidTest/java/androidx/media2/widget/MediaControlView_WithSthTestBase.java b/media2/widget/src/androidTest/java/androidx/media2/widget/MediaControlView_WithSthTestBase.java
new file mode 100644
index 0000000..5ad0eb6a
--- /dev/null
+++ b/media2/widget/src/androidTest/java/androidx/media2/widget/MediaControlView_WithSthTestBase.java
@@ -0,0 +1,316 @@
+/*
+ * 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.media2.widget;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.RootMatchers.isPlatformPopup;
+import static androidx.test.espresso.matcher.ViewMatchers.isClickable;
+import static androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.net.Uri;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.media2.common.MediaItem;
+import androidx.media2.common.MediaMetadata;
+import androidx.media2.common.SessionPlayer;
+import androidx.media2.common.SessionPlayer.TrackInfo;
+import androidx.media2.session.MediaController;
+import androidx.media2.widget.test.R;
+import androidx.test.rule.ActivityTestRule;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Base class for testing {@link MediaControlView} with a {@link SessionPlayer} or
+ * {@link MediaController}.
+ */
+public abstract class MediaControlView_WithSthTestBase extends MediaWidgetTestBase {
+ private static final long FFWD_MS = 30000L;
+ private static final long REW_MS = 10000L;
+
+ private MediaControlViewTestActivity mActivity;
+ private MediaControlView mMediaControlView;
+ private MediaItem mFileSchemeMediaItem;
+
+ @Rule
+ public ActivityTestRule<MediaControlViewTestActivity> mActivityRule =
+ new ActivityTestRule<>(MediaControlViewTestActivity.class);
+
+ @Before
+ public void setup() throws Throwable {
+ mActivity = mActivityRule.getActivity();
+ mMediaControlView = mActivity.findViewById(R.id.mediacontrolview);
+
+ Uri fileSchemeUri = Uri.parse("android.resource://" + mContext.getPackageName() + "/"
+ + R.raw.test_file_scheme_video);
+ mFileSchemeMediaItem = createTestMediaItem(fileSchemeUri);
+
+ setKeepScreenOn(mActivityRule);
+ checkAttachedToWindow(mMediaControlView);
+ }
+
+ @After
+ public void tearDown() throws Throwable {
+ mActivityRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ closeAll();
+ }
+ });
+ }
+
+ @Test
+ public void testPlayPauseButtonClick() throws Throwable {
+ final CountDownLatch latchForPausedState = new CountDownLatch(1);
+ final CountDownLatch latchForPlayingState = new CountDownLatch(1);
+ final PlayerWrapper playerWrapper = createPlayerWrapper(new PlayerWrapper.PlayerCallback() {
+ @Override
+ public void onPlayerStateChanged(@NonNull PlayerWrapper player, int state) {
+ if (state == SessionPlayer.PLAYER_STATE_PAUSED) {
+ latchForPausedState.countDown();
+ } else if (state == SessionPlayer.PLAYER_STATE_PLAYING) {
+ latchForPlayingState.countDown();
+ }
+ }
+ }, mFileSchemeMediaItem);
+ setPlayerWrapper(playerWrapper);
+ assertTrue(latchForPausedState.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ onView(allOf(withId(R.id.pause), isCompletelyDisplayed())).perform(click());
+ assertTrue(latchForPlayingState.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testFfwdButtonClick() throws Throwable {
+ final CountDownLatch latchForPausedState = new CountDownLatch(1);
+ final CountDownLatch latchForFfwd = new CountDownLatch(1);
+ final PlayerWrapper playerWrapper = createPlayerWrapper(new PlayerWrapper.PlayerCallback() {
+ @Override
+ public void onSeekCompleted(@NonNull PlayerWrapper player, long position) {
+ if (position >= FFWD_MS) {
+ latchForFfwd.countDown();
+ }
+ }
+
+ @Override
+ public void onPlayerStateChanged(@NonNull PlayerWrapper player, int state) {
+ if (state == SessionPlayer.PLAYER_STATE_PAUSED) {
+ latchForPausedState.countDown();
+ }
+ }
+ }, mFileSchemeMediaItem);
+ setPlayerWrapper(playerWrapper);
+ assertTrue(latchForPausedState.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ onView(allOf(withId(R.id.ffwd), isCompletelyDisplayed())).perform(click());
+ assertTrue(latchForFfwd.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testRewButtonClick() throws Throwable {
+ final CountDownLatch latchForFfwd = new CountDownLatch(1);
+ final CountDownLatch latchForRew = new CountDownLatch(1);
+ final PlayerWrapper playerWrapper = createPlayerWrapper(new PlayerWrapper.PlayerCallback() {
+ long mExpectedPosition = FFWD_MS;
+ final long mDelta = 1000L;
+
+ @Override
+ public void onPlayerStateChanged(@NonNull PlayerWrapper player, int state) {
+ if (state == SessionPlayer.PLAYER_STATE_PAUSED) {
+ mExpectedPosition = FFWD_MS;
+ player.seekTo(mExpectedPosition);
+ }
+ }
+
+ @Override
+ public void onSeekCompleted(@NonNull PlayerWrapper player, long position) {
+ // Ignore the initial seek. Internal MediaPlayer behavior can be changed.
+ if (position == 0 && mExpectedPosition == FFWD_MS) {
+ return;
+ }
+ assertTrue(equalsSeekPosition(mExpectedPosition, position, mDelta));
+ if (mExpectedPosition == FFWD_MS) {
+ mExpectedPosition = position - REW_MS;
+ latchForFfwd.countDown();
+ } else {
+ latchForRew.countDown();
+ }
+ }
+
+ private boolean equalsSeekPosition(long expected, long actual, long delta) {
+ return (actual < expected + delta) && (actual > expected - delta);
+ }
+ }, mFileSchemeMediaItem);
+ setPlayerWrapper(playerWrapper);
+ assertTrue(latchForFfwd.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ onView(allOf(withId(R.id.rew), isCompletelyDisplayed())).perform(click());
+ assertTrue(latchForRew.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testSetMetadataForNonMusicFile() throws Throwable {
+ final String title = "BigBuckBunny";
+ final CountDownLatch latch = new CountDownLatch(1);
+ final MediaMetadata metadata = new MediaMetadata.Builder()
+ .putString(MediaMetadata.METADATA_KEY_TITLE, title).build();
+ mFileSchemeMediaItem.setMetadata(metadata);
+ final PlayerWrapper playerWrapper = createPlayerWrapper(new PlayerWrapper.PlayerCallback() {
+ @Override
+ public void onCurrentMediaItemChanged(@NonNull PlayerWrapper player,
+ @Nullable MediaItem item) {
+ if (item != null) {
+ assertNotNull(item.getMetadata());
+ assertEquals(title, metadata.getString(MediaMetadata.METADATA_KEY_TITLE));
+ latch.countDown();
+ }
+ }
+ }, mFileSchemeMediaItem);
+ setPlayerWrapper(playerWrapper);
+ assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ onView(withId(R.id.title_text)).check(matches(withText(title)));
+ }
+
+ @Test
+ public void testButtonVisibilityForMusicFile() throws Throwable {
+ Uri uri = Uri.parse("android.resource://" + mContext.getPackageName() + "/"
+ + R.raw.test_music);
+ final MediaItem uriMediaItem = createTestMediaItem(uri);
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ final PlayerWrapper playerWrapper = createPlayerWrapper(new PlayerWrapper.PlayerCallback() {
+ @Override
+ public void onTrackInfoChanged(@NonNull PlayerWrapper player,
+ @NonNull List<TrackInfo> trackInfos) {
+ latch.countDown();
+ }
+ }, uriMediaItem);
+ setPlayerWrapper(playerWrapper);
+ assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ onView(withId(R.id.subtitle)).check(matches(not(isDisplayed())));
+ }
+
+ @Test
+ public void testUpdateAndSelectSubtitleTrack() throws Throwable {
+ Uri uri = Uri.parse("android.resource://" + mContext.getPackageName() + "/"
+ + R.raw.testvideo_with_2_subtitle_tracks);
+
+ final String subtitleTrackOffText = mContext.getResources().getString(
+ R.string.MediaControlView_subtitle_off_text);
+ final String subtitleTrack1Text = mContext.getResources().getString(
+ R.string.MediaControlView_subtitle_track_number_text, 1);
+
+ final MediaItem mediaItem = createTestMediaItem(uri);
+
+ final CountDownLatch latchForReady = new CountDownLatch(1);
+ final CountDownLatch latchForTrackUpdate = new CountDownLatch(1);
+ final CountDownLatch latchForSubtitleSelect = new CountDownLatch(1);
+ final CountDownLatch latchForSubtitleDeselect = new CountDownLatch(1);
+ final PlayerWrapper playerWrapper = createPlayerWrapper(new PlayerWrapper.PlayerCallback() {
+ private TrackInfo mFirstSubtitleTrack;
+
+ @Override
+ public void onPlayerStateChanged(@NonNull PlayerWrapper player, int state) {
+ if (state == SessionPlayer.PLAYER_STATE_PAUSED) {
+ latchForReady.countDown();
+ }
+ }
+
+ @Override
+ public void onTrackInfoChanged(@NonNull PlayerWrapper player,
+ @NonNull List<TrackInfo> trackInfos) {
+ if (mFirstSubtitleTrack != null) {
+ return;
+ }
+ assertNotNull(trackInfos);
+ for (int i = 0; i < trackInfos.size(); i++) {
+ TrackInfo trackInfo = trackInfos.get(i);
+ if (trackInfo.getTrackType() == TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE) {
+ mFirstSubtitleTrack = trackInfo;
+ latchForTrackUpdate.countDown();
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void onTrackSelected(@NonNull PlayerWrapper player,
+ @NonNull TrackInfo trackInfo) {
+ assertEquals(mFirstSubtitleTrack, trackInfo);
+ latchForSubtitleSelect.countDown();
+ }
+
+ @Override
+ public void onTrackDeselected(@NonNull PlayerWrapper player,
+ @NonNull TrackInfo trackInfo) {
+ assertEquals(mFirstSubtitleTrack, trackInfo);
+ latchForSubtitleDeselect.countDown();
+ }
+ }, mediaItem);
+ setPlayerWrapper(playerWrapper);
+ assertTrue(latchForReady.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ // MediaPlayer needs a surface to be set in order to produce subtitle tracks
+ playerWrapper.setSurface(mActivity.getSurfaceHolder().getSurface());
+ assertTrue(latchForTrackUpdate.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+
+ onView(withId(R.id.subtitle)).check(matches(isClickable()));
+ onView(withId(R.id.subtitle)).perform(click());
+ onView(withText(subtitleTrack1Text)).inRoot(isPlatformPopup())
+ .check(matches(isCompletelyDisplayed()));
+ onView(withText(subtitleTrack1Text)).inRoot(isPlatformPopup()).perform(click());
+ assertTrue(latchForSubtitleSelect.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+
+ onView(withId(R.id.subtitle)).check(matches(isClickable()));
+ onView(withId(R.id.subtitle)).perform(click());
+ onView(withText(subtitleTrackOffText)).inRoot(isPlatformPopup())
+ .check(matches(isCompletelyDisplayed()));
+ onView(withText(subtitleTrackOffText)).inRoot(isPlatformPopup()).perform(click());
+ assertTrue(latchForSubtitleDeselect.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ private void setPlayerWrapper(final PlayerWrapper playerWrapper) throws Throwable {
+ mActivityRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ if (playerWrapper.mPlayer != null) {
+ mMediaControlView.setPlayer(playerWrapper.mPlayer);
+ } else if (playerWrapper.mController != null) {
+ mMediaControlView.setMediaController(playerWrapper.mController);
+ }
+ }
+ });
+ }
+
+ abstract PlayerWrapper createPlayerWrapper(@NonNull PlayerWrapper.PlayerCallback callback,
+ @Nullable MediaItem item);
+}
diff --git a/media2/widget/src/androidTest/java/androidx/media2/widget/MediaTestBase.java b/media2/widget/src/androidTest/java/androidx/media2/widget/MediaTestBase.java
new file mode 100644
index 0000000..e6d429f
--- /dev/null
+++ b/media2/widget/src/androidTest/java/androidx/media2/widget/MediaTestBase.java
@@ -0,0 +1,70 @@
+/*
+ * 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.media2.widget;
+
+import android.content.Context;
+import android.media.AudioManager;
+import android.os.Looper;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.BeforeClass;
+
+/**
+ * Base class for all media tests.
+ */
+abstract class MediaTestBase {
+ /**
+ * All tests methods should start with this.
+ * <p>
+ * MediaControllerCompat, which is wrapped by the MediaSession, can be only created by the
+ * thread whose Looper is prepared. However, when the presubmit test runs on the server,
+ * test runs with the {@link org.junit.internal.runners.statements.FailOnTimeout} which creates
+ * dedicated thread for running test methods while methods annotated with @After or @Before
+ * runs on the normal test different thread. This ensures that the current Looper is prepared.
+ */
+ public static void prepareLooper() {
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+ }
+
+ @BeforeClass
+ public static void setupMainLooper() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ // Prepare the main looper if it hasn't.
+ // Some framework APIs always run on the main looper.
+ if (Looper.getMainLooper() == null) {
+ Looper.prepareMainLooper();
+ }
+
+ // Initialize AudioManager on the main thread to workaround b/78617702 that
+ // audio focus listener is called on the thread where the AudioManager was
+ // originally initialized.
+ // Without posting this, audio focus listeners wouldn't be called because the
+ // listeners would be posted to the test thread (here) where it waits until the
+ // tests are finished.
+ Context context = ApplicationProvider.getApplicationContext();
+ AudioManager manager =
+ (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+ }
+ });
+ }
+}
diff --git a/media2/widget/src/androidTest/java/androidx/media2/widget/MediaWidgetTestBase.java b/media2/widget/src/androidTest/java/androidx/media2/widget/MediaWidgetTestBase.java
index e93dced..c6960d3 100644
--- a/media2/widget/src/androidTest/java/androidx/media2/widget/MediaWidgetTestBase.java
+++ b/media2/widget/src/androidTest/java/androidx/media2/widget/MediaWidgetTestBase.java
@@ -29,9 +29,15 @@
import android.view.View;
import android.view.WindowManager;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.media2.common.MediaItem;
+import androidx.media2.common.SessionPlayer;
import androidx.media2.common.UriMediaItem;
+import androidx.media2.player.MediaPlayer;
+import androidx.media2.session.MediaController;
+import androidx.media2.session.MediaSession;
import androidx.media2.widget.test.R;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -39,14 +45,20 @@
import org.junit.Before;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
-public class MediaWidgetTestBase {
+public class MediaWidgetTestBase extends MediaTestBase {
// Expected success time
static final int WAIT_TIME_MS = 1000;
+ private List<SessionPlayer> mPlayers = new ArrayList<>();
+ private List<MediaSession> mSessions = new ArrayList<>();
+ private List<MediaController> mControllers = new ArrayList<>();
+
Context mContext;
Executor mMainHandlerExecutor;
@@ -108,4 +120,57 @@
MediaItem createTestMediaItem(Uri uri) {
return new UriMediaItem.Builder(uri).build();
}
+
+ PlayerWrapper createPlayerWrapperOfController(@NonNull PlayerWrapper.PlayerCallback callback,
+ @Nullable MediaItem item) {
+ prepareLooper();
+
+ SessionPlayer player = new MediaPlayer(mContext);
+ MediaSession session = new MediaSession.Builder(mContext, player).build();
+ MediaController controller = new MediaController.Builder(mContext)
+ .setSessionToken(session.getToken())
+ .build();
+ mPlayers.add(player);
+ mSessions.add(session);
+ mControllers.add(controller);
+ PlayerWrapper wrapper = new PlayerWrapper(controller, mMainHandlerExecutor, callback);
+ wrapper.attachCallback();
+ if (item != null) {
+ player.setMediaItem(item);
+ player.prepare();
+ }
+ return wrapper;
+ }
+
+ PlayerWrapper createPlayerWrapperOfPlayer(@NonNull PlayerWrapper.PlayerCallback callback,
+ @Nullable MediaItem item) {
+ SessionPlayer player = new MediaPlayer(mContext);
+ mPlayers.add(player);
+ PlayerWrapper wrapper = new PlayerWrapper(player, mMainHandlerExecutor, callback);
+ wrapper.attachCallback();
+ if (item != null) {
+ player.setMediaItem(item);
+ player.prepare();
+ }
+ return wrapper;
+ }
+
+ void closeAll() {
+ for (MediaController controller : mControllers) {
+ controller.close();
+ }
+ for (MediaSession session : mSessions) {
+ session.close();
+ }
+ for (SessionPlayer player : mPlayers) {
+ try {
+ player.close();
+ } catch (Exception ex) {
+ // ignore
+ }
+ }
+ mControllers.clear();
+ mSessions.clear();
+ mPlayers.clear();
+ }
}
diff --git a/media2/widget/src/androidTest/java/androidx/media2/widget/UriUtilTest.java b/media2/widget/src/androidTest/java/androidx/media2/widget/UriUtilTest.java
new file mode 100644
index 0000000..eee41e4
--- /dev/null
+++ b/media2/widget/src/androidTest/java/androidx/media2/widget/UriUtilTest.java
@@ -0,0 +1,43 @@
+/*
+ * 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.media2.widget;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.net.Uri;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test {@link UriUtil}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class UriUtilTest {
+ @Test
+ @SmallTest
+ public void testIsFromNetwork() {
+ assertTrue(UriUtil.isFromNetwork(Uri.parse("http://localhost/dummy.mp4")));
+ assertTrue(UriUtil.isFromNetwork(Uri.parse("https://localhost/dummy.mp4")));
+ assertTrue(UriUtil.isFromNetwork(Uri.parse("rtsp://localhost/dummy.mp4")));
+ assertFalse(UriUtil.isFromNetwork(Uri.parse("file:///dummy.mp4")));
+ }
+}
diff --git a/media2/widget/src/androidTest/java/androidx/media2/widget/VideoView_WithControllerTest.java b/media2/widget/src/androidTest/java/androidx/media2/widget/VideoView_WithControllerTest.java
new file mode 100644
index 0000000..8fd72f5
--- /dev/null
+++ b/media2/widget/src/androidTest/java/androidx/media2/widget/VideoView_WithControllerTest.java
@@ -0,0 +1,39 @@
+/*
+ * 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.media2.widget;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.media2.common.MediaItem;
+import androidx.media2.session.MediaController;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.LargeTest;
+
+import org.junit.runner.RunWith;
+
+/**
+ * Test {@link VideoView} with a {@link MediaController}.
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class VideoView_WithControllerTest extends VideoView_WithSthTestBase {
+ @Override
+ PlayerWrapper createPlayerWrapper(@NonNull PlayerWrapper.PlayerCallback callback,
+ @Nullable MediaItem item) {
+ return createPlayerWrapperOfController(callback, item);
+ }
+}
diff --git a/media2/widget/src/androidTest/java/androidx/media2/widget/VideoView_WithPlayerTest.java b/media2/widget/src/androidTest/java/androidx/media2/widget/VideoView_WithPlayerTest.java
index 1fa4b5c..9387a41 100644
--- a/media2/widget/src/androidTest/java/androidx/media2/widget/VideoView_WithPlayerTest.java
+++ b/media2/widget/src/androidTest/java/androidx/media2/widget/VideoView_WithPlayerTest.java
@@ -16,31 +16,13 @@
package androidx.media2.widget;
-import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.after;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.verify;
-
-import android.app.Activity;
-import android.content.res.AssetFileDescriptor;
-import android.os.ParcelFileDescriptor;
-
-import androidx.media2.common.FileMediaItem;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.media2.common.MediaItem;
import androidx.media2.common.SessionPlayer;
-import androidx.media2.player.MediaPlayer;
-import androidx.media2.widget.test.R;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
-import androidx.test.rule.ActivityTestRule;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
import org.junit.runner.RunWith;
/**
@@ -48,160 +30,10 @@
*/
@RunWith(AndroidJUnit4.class)
@LargeTest
-public class VideoView_WithPlayerTest extends MediaWidgetTestBase {
- private Activity mActivity;
- private VideoView mVideoView;
- private MediaItem mMediaItem;
- private SessionPlayer.PlayerCallback mPlayerCallback;
- private SessionPlayer mPlayer;
-
- @Rule
- public ActivityTestRule<VideoViewTestActivity> mActivityRule =
- new ActivityTestRule<>(VideoViewTestActivity.class);
-
- @Before
- public void setup() throws Throwable {
- mActivity = mActivityRule.getActivity();
- mVideoView = mActivity.findViewById(R.id.videoview);
- mMediaItem = createTestMediaItem();
-
- setKeepScreenOn(mActivityRule);
- checkAttachedToWindow(mVideoView);
-
- mPlayerCallback = mock(SessionPlayer.PlayerCallback.class);
- mPlayer = new MediaPlayer(mContext);
- mPlayer.registerPlayerCallback(mMainHandlerExecutor, mPlayerCallback);
- mActivityRule.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mVideoView.setPlayer(mPlayer);
- }
- });
- }
-
- @After
- public void tearDown() throws Throwable {
- mActivityRule.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- try {
- mPlayer.close();
- } catch (Exception ex) {
- // ignore
- }
- }
- });
- }
-
- @Test
- public void testPlayVideo() throws Throwable {
- waitToPrepare(mMediaItem);
- verify(mPlayerCallback, timeout(WAIT_TIME_MS).atLeastOnce()).onCurrentMediaItemChanged(
- any(SessionPlayer.class), any(MediaItem.class));
- verify(mPlayerCallback, timeout(WAIT_TIME_MS).atLeastOnce()).onPlayerStateChanged(
- any(SessionPlayer.class), eq(SessionPlayer.PLAYER_STATE_PAUSED));
- verify(mPlayerCallback, after(WAIT_TIME_MS).never()).onPlayerStateChanged(
- any(SessionPlayer.class), eq(SessionPlayer.PLAYER_STATE_PLAYING));
- assertEquals(SessionPlayer.PLAYER_STATE_PAUSED, mPlayer.getPlayerState());
-
- mPlayer.play();
- verify(mPlayerCallback, timeout(WAIT_TIME_MS).atLeastOnce()).onPlayerStateChanged(
- any(SessionPlayer.class), eq(SessionPlayer.PLAYER_STATE_PLAYING));
- }
-
- @Test
- public void testPlayVideoWithMediaItemFromFileDescriptor() throws Throwable {
- AssetFileDescriptor afd = mContext.getResources()
- .openRawResourceFd(R.raw.testvideo_with_2_subtitle_tracks);
- final MediaItem item = new FileMediaItem.Builder(
- ParcelFileDescriptor.dup(afd.getFileDescriptor()))
- .setFileDescriptorOffset(afd.getStartOffset())
- .setFileDescriptorLength(afd.getLength())
- .build();
- afd.close();
-
- waitToPrepare(item);
- verify(mPlayerCallback, timeout(WAIT_TIME_MS).atLeastOnce()).onCurrentMediaItemChanged(
- any(SessionPlayer.class), eq(item));
- verify(mPlayerCallback, timeout(WAIT_TIME_MS).atLeastOnce()).onPlayerStateChanged(
- any(SessionPlayer.class), eq(SessionPlayer.PLAYER_STATE_PAUSED));
-
- mPlayer.play();
- verify(mPlayerCallback, timeout(WAIT_TIME_MS).atLeastOnce()).onPlayerStateChanged(
- any(SessionPlayer.class), eq(SessionPlayer.PLAYER_STATE_PLAYING));
- }
-
- @Test
- public void testPlayVideoOnTextureView() throws Throwable {
- final VideoView.OnViewTypeChangedListener mockViewTypeListener =
- mock(VideoView.OnViewTypeChangedListener.class);
-
- // The default view type is surface view.
- assertEquals(mVideoView.getViewType(), VideoView.VIEW_TYPE_SURFACEVIEW);
-
- mActivityRule.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mVideoView.setOnViewTypeChangedListener(mockViewTypeListener);
- mVideoView.setViewType(VideoView.VIEW_TYPE_TEXTUREVIEW);
- }
- });
- waitToPrepare(mMediaItem);
- verify(mockViewTypeListener, timeout(WAIT_TIME_MS))
- .onViewTypeChanged(mVideoView, VideoView.VIEW_TYPE_TEXTUREVIEW);
- verify(mPlayerCallback, timeout(WAIT_TIME_MS).atLeastOnce()).onCurrentMediaItemChanged(
- any(SessionPlayer.class), any(MediaItem.class));
- verify(mPlayerCallback, timeout(WAIT_TIME_MS).atLeast(1)).onPlayerStateChanged(
- any(SessionPlayer.class), eq(SessionPlayer.PLAYER_STATE_PAUSED));
-
- mPlayer.play();
- verify(mPlayerCallback, timeout(WAIT_TIME_MS).atLeast(1)).onPlayerStateChanged(
- any(SessionPlayer.class), eq(SessionPlayer.PLAYER_STATE_PLAYING));
- }
-
- @Test
- public void testSetViewType() throws Throwable {
- final VideoView.OnViewTypeChangedListener mockViewTypeListener =
- mock(VideoView.OnViewTypeChangedListener.class);
-
- // The default view type is surface view.
- assertEquals(mVideoView.getViewType(), VideoView.VIEW_TYPE_SURFACEVIEW);
-
- mActivityRule.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mVideoView.setOnViewTypeChangedListener(mockViewTypeListener);
- mVideoView.setViewType(VideoView.VIEW_TYPE_TEXTUREVIEW);
- mVideoView.setViewType(VideoView.VIEW_TYPE_SURFACEVIEW);
- mVideoView.setViewType(VideoView.VIEW_TYPE_TEXTUREVIEW);
- mVideoView.setViewType(VideoView.VIEW_TYPE_SURFACEVIEW);
- }
- });
-
- waitToPrepare(mMediaItem);
- verify(mPlayerCallback, timeout(WAIT_TIME_MS).atLeastOnce()).onCurrentMediaItemChanged(
- any(SessionPlayer.class), any(MediaItem.class));
- // WAIT_TIME_MS multiplied by the number of operations.
- verify(mPlayerCallback, timeout(WAIT_TIME_MS * 5).atLeast(1)).onPlayerStateChanged(
- any(SessionPlayer.class), eq(SessionPlayer.PLAYER_STATE_PAUSED));
- assertEquals(mVideoView.getViewType(), VideoView.VIEW_TYPE_SURFACEVIEW);
-
- mPlayer.play();
- verify(mPlayerCallback, timeout(WAIT_TIME_MS).atLeastOnce()).onPlayerStateChanged(
- any(SessionPlayer.class), eq(SessionPlayer.PLAYER_STATE_PLAYING));
-
- mActivityRule.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mVideoView.setViewType(VideoView.VIEW_TYPE_TEXTUREVIEW);
- }
- });
- verify(mockViewTypeListener, timeout(WAIT_TIME_MS))
- .onViewTypeChanged(mVideoView, VideoView.VIEW_TYPE_TEXTUREVIEW);
- }
-
- private void waitToPrepare(MediaItem item) throws Exception {
- mPlayer.setMediaItem(item);
- mPlayer.prepare().get();
+public class VideoView_WithPlayerTest extends VideoView_WithSthTestBase {
+ @Override
+ PlayerWrapper createPlayerWrapper(@NonNull PlayerWrapper.PlayerCallback callback,
+ @Nullable MediaItem item) {
+ return createPlayerWrapperOfPlayer(callback, item);
}
}
diff --git a/media2/widget/src/androidTest/java/androidx/media2/widget/VideoView_WithSthTestBase.java b/media2/widget/src/androidTest/java/androidx/media2/widget/VideoView_WithSthTestBase.java
new file mode 100644
index 0000000..1996d0d
--- /dev/null
+++ b/media2/widget/src/androidTest/java/androidx/media2/widget/VideoView_WithSthTestBase.java
@@ -0,0 +1,221 @@
+/*
+ * 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.media2.widget;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+
+import android.app.Activity;
+import android.content.res.AssetFileDescriptor;
+import android.os.ParcelFileDescriptor;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.media2.common.FileMediaItem;
+import androidx.media2.common.MediaItem;
+import androidx.media2.common.SessionPlayer;
+import androidx.media2.session.MediaController;
+import androidx.media2.widget.test.R;
+import androidx.test.rule.ActivityTestRule;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Base class for testing {@link VideoView} with a {@link SessionPlayer} or
+ * {@link MediaController}.
+ */
+public abstract class VideoView_WithSthTestBase extends MediaWidgetTestBase {
+ private Activity mActivity;
+ private VideoView mVideoView;
+ private MediaItem mMediaItem;
+
+ @Rule
+ public ActivityTestRule<VideoViewTestActivity> mActivityRule =
+ new ActivityTestRule<>(VideoViewTestActivity.class);
+
+ @Before
+ public void setup() throws Throwable {
+ mActivity = mActivityRule.getActivity();
+ mVideoView = mActivity.findViewById(R.id.videoview);
+ mMediaItem = createTestMediaItem();
+
+ setKeepScreenOn(mActivityRule);
+ checkAttachedToWindow(mVideoView);
+ }
+
+ @After
+ public void tearDown() throws Throwable {
+ mActivityRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ closeAll();
+ }
+ });
+ }
+
+ @Test
+ public void testPlayVideo() throws Throwable {
+ PlayerCallback callback = new PlayerCallback();
+ PlayerWrapper playerWrapper = createPlayerWrapper(callback, mMediaItem);
+ setPlayerWrapper(playerWrapper);
+ assertTrue(callback.mItemLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ assertTrue(callback.mPausedLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ assertEquals(1, callback.mPlayingLatch.getCount());
+ assertEquals(SessionPlayer.PLAYER_STATE_PAUSED, playerWrapper.getPlayerState());
+
+ playerWrapper.play();
+ assertTrue(callback.mPlayingLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testPlayVideoWithMediaItemFromFileDescriptor() throws Throwable {
+ AssetFileDescriptor afd = mContext.getResources()
+ .openRawResourceFd(R.raw.testvideo_with_2_subtitle_tracks);
+ final MediaItem item = new FileMediaItem.Builder(
+ ParcelFileDescriptor.dup(afd.getFileDescriptor()))
+ .setFileDescriptorOffset(afd.getStartOffset())
+ .setFileDescriptorLength(afd.getLength())
+ .build();
+ afd.close();
+
+ PlayerCallback callback = new PlayerCallback();
+ PlayerWrapper playerWrapper = createPlayerWrapper(callback, item);
+ setPlayerWrapper(playerWrapper);
+ assertTrue(callback.mItemLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ assertTrue(callback.mPausedLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+
+ playerWrapper.play();
+ assertTrue(callback.mPlayingLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testPlayVideoOnTextureView() throws Throwable {
+ final VideoView.OnViewTypeChangedListener mockViewTypeListener =
+ mock(VideoView.OnViewTypeChangedListener.class);
+
+ PlayerCallback callback = new PlayerCallback();
+ PlayerWrapper playerWrapper = createPlayerWrapper(callback, mMediaItem);
+ setPlayerWrapper(playerWrapper);
+
+ // The default view type is surface view.
+ assertEquals(mVideoView.getViewType(), VideoView.VIEW_TYPE_SURFACEVIEW);
+
+ mActivityRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mVideoView.setOnViewTypeChangedListener(mockViewTypeListener);
+ mVideoView.setViewType(VideoView.VIEW_TYPE_TEXTUREVIEW);
+ }
+ });
+ verify(mockViewTypeListener, timeout(WAIT_TIME_MS))
+ .onViewTypeChanged(mVideoView, VideoView.VIEW_TYPE_TEXTUREVIEW);
+ assertTrue(callback.mItemLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ assertTrue(callback.mPausedLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+
+ playerWrapper.play();
+ assertTrue(callback.mPlayingLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
+ @Test
+ public void testSetViewType() throws Throwable {
+ final VideoView.OnViewTypeChangedListener mockViewTypeListener =
+ mock(VideoView.OnViewTypeChangedListener.class);
+
+ PlayerCallback callback = new PlayerCallback();
+ PlayerWrapper playerWrapper = createPlayerWrapper(callback, mMediaItem);
+ setPlayerWrapper(playerWrapper);
+
+ // The default view type is surface view.
+ assertEquals(mVideoView.getViewType(), VideoView.VIEW_TYPE_SURFACEVIEW);
+
+ mActivityRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mVideoView.setOnViewTypeChangedListener(mockViewTypeListener);
+ mVideoView.setViewType(VideoView.VIEW_TYPE_TEXTUREVIEW);
+ mVideoView.setViewType(VideoView.VIEW_TYPE_SURFACEVIEW);
+ mVideoView.setViewType(VideoView.VIEW_TYPE_TEXTUREVIEW);
+ mVideoView.setViewType(VideoView.VIEW_TYPE_SURFACEVIEW);
+ }
+ });
+
+ assertTrue(callback.mItemLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ // WAIT_TIME_MS multiplied by the number of operations.
+ assertTrue(callback.mPausedLatch.await(WAIT_TIME_MS * 5, TimeUnit.MILLISECONDS));
+ assertEquals(mVideoView.getViewType(), VideoView.VIEW_TYPE_SURFACEVIEW);
+
+ playerWrapper.play();
+ assertTrue(callback.mPlayingLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+
+ mActivityRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mVideoView.setViewType(VideoView.VIEW_TYPE_TEXTUREVIEW);
+ }
+ });
+ verify(mockViewTypeListener, timeout(WAIT_TIME_MS))
+ .onViewTypeChanged(mVideoView, VideoView.VIEW_TYPE_TEXTUREVIEW);
+ }
+
+ private void setPlayerWrapper(final PlayerWrapper playerWrapper) throws Throwable {
+ mActivityRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ if (playerWrapper.mPlayer != null) {
+ mVideoView.setPlayer(playerWrapper.mPlayer);
+ } else if (playerWrapper.mController != null) {
+ mVideoView.setMediaController(playerWrapper.mController);
+ }
+ }
+ });
+ }
+
+ private class PlayerCallback extends PlayerWrapper.PlayerCallback {
+ private CountDownLatch mItemLatch = new CountDownLatch(1);
+ private CountDownLatch mPausedLatch = new CountDownLatch(1);
+ private CountDownLatch mPlayingLatch = new CountDownLatch(1);
+
+ @Override
+ void onCurrentMediaItemChanged(@NonNull PlayerWrapper player,
+ @Nullable MediaItem item) {
+ if (item != null) {
+ mItemLatch.countDown();
+ }
+ }
+
+ @Override
+ void onPlayerStateChanged(@NonNull PlayerWrapper player, int state) {
+ if (state == SessionPlayer.PLAYER_STATE_PAUSED) {
+ mPausedLatch.countDown();
+ } else if (state == SessionPlayer.PLAYER_STATE_PLAYING) {
+ mPlayingLatch.countDown();
+ }
+ }
+ }
+
+ abstract PlayerWrapper createPlayerWrapper(@NonNull PlayerWrapper.PlayerCallback callback,
+ @Nullable MediaItem item);
+}
diff --git a/media2/widget/src/main/java/androidx/media2/widget/MediaControlView.java b/media2/widget/src/main/java/androidx/media2/widget/MediaControlView.java
index 9a377c5..a411e24 100644
--- a/media2/widget/src/main/java/androidx/media2/widget/MediaControlView.java
+++ b/media2/widget/src/main/java/androidx/media2/widget/MediaControlView.java
@@ -49,7 +49,6 @@
import androidx.annotation.IdRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;
import androidx.media2.common.MediaItem;
@@ -1496,7 +1495,6 @@
/**
* @return true iff the current media item is from network.
*/
- @VisibleForTesting
boolean isCurrentMediaItemFromNetwork() {
ensurePlayerIsNotNull();
diff --git a/mediarouter/api/api_lint.ignore b/mediarouter/api/api_lint.ignore
new file mode 100644
index 0000000..1090c0a
--- /dev/null
+++ b/mediarouter/api/api_lint.ignore
@@ -0,0 +1,69 @@
+// Baseline format: 1.0
+ActionValue: androidx.mediarouter.media.MediaControlIntent#ACTION_END_SESSION:
+ Inconsistent action value; expected `androidx.mediarouter.media.action.END_SESSION`, was `android.media.intent.action.END_SESSION`
+ActionValue: androidx.mediarouter.media.MediaControlIntent#ACTION_ENQUEUE:
+ Inconsistent action value; expected `androidx.mediarouter.media.action.ENQUEUE`, was `android.media.intent.action.ENQUEUE`
+ActionValue: androidx.mediarouter.media.MediaControlIntent#ACTION_GET_SESSION_STATUS:
+ Inconsistent action value; expected `androidx.mediarouter.media.action.GET_SESSION_STATUS`, was `android.media.intent.action.GET_SESSION_STATUS`
+ActionValue: androidx.mediarouter.media.MediaControlIntent#ACTION_GET_STATUS:
+ Inconsistent action value; expected `androidx.mediarouter.media.action.GET_STATUS`, was `android.media.intent.action.GET_STATUS`
+ActionValue: androidx.mediarouter.media.MediaControlIntent#ACTION_PAUSE:
+ Inconsistent action value; expected `androidx.mediarouter.media.action.PAUSE`, was `android.media.intent.action.PAUSE`
+ActionValue: androidx.mediarouter.media.MediaControlIntent#ACTION_PLAY:
+ Inconsistent action value; expected `androidx.mediarouter.media.action.PLAY`, was `android.media.intent.action.PLAY`
+ActionValue: androidx.mediarouter.media.MediaControlIntent#ACTION_REMOVE:
+ Inconsistent action value; expected `androidx.mediarouter.media.action.REMOVE`, was `android.media.intent.action.REMOVE`
+ActionValue: androidx.mediarouter.media.MediaControlIntent#ACTION_RESUME:
+ Inconsistent action value; expected `androidx.mediarouter.media.action.RESUME`, was `android.media.intent.action.RESUME`
+ActionValue: androidx.mediarouter.media.MediaControlIntent#ACTION_SEEK:
+ Inconsistent action value; expected `androidx.mediarouter.media.action.SEEK`, was `android.media.intent.action.SEEK`
+ActionValue: androidx.mediarouter.media.MediaControlIntent#ACTION_SEND_MESSAGE:
+ Inconsistent action value; expected `androidx.mediarouter.media.action.SEND_MESSAGE`, was `android.media.intent.action.SEND_MESSAGE`
+ActionValue: androidx.mediarouter.media.MediaControlIntent#ACTION_START_SESSION:
+ Inconsistent action value; expected `androidx.mediarouter.media.action.START_SESSION`, was `android.media.intent.action.START_SESSION`
+ActionValue: androidx.mediarouter.media.MediaControlIntent#ACTION_STOP:
+ Inconsistent action value; expected `androidx.mediarouter.media.action.STOP`, was `android.media.intent.action.STOP`
+ActionValue: androidx.mediarouter.media.MediaControlIntent#EXTRA_ERROR_CODE:
+ Inconsistent extra value; expected `androidx.mediarouter.media.extra.ERROR_CODE`, was `android.media.intent.extra.ERROR_CODE`
+ActionValue: androidx.mediarouter.media.MediaControlIntent#EXTRA_ITEM_CONTENT_POSITION:
+ Inconsistent extra value; expected `androidx.mediarouter.media.extra.ITEM_CONTENT_POSITION`, was `android.media.intent.extra.ITEM_POSITION`
+ActionValue: androidx.mediarouter.media.MediaControlIntent#EXTRA_ITEM_HTTP_HEADERS:
+ Inconsistent extra value; expected `androidx.mediarouter.media.extra.ITEM_HTTP_HEADERS`, was `android.media.intent.extra.HTTP_HEADERS`
+ActionValue: androidx.mediarouter.media.MediaControlIntent#EXTRA_ITEM_ID:
+ Inconsistent extra value; expected `androidx.mediarouter.media.extra.ITEM_ID`, was `android.media.intent.extra.ITEM_ID`
+ActionValue: androidx.mediarouter.media.MediaControlIntent#EXTRA_ITEM_METADATA:
+ Inconsistent extra value; expected `androidx.mediarouter.media.extra.ITEM_METADATA`, was `android.media.intent.extra.ITEM_METADATA`
+ActionValue: androidx.mediarouter.media.MediaControlIntent#EXTRA_ITEM_STATUS:
+ Inconsistent extra value; expected `androidx.mediarouter.media.extra.ITEM_STATUS`, was `android.media.intent.extra.ITEM_STATUS`
+ActionValue: androidx.mediarouter.media.MediaControlIntent#EXTRA_ITEM_STATUS_UPDATE_RECEIVER:
+ Inconsistent extra value; expected `androidx.mediarouter.media.extra.ITEM_STATUS_UPDATE_RECEIVER`, was `android.media.intent.extra.ITEM_STATUS_UPDATE_RECEIVER`
+ActionValue: androidx.mediarouter.media.MediaControlIntent#EXTRA_MESSAGE:
+ Inconsistent extra value; expected `androidx.mediarouter.media.extra.MESSAGE`, was `android.media.intent.extra.MESSAGE`
+ActionValue: androidx.mediarouter.media.MediaControlIntent#EXTRA_MESSAGE_RECEIVER:
+ Inconsistent extra value; expected `androidx.mediarouter.media.extra.MESSAGE_RECEIVER`, was `android.media.intent.extra.MESSAGE_RECEIVER`
+ActionValue: androidx.mediarouter.media.MediaControlIntent#EXTRA_SESSION_ID:
+ Inconsistent extra value; expected `androidx.mediarouter.media.extra.SESSION_ID`, was `android.media.intent.extra.SESSION_ID`
+ActionValue: androidx.mediarouter.media.MediaControlIntent#EXTRA_SESSION_STATUS:
+ Inconsistent extra value; expected `androidx.mediarouter.media.extra.SESSION_STATUS`, was `android.media.intent.extra.SESSION_STATUS`
+ActionValue: androidx.mediarouter.media.MediaControlIntent#EXTRA_SESSION_STATUS_UPDATE_RECEIVER:
+ Inconsistent extra value; expected `androidx.mediarouter.media.extra.SESSION_STATUS_UPDATE_RECEIVER`, was `android.media.intent.extra.SESSION_STATUS_UPDATE_RECEIVER`
+ActionValue: androidx.mediarouter.media.MediaItemStatus#EXTRA_HTTP_RESPONSE_HEADERS:
+ Inconsistent extra value; expected `androidx.mediarouter.media.extra.HTTP_RESPONSE_HEADERS`, was `android.media.status.extra.HTTP_RESPONSE_HEADERS`
+ActionValue: androidx.mediarouter.media.MediaItemStatus#EXTRA_HTTP_STATUS_CODE:
+ Inconsistent extra value; expected `androidx.mediarouter.media.extra.HTTP_STATUS_CODE`, was `android.media.status.extra.HTTP_STATUS_CODE`
+
+
+InterfaceConstant: androidx.mediarouter.media.MediaRouteProviderService#SERVICE_INTERFACE:
+ Inconsistent interface constant; expected 'MediaRouteProviderService'`
+
+
+KotlinOperator: androidx.mediarouter.media.MediaRouteSelector#contains(androidx.mediarouter.media.MediaRouteSelector):
+ Method can be invoked as a "in" operator from Kotlin: `contains` (this is usually desirable; just make sure it makes sense for this type of object)
+
+
+RegistrationName: androidx.mediarouter.media.MediaRouter#addCallback(androidx.mediarouter.media.MediaRouteSelector, androidx.mediarouter.media.MediaRouter.Callback):
+ Callback methods should be named register/unregister; was addCallback
+RegistrationName: androidx.mediarouter.media.MediaRouter#addCallback(androidx.mediarouter.media.MediaRouteSelector, androidx.mediarouter.media.MediaRouter.Callback, int):
+ Callback methods should be named register/unregister; was addCallback
+RegistrationName: androidx.mediarouter.media.MediaRouter#removeCallback(androidx.mediarouter.media.MediaRouter.Callback):
+ Callback methods should be named register/unregister; was removeCallback
diff --git a/navigation/navigation-common-ktx/api/api_lint.ignore b/navigation/navigation-common-ktx/api/api_lint.ignore
new file mode 100644
index 0000000..6984790f
--- /dev/null
+++ b/navigation/navigation-common-ktx/api/api_lint.ignore
@@ -0,0 +1,5 @@
+// Baseline format: 1.0
+DocumentExceptions: androidx.navigation.NavArgumentBuilder#getType():
+ Method NavArgumentBuilder.getType appears to be throwing java.lang.IllegalStateException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.navigation.NavGraphBuilder#build():
+ Method NavGraphBuilder.build appears to be throwing java.lang.IllegalStateException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
diff --git a/navigation/navigation-common/api/api_lint.ignore b/navigation/navigation-common/api/api_lint.ignore
new file mode 100644
index 0000000..59abda6
--- /dev/null
+++ b/navigation/navigation-common/api/api_lint.ignore
@@ -0,0 +1,11 @@
+// Baseline format: 1.0
+KotlinOperator: androidx.navigation.NavType#get(android.os.Bundle, String):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.navigation.NavType.ParcelableArrayType#get(android.os.Bundle, String):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.navigation.NavType.ParcelableType#get(android.os.Bundle, String):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.navigation.NavType.SerializableArrayType#get(android.os.Bundle, String):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.navigation.NavType.SerializableType#get(android.os.Bundle, String):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
diff --git a/navigation/navigation-fragment-ktx/api/api_lint.ignore b/navigation/navigation-fragment-ktx/api/api_lint.ignore
new file mode 100644
index 0000000..05fb76a
--- /dev/null
+++ b/navigation/navigation-fragment-ktx/api/api_lint.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+DocumentExceptions: androidx.navigation.fragment.FragmentNavArgsLazyKt#navArgs(androidx.fragment.app.Fragment):
+ Method FragmentNavArgsLazyKt.navArgs appears to be throwing java.lang.IllegalStateException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
diff --git a/navigation/navigation-testing/api/api_lint.ignore b/navigation/navigation-testing/api/api_lint.ignore
new file mode 100644
index 0000000..f0130af
--- /dev/null
+++ b/navigation/navigation-testing/api/api_lint.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+DocumentExceptions: androidx.navigation.testing.TestNavigator#getCurrent():
+ Method TestNavigator.getCurrent appears to be throwing java.lang.IllegalStateException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
diff --git a/navigation/navigation-ui/api/api_lint.ignore b/navigation/navigation-ui/api/api_lint.ignore
new file mode 100644
index 0000000..f05bc95
--- /dev/null
+++ b/navigation/navigation-ui/api/api_lint.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+AcronymName: androidx.navigation.ui.NavigationUI:
+ Acronyms should not be capitalized in class names: was `NavigationUI`, should this be `NavigationUi`?
diff --git a/paging/common/api/api_lint.ignore b/paging/common/api/api_lint.ignore
new file mode 100644
index 0000000..b75b690
--- /dev/null
+++ b/paging/common/api/api_lint.ignore
@@ -0,0 +1,45 @@
+// Baseline format: 1.0
+DocumentExceptions: androidx.paging.DataSource#getExecutor():
+ Method DataSource.getExecutor appears to be throwing java.lang.IllegalStateException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.paging.ItemKeyedDataSource.LoadCallback#onError(Throwable):
+ Method LoadCallback.onError appears to be throwing java.lang.IllegalStateException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.paging.PageKeyedDataSource.LoadCallback#onError(Throwable):
+ Method LoadCallback.onError appears to be throwing java.lang.IllegalStateException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.paging.PageKeyedDataSource.LoadInitialCallback#onError(Throwable):
+ Method LoadInitialCallback.onError appears to be throwing java.lang.IllegalStateException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.paging.PagedList#loadAround(int):
+ Method PagedList.loadAround appears to be throwing java.lang.IndexOutOfBoundsException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.paging.PagedList.Builder#build():
+ Method Builder.build appears to be throwing java.lang.IllegalArgumentException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.paging.PagedList.Builder#buildAsync():
+ Method Builder.buildAsync appears to be throwing java.lang.IllegalArgumentException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.paging.PagedList.Config.Builder#build():
+ Method Builder.build appears to be throwing java.lang.IllegalArgumentException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.paging.PagedList.Config.Builder#setPageSize(int):
+ Method Builder.setPageSize appears to be throwing java.lang.IllegalArgumentException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.paging.PositionalDataSource.LoadInitialCallback#onError(Throwable):
+ Method LoadInitialCallback.onError appears to be throwing java.lang.IllegalStateException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.paging.PositionalDataSource.LoadRangeCallback#onError(Throwable):
+ Method LoadRangeCallback.onError appears to be throwing java.lang.IllegalStateException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+
+
+EqualsAndHashCode: androidx.paging.DataSource.BaseResult#equals(Object):
+ Must override both equals and hashCode; missing one in androidx.paging.DataSource.BaseResult
+
+
+KotlinOperator: androidx.paging.PagedList#get(int):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+
+
+RegistrationName: androidx.paging.DataSource#addInvalidatedCallback(androidx.paging.DataSource.InvalidatedCallback):
+ Callback methods should be named register/unregister; was addInvalidatedCallback
+RegistrationName: androidx.paging.DataSource#addInvalidatedCallback(kotlin.jvm.functions.Function0<kotlin.Unit>):
+ Callback methods should be named register/unregister; was addInvalidatedCallback
+RegistrationName: androidx.paging.DataSource#removeInvalidatedCallback(androidx.paging.DataSource.InvalidatedCallback):
+ Callback methods should be named register/unregister; was removeInvalidatedCallback
+RegistrationName: androidx.paging.DataSource#removeInvalidatedCallback(kotlin.jvm.functions.Function0<kotlin.Unit>):
+ Callback methods should be named register/unregister; was removeInvalidatedCallback
+RegistrationName: androidx.paging.PagedList#addWeakCallback(java.util.List<? extends T>, androidx.paging.PagedList.Callback):
+ Callback methods should be named register/unregister; was addWeakCallback
+RegistrationName: androidx.paging.PagedList#removeWeakCallback(androidx.paging.PagedList.Callback):
+ Callback methods should be named register/unregister; was removeWeakCallback
diff --git a/paging/runtime/api/api_lint.ignore b/paging/runtime/api/api_lint.ignore
new file mode 100644
index 0000000..68bf03a
--- /dev/null
+++ b/paging/runtime/api/api_lint.ignore
@@ -0,0 +1,5 @@
+// Baseline format: 1.0
+DocumentExceptions: androidx.paging.AsyncPagedListDiffer#getItem(int):
+ Method AsyncPagedListDiffer.getItem appears to be throwing java.lang.IndexOutOfBoundsException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.paging.AsyncPagedListDiffer#submitList(androidx.paging.PagedList<T>, Runnable):
+ Method AsyncPagedListDiffer.submitList appears to be throwing java.lang.IllegalArgumentException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
diff --git a/preference/api/api_lint.ignore b/preference/api/api_lint.ignore
new file mode 100644
index 0000000..5b1c512
--- /dev/null
+++ b/preference/api/api_lint.ignore
@@ -0,0 +1,23 @@
+// Baseline format: 1.0
+CallbackMethodName: androidx.preference.PreferenceManager.PreferenceComparisonCallback:
+ Callback method names must follow the on<Something> style: arePreferenceItemsTheSame
+CallbackMethodName: androidx.preference.PreferenceManager.SimplePreferenceComparisonCallback:
+ Callback method names must follow the on<Something> style: arePreferenceItemsTheSame
+
+
+ParcelConstructor: androidx.preference.Preference.BaseSavedState#BaseSavedState(android.os.Parcel):
+ Parcelable inflation is exposed through CREATOR, not raw constructors, in androidx.preference.Preference.BaseSavedState
+
+
+ParcelCreator: androidx.preference.Preference.BaseSavedState:
+ Parcelable requires `void writeToParcel(Parcel, int)`; missing in androidx.preference.Preference.BaseSavedState
+
+
+ParcelNotFinal: androidx.preference.Preference.BaseSavedState:
+ Parcelable classes must be final: androidx.preference.Preference.BaseSavedState is not final
+
+
+VisiblySynchronized: androidx.preference.PreferenceGroup#addPreference(androidx.preference.Preference):
+ Internal locks must not be exposed (synchronizing on this or class is still externally observable): method androidx.preference.PreferenceGroup.addPreference(androidx.preference.Preference)
+VisiblySynchronized: androidx.preference.PreferenceGroup#removeAll():
+ Internal locks must not be exposed (synchronizing on this or class is still externally observable): method androidx.preference.PreferenceGroup.removeAll()
diff --git a/preference/ktx/api/api_lint.ignore b/preference/ktx/api/api_lint.ignore
new file mode 100644
index 0000000..e006881
--- /dev/null
+++ b/preference/ktx/api/api_lint.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+DocumentExceptions: androidx.preference.PreferenceGroupKt#iterator(androidx.preference.PreferenceGroup):
+ Method PreferenceGroupKt.iterator appears to be throwing java.lang.IndexOutOfBoundsException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
diff --git a/recyclerview/recyclerview/api/1.1.0-beta01.txt b/recyclerview/recyclerview/api/1.1.0-beta01.txt
new file mode 100644
index 0000000..2e41ff1
--- /dev/null
+++ b/recyclerview/recyclerview/api/1.1.0-beta01.txt
@@ -0,0 +1,1037 @@
+// Signature format: 3.0
+package androidx.recyclerview.widget {
+
+ public final class AdapterListUpdateCallback implements androidx.recyclerview.widget.ListUpdateCallback {
+ ctor public AdapterListUpdateCallback(androidx.recyclerview.widget.RecyclerView.Adapter);
+ method public void onChanged(int, int, Object!);
+ method public void onInserted(int, int);
+ method public void onMoved(int, int);
+ method public void onRemoved(int, int);
+ }
+
+ public final class AsyncDifferConfig<T> {
+ method public java.util.concurrent.Executor getBackgroundThreadExecutor();
+ method public androidx.recyclerview.widget.DiffUtil.ItemCallback<T!> getDiffCallback();
+ }
+
+ public static final class AsyncDifferConfig.Builder<T> {
+ ctor public AsyncDifferConfig.Builder(androidx.recyclerview.widget.DiffUtil.ItemCallback<T!>);
+ method public androidx.recyclerview.widget.AsyncDifferConfig<T!> build();
+ method public androidx.recyclerview.widget.AsyncDifferConfig.Builder<T!> setBackgroundThreadExecutor(java.util.concurrent.Executor!);
+ }
+
+ public class AsyncListDiffer<T> {
+ ctor public AsyncListDiffer(androidx.recyclerview.widget.RecyclerView.Adapter, androidx.recyclerview.widget.DiffUtil.ItemCallback<T!>);
+ ctor public AsyncListDiffer(androidx.recyclerview.widget.ListUpdateCallback, androidx.recyclerview.widget.AsyncDifferConfig<T!>);
+ method public void addListListener(androidx.recyclerview.widget.AsyncListDiffer.ListListener<T!>);
+ method public java.util.List<T!> getCurrentList();
+ method public void removeListListener(androidx.recyclerview.widget.AsyncListDiffer.ListListener<T!>);
+ method public void submitList(java.util.List<T!>?);
+ method public void submitList(java.util.List<T!>?, Runnable?);
+ }
+
+ public static interface AsyncListDiffer.ListListener<T> {
+ method public void onCurrentListChanged(java.util.List<T!>, java.util.List<T!>);
+ }
+
+ public class AsyncListUtil<T> {
+ ctor public AsyncListUtil(Class<T!>, int, androidx.recyclerview.widget.AsyncListUtil.DataCallback<T!>, androidx.recyclerview.widget.AsyncListUtil.ViewCallback);
+ method public T? getItem(int);
+ method public int getItemCount();
+ method public void onRangeChanged();
+ method public void refresh();
+ }
+
+ public abstract static class AsyncListUtil.DataCallback<T> {
+ ctor public AsyncListUtil.DataCallback();
+ method @WorkerThread public abstract void fillData(T![], int, int);
+ method @WorkerThread public int getMaxCachedTiles();
+ method @WorkerThread public void recycleData(T![], int);
+ method @WorkerThread public abstract int refreshData();
+ }
+
+ public abstract static class AsyncListUtil.ViewCallback {
+ ctor public AsyncListUtil.ViewCallback();
+ method @UiThread public void extendRangeInto(int[], int[], int);
+ method @UiThread public abstract void getItemRangeInto(int[]);
+ method @UiThread public abstract void onDataRefresh();
+ method @UiThread public abstract void onItemLoaded(int);
+ field public static final int HINT_SCROLL_ASC = 2; // 0x2
+ field public static final int HINT_SCROLL_DESC = 1; // 0x1
+ field public static final int HINT_SCROLL_NONE = 0; // 0x0
+ }
+
+ public class BatchingListUpdateCallback implements androidx.recyclerview.widget.ListUpdateCallback {
+ ctor public BatchingListUpdateCallback(androidx.recyclerview.widget.ListUpdateCallback);
+ method public void dispatchLastEvent();
+ method public void onChanged(int, int, Object!);
+ method public void onInserted(int, int);
+ method public void onMoved(int, int);
+ method public void onRemoved(int, int);
+ }
+
+ public class DefaultItemAnimator extends androidx.recyclerview.widget.SimpleItemAnimator {
+ ctor public DefaultItemAnimator();
+ method public boolean animateAdd(androidx.recyclerview.widget.RecyclerView.ViewHolder!);
+ method public boolean animateChange(androidx.recyclerview.widget.RecyclerView.ViewHolder!, androidx.recyclerview.widget.RecyclerView.ViewHolder!, int, int, int, int);
+ method public boolean animateMove(androidx.recyclerview.widget.RecyclerView.ViewHolder!, int, int, int, int);
+ method public boolean animateRemove(androidx.recyclerview.widget.RecyclerView.ViewHolder!);
+ method public void endAnimation(androidx.recyclerview.widget.RecyclerView.ViewHolder!);
+ method public void endAnimations();
+ method public boolean isRunning();
+ method public void runPendingAnimations();
+ }
+
+ public class DiffUtil {
+ method public static androidx.recyclerview.widget.DiffUtil.DiffResult calculateDiff(androidx.recyclerview.widget.DiffUtil.Callback);
+ method public static androidx.recyclerview.widget.DiffUtil.DiffResult calculateDiff(androidx.recyclerview.widget.DiffUtil.Callback, boolean);
+ }
+
+ public abstract static class DiffUtil.Callback {
+ ctor public DiffUtil.Callback();
+ method public abstract boolean areContentsTheSame(int, int);
+ method public abstract boolean areItemsTheSame(int, int);
+ method public Object? getChangePayload(int, int);
+ method public abstract int getNewListSize();
+ method public abstract int getOldListSize();
+ }
+
+ public static class DiffUtil.DiffResult {
+ method public int convertNewPositionToOld(@IntRange(from=0) int);
+ method public int convertOldPositionToNew(@IntRange(from=0) int);
+ method public void dispatchUpdatesTo(androidx.recyclerview.widget.RecyclerView.Adapter);
+ method public void dispatchUpdatesTo(androidx.recyclerview.widget.ListUpdateCallback);
+ field public static final int NO_POSITION = -1; // 0xffffffff
+ }
+
+ public abstract static class DiffUtil.ItemCallback<T> {
+ ctor public DiffUtil.ItemCallback();
+ method public abstract boolean areContentsTheSame(T, T);
+ method public abstract boolean areItemsTheSame(T, T);
+ method public Object? getChangePayload(T, T);
+ }
+
+ public class DividerItemDecoration extends androidx.recyclerview.widget.RecyclerView.ItemDecoration {
+ ctor public DividerItemDecoration(android.content.Context!, int);
+ method public android.graphics.drawable.Drawable? getDrawable();
+ method public void setDrawable(android.graphics.drawable.Drawable);
+ method public void setOrientation(int);
+ field public static final int HORIZONTAL = 0; // 0x0
+ field public static final int VERTICAL = 1; // 0x1
+ }
+
+ public class GridLayoutManager extends androidx.recyclerview.widget.LinearLayoutManager {
+ ctor public GridLayoutManager(android.content.Context!, android.util.AttributeSet!, int, int);
+ ctor public GridLayoutManager(android.content.Context!, int);
+ ctor public GridLayoutManager(android.content.Context!, int, int, boolean);
+ method public int getSpanCount();
+ method public androidx.recyclerview.widget.GridLayoutManager.SpanSizeLookup! getSpanSizeLookup();
+ method public boolean isUsingSpansToEstimateScrollbarDimensions();
+ method public void setSpanCount(int);
+ method public void setSpanSizeLookup(androidx.recyclerview.widget.GridLayoutManager.SpanSizeLookup!);
+ method public void setUsingSpansToEstimateScrollbarDimensions(boolean);
+ field public static final int DEFAULT_SPAN_COUNT = -1; // 0xffffffff
+ }
+
+ public static final class GridLayoutManager.DefaultSpanSizeLookup extends androidx.recyclerview.widget.GridLayoutManager.SpanSizeLookup {
+ ctor public GridLayoutManager.DefaultSpanSizeLookup();
+ method public int getSpanSize(int);
+ }
+
+ public static class GridLayoutManager.LayoutParams extends androidx.recyclerview.widget.RecyclerView.LayoutParams {
+ ctor public GridLayoutManager.LayoutParams(android.content.Context!, android.util.AttributeSet!);
+ ctor public GridLayoutManager.LayoutParams(int, int);
+ ctor public GridLayoutManager.LayoutParams(android.view.ViewGroup.MarginLayoutParams!);
+ ctor public GridLayoutManager.LayoutParams(android.view.ViewGroup.LayoutParams!);
+ ctor public GridLayoutManager.LayoutParams(androidx.recyclerview.widget.RecyclerView.LayoutParams!);
+ method public int getSpanIndex();
+ method public int getSpanSize();
+ field public static final int INVALID_SPAN_ID = -1; // 0xffffffff
+ }
+
+ public abstract static class GridLayoutManager.SpanSizeLookup {
+ ctor public GridLayoutManager.SpanSizeLookup();
+ method public int getSpanGroupIndex(int, int);
+ method public int getSpanIndex(int, int);
+ method public abstract int getSpanSize(int);
+ method public void invalidateSpanGroupIndexCache();
+ method public void invalidateSpanIndexCache();
+ method public boolean isSpanGroupIndexCacheEnabled();
+ method public boolean isSpanIndexCacheEnabled();
+ method public void setSpanGroupIndexCacheEnabled(boolean);
+ method public void setSpanIndexCacheEnabled(boolean);
+ }
+
+ public class ItemTouchHelper extends androidx.recyclerview.widget.RecyclerView.ItemDecoration implements androidx.recyclerview.widget.RecyclerView.OnChildAttachStateChangeListener {
+ ctor public ItemTouchHelper(androidx.recyclerview.widget.ItemTouchHelper.Callback);
+ method public void attachToRecyclerView(androidx.recyclerview.widget.RecyclerView?);
+ method public void onChildViewAttachedToWindow(android.view.View);
+ method public void onChildViewDetachedFromWindow(android.view.View);
+ method public void startDrag(androidx.recyclerview.widget.RecyclerView.ViewHolder);
+ method public void startSwipe(androidx.recyclerview.widget.RecyclerView.ViewHolder);
+ field public static final int ACTION_STATE_DRAG = 2; // 0x2
+ field public static final int ACTION_STATE_IDLE = 0; // 0x0
+ field public static final int ACTION_STATE_SWIPE = 1; // 0x1
+ field public static final int ANIMATION_TYPE_DRAG = 8; // 0x8
+ field public static final int ANIMATION_TYPE_SWIPE_CANCEL = 4; // 0x4
+ field public static final int ANIMATION_TYPE_SWIPE_SUCCESS = 2; // 0x2
+ field public static final int DOWN = 2; // 0x2
+ field public static final int END = 32; // 0x20
+ field public static final int LEFT = 4; // 0x4
+ field public static final int RIGHT = 8; // 0x8
+ field public static final int START = 16; // 0x10
+ field public static final int UP = 1; // 0x1
+ }
+
+ public abstract static class ItemTouchHelper.Callback {
+ ctor public ItemTouchHelper.Callback();
+ method public boolean canDropOver(androidx.recyclerview.widget.RecyclerView, androidx.recyclerview.widget.RecyclerView.ViewHolder, androidx.recyclerview.widget.RecyclerView.ViewHolder);
+ method public androidx.recyclerview.widget.RecyclerView.ViewHolder! chooseDropTarget(androidx.recyclerview.widget.RecyclerView.ViewHolder, java.util.List<androidx.recyclerview.widget.RecyclerView.ViewHolder!>, int, int);
+ method public void clearView(androidx.recyclerview.widget.RecyclerView, androidx.recyclerview.widget.RecyclerView.ViewHolder);
+ method public int convertToAbsoluteDirection(int, int);
+ method public static int convertToRelativeDirection(int, int);
+ method public long getAnimationDuration(androidx.recyclerview.widget.RecyclerView, int, float, float);
+ method public int getBoundingBoxMargin();
+ method public static androidx.recyclerview.widget.ItemTouchUIUtil getDefaultUIUtil();
+ method public float getMoveThreshold(androidx.recyclerview.widget.RecyclerView.ViewHolder);
+ method public abstract int getMovementFlags(androidx.recyclerview.widget.RecyclerView, androidx.recyclerview.widget.RecyclerView.ViewHolder);
+ method public float getSwipeEscapeVelocity(float);
+ method public float getSwipeThreshold(androidx.recyclerview.widget.RecyclerView.ViewHolder);
+ method public float getSwipeVelocityThreshold(float);
+ method public int interpolateOutOfBoundsScroll(androidx.recyclerview.widget.RecyclerView, int, int, int, long);
+ method public boolean isItemViewSwipeEnabled();
+ method public boolean isLongPressDragEnabled();
+ method public static int makeFlag(int, int);
+ method public static int makeMovementFlags(int, int);
+ method public void onChildDraw(android.graphics.Canvas, androidx.recyclerview.widget.RecyclerView, androidx.recyclerview.widget.RecyclerView.ViewHolder, float, float, int, boolean);
+ method public void onChildDrawOver(android.graphics.Canvas, androidx.recyclerview.widget.RecyclerView, androidx.recyclerview.widget.RecyclerView.ViewHolder!, float, float, int, boolean);
+ method public abstract boolean onMove(androidx.recyclerview.widget.RecyclerView, androidx.recyclerview.widget.RecyclerView.ViewHolder, androidx.recyclerview.widget.RecyclerView.ViewHolder);
+ method public void onMoved(androidx.recyclerview.widget.RecyclerView, androidx.recyclerview.widget.RecyclerView.ViewHolder, int, androidx.recyclerview.widget.RecyclerView.ViewHolder, int, int, int);
+ method public void onSelectedChanged(androidx.recyclerview.widget.RecyclerView.ViewHolder?, int);
+ method public abstract void onSwiped(androidx.recyclerview.widget.RecyclerView.ViewHolder, int);
+ field public static final int DEFAULT_DRAG_ANIMATION_DURATION = 200; // 0xc8
+ field public static final int DEFAULT_SWIPE_ANIMATION_DURATION = 250; // 0xfa
+ }
+
+ public abstract static class ItemTouchHelper.SimpleCallback extends androidx.recyclerview.widget.ItemTouchHelper.Callback {
+ ctor public ItemTouchHelper.SimpleCallback(int, int);
+ method public int getDragDirs(androidx.recyclerview.widget.RecyclerView, androidx.recyclerview.widget.RecyclerView.ViewHolder);
+ method public int getMovementFlags(androidx.recyclerview.widget.RecyclerView, androidx.recyclerview.widget.RecyclerView.ViewHolder);
+ method public int getSwipeDirs(androidx.recyclerview.widget.RecyclerView, androidx.recyclerview.widget.RecyclerView.ViewHolder);
+ method public void setDefaultDragDirs(int);
+ method public void setDefaultSwipeDirs(int);
+ }
+
+ public static interface ItemTouchHelper.ViewDropHandler {
+ method public void prepareForDrop(android.view.View, android.view.View, int, int);
+ }
+
+ public interface ItemTouchUIUtil {
+ method public void clearView(android.view.View!);
+ method public void onDraw(android.graphics.Canvas!, androidx.recyclerview.widget.RecyclerView!, android.view.View!, float, float, int, boolean);
+ method public void onDrawOver(android.graphics.Canvas!, androidx.recyclerview.widget.RecyclerView!, android.view.View!, float, float, int, boolean);
+ method public void onSelected(android.view.View!);
+ }
+
+ public class LinearLayoutManager extends androidx.recyclerview.widget.RecyclerView.LayoutManager implements androidx.recyclerview.widget.ItemTouchHelper.ViewDropHandler androidx.recyclerview.widget.RecyclerView.SmoothScroller.ScrollVectorProvider {
+ ctor public LinearLayoutManager(android.content.Context!);
+ ctor public LinearLayoutManager(android.content.Context!, int, boolean);
+ ctor public LinearLayoutManager(android.content.Context!, android.util.AttributeSet!, int, int);
+ method protected void calculateExtraLayoutSpace(androidx.recyclerview.widget.RecyclerView.State, int[]);
+ method public android.graphics.PointF! computeScrollVectorForPosition(int);
+ method public int findFirstCompletelyVisibleItemPosition();
+ method public int findFirstVisibleItemPosition();
+ method public int findLastCompletelyVisibleItemPosition();
+ method public int findLastVisibleItemPosition();
+ method public androidx.recyclerview.widget.RecyclerView.LayoutParams! generateDefaultLayoutParams();
+ method @Deprecated protected int getExtraLayoutSpace(androidx.recyclerview.widget.RecyclerView.State!);
+ method public int getInitialPrefetchItemCount();
+ method public int getOrientation();
+ method public boolean getRecycleChildrenOnDetach();
+ method public boolean getReverseLayout();
+ method public boolean getStackFromEnd();
+ method protected boolean isLayoutRTL();
+ method public boolean isSmoothScrollbarEnabled();
+ method public void prepareForDrop(android.view.View, android.view.View, int, int);
+ method public void scrollToPositionWithOffset(int, int);
+ method public void setInitialPrefetchItemCount(int);
+ method public void setOrientation(int);
+ method public void setRecycleChildrenOnDetach(boolean);
+ method public void setReverseLayout(boolean);
+ method public void setSmoothScrollbarEnabled(boolean);
+ method public void setStackFromEnd(boolean);
+ field public static final int HORIZONTAL = 0; // 0x0
+ field public static final int INVALID_OFFSET = -2147483648; // 0x80000000
+ field public static final int VERTICAL = 1; // 0x1
+ }
+
+ protected static class LinearLayoutManager.LayoutChunkResult {
+ ctor protected LinearLayoutManager.LayoutChunkResult();
+ field public int mConsumed;
+ field public boolean mFinished;
+ field public boolean mFocusable;
+ field public boolean mIgnoreConsumed;
+ }
+
+ public class LinearSmoothScroller extends androidx.recyclerview.widget.RecyclerView.SmoothScroller {
+ ctor public LinearSmoothScroller(android.content.Context!);
+ method public int calculateDtToFit(int, int, int, int, int);
+ method public int calculateDxToMakeVisible(android.view.View!, int);
+ method public int calculateDyToMakeVisible(android.view.View!, int);
+ method protected float calculateSpeedPerPixel(android.util.DisplayMetrics!);
+ method protected int calculateTimeForDeceleration(int);
+ method protected int calculateTimeForScrolling(int);
+ method protected int getHorizontalSnapPreference();
+ method protected int getVerticalSnapPreference();
+ method protected void onSeekTargetStep(int, int, androidx.recyclerview.widget.RecyclerView.State!, androidx.recyclerview.widget.RecyclerView.SmoothScroller.Action!);
+ method protected void onStart();
+ method protected void onStop();
+ method protected void onTargetFound(android.view.View!, androidx.recyclerview.widget.RecyclerView.State!, androidx.recyclerview.widget.RecyclerView.SmoothScroller.Action!);
+ method protected void updateActionForInterimTarget(androidx.recyclerview.widget.RecyclerView.SmoothScroller.Action!);
+ field public static final int SNAP_TO_ANY = 0; // 0x0
+ field public static final int SNAP_TO_END = 1; // 0x1
+ field public static final int SNAP_TO_START = -1; // 0xffffffff
+ field protected final android.view.animation.DecelerateInterpolator! mDecelerateInterpolator;
+ field protected int mInterimTargetDx;
+ field protected int mInterimTargetDy;
+ field protected final android.view.animation.LinearInterpolator! mLinearInterpolator;
+ field protected android.graphics.PointF! mTargetVector;
+ }
+
+ public class LinearSnapHelper extends androidx.recyclerview.widget.SnapHelper {
+ ctor public LinearSnapHelper();
+ method public int[]! calculateDistanceToFinalSnap(androidx.recyclerview.widget.RecyclerView.LayoutManager, android.view.View);
+ method public android.view.View! findSnapView(androidx.recyclerview.widget.RecyclerView.LayoutManager!);
+ method public int findTargetSnapPosition(androidx.recyclerview.widget.RecyclerView.LayoutManager!, int, int);
+ }
+
+ public abstract class ListAdapter<T, VH extends androidx.recyclerview.widget.RecyclerView.ViewHolder> extends androidx.recyclerview.widget.RecyclerView.Adapter<VH> {
+ ctor protected ListAdapter(androidx.recyclerview.widget.DiffUtil.ItemCallback<T!>);
+ ctor protected ListAdapter(androidx.recyclerview.widget.AsyncDifferConfig<T!>);
+ method public java.util.List<T!> getCurrentList();
+ method protected T! getItem(int);
+ method public int getItemCount();
+ method public void onCurrentListChanged(java.util.List<T!>, java.util.List<T!>);
+ method public void submitList(java.util.List<T!>?);
+ method public void submitList(java.util.List<T!>?, Runnable?);
+ }
+
+ public interface ListUpdateCallback {
+ method public void onChanged(int, int, Object?);
+ method public void onInserted(int, int);
+ method public void onMoved(int, int);
+ method public void onRemoved(int, int);
+ }
+
+ public abstract class OrientationHelper {
+ method public static androidx.recyclerview.widget.OrientationHelper! createHorizontalHelper(androidx.recyclerview.widget.RecyclerView.LayoutManager!);
+ method public static androidx.recyclerview.widget.OrientationHelper! createOrientationHelper(androidx.recyclerview.widget.RecyclerView.LayoutManager!, int);
+ method public static androidx.recyclerview.widget.OrientationHelper! createVerticalHelper(androidx.recyclerview.widget.RecyclerView.LayoutManager!);
+ method public abstract int getDecoratedEnd(android.view.View!);
+ method public abstract int getDecoratedMeasurement(android.view.View!);
+ method public abstract int getDecoratedMeasurementInOther(android.view.View!);
+ method public abstract int getDecoratedStart(android.view.View!);
+ method public abstract int getEnd();
+ method public abstract int getEndAfterPadding();
+ method public abstract int getEndPadding();
+ method public androidx.recyclerview.widget.RecyclerView.LayoutManager! getLayoutManager();
+ method public abstract int getMode();
+ method public abstract int getModeInOther();
+ method public abstract int getStartAfterPadding();
+ method public abstract int getTotalSpace();
+ method public int getTotalSpaceChange();
+ method public abstract int getTransformedEndWithDecoration(android.view.View!);
+ method public abstract int getTransformedStartWithDecoration(android.view.View!);
+ method public abstract void offsetChild(android.view.View!, int);
+ method public abstract void offsetChildren(int);
+ method public void onLayoutComplete();
+ field public static final int HORIZONTAL = 0; // 0x0
+ field public static final int VERTICAL = 1; // 0x1
+ field protected final androidx.recyclerview.widget.RecyclerView.LayoutManager! mLayoutManager;
+ }
+
+ public class PagerSnapHelper extends androidx.recyclerview.widget.SnapHelper {
+ ctor public PagerSnapHelper();
+ method public int[]? calculateDistanceToFinalSnap(androidx.recyclerview.widget.RecyclerView.LayoutManager, android.view.View);
+ method protected androidx.recyclerview.widget.LinearSmoothScroller! createSnapScroller(androidx.recyclerview.widget.RecyclerView.LayoutManager!);
+ method public android.view.View? findSnapView(androidx.recyclerview.widget.RecyclerView.LayoutManager!);
+ method public int findTargetSnapPosition(androidx.recyclerview.widget.RecyclerView.LayoutManager!, int, int);
+ }
+
+ public class RecyclerView extends android.view.ViewGroup implements androidx.core.view.NestedScrollingChild2 androidx.core.view.NestedScrollingChild3 androidx.core.view.ScrollingView {
+ ctor public RecyclerView(android.content.Context);
+ ctor public RecyclerView(android.content.Context, android.util.AttributeSet?);
+ ctor public RecyclerView(android.content.Context, android.util.AttributeSet?, int);
+ method public void addItemDecoration(androidx.recyclerview.widget.RecyclerView.ItemDecoration, int);
+ method public void addItemDecoration(androidx.recyclerview.widget.RecyclerView.ItemDecoration);
+ method public void addOnChildAttachStateChangeListener(androidx.recyclerview.widget.RecyclerView.OnChildAttachStateChangeListener);
+ method public void addOnItemTouchListener(androidx.recyclerview.widget.RecyclerView.OnItemTouchListener);
+ method public void addOnScrollListener(androidx.recyclerview.widget.RecyclerView.OnScrollListener);
+ method public void clearOnChildAttachStateChangeListeners();
+ method public void clearOnScrollListeners();
+ method public int computeHorizontalScrollExtent();
+ method public int computeHorizontalScrollOffset();
+ method public int computeHorizontalScrollRange();
+ method public int computeVerticalScrollExtent();
+ method public int computeVerticalScrollOffset();
+ method public int computeVerticalScrollRange();
+ method public boolean dispatchNestedPreScroll(int, int, int[]!, int[]!, int);
+ method public boolean dispatchNestedScroll(int, int, int, int, int[]!, int);
+ method public final void dispatchNestedScroll(int, int, int, int, int[]!, int, int[]);
+ method public boolean drawChild(android.graphics.Canvas!, android.view.View!, long);
+ method public android.view.View? findChildViewUnder(float, float);
+ method public android.view.View? findContainingItemView(android.view.View);
+ method public androidx.recyclerview.widget.RecyclerView.ViewHolder? findContainingViewHolder(android.view.View);
+ method public androidx.recyclerview.widget.RecyclerView.ViewHolder? findViewHolderForAdapterPosition(int);
+ method public androidx.recyclerview.widget.RecyclerView.ViewHolder! findViewHolderForItemId(long);
+ method public androidx.recyclerview.widget.RecyclerView.ViewHolder? findViewHolderForLayoutPosition(int);
+ method @Deprecated public androidx.recyclerview.widget.RecyclerView.ViewHolder? findViewHolderForPosition(int);
+ method public boolean fling(int, int);
+ method public androidx.recyclerview.widget.RecyclerView.Adapter? getAdapter();
+ method public int getChildAdapterPosition(android.view.View);
+ method public long getChildItemId(android.view.View);
+ method public int getChildLayoutPosition(android.view.View);
+ method @Deprecated public int getChildPosition(android.view.View);
+ method public androidx.recyclerview.widget.RecyclerView.ViewHolder! getChildViewHolder(android.view.View);
+ method public androidx.recyclerview.widget.RecyclerViewAccessibilityDelegate? getCompatAccessibilityDelegate();
+ method public void getDecoratedBoundsWithMargins(android.view.View, android.graphics.Rect);
+ method public androidx.recyclerview.widget.RecyclerView.EdgeEffectFactory getEdgeEffectFactory();
+ method public androidx.recyclerview.widget.RecyclerView.ItemAnimator? getItemAnimator();
+ method public androidx.recyclerview.widget.RecyclerView.ItemDecoration getItemDecorationAt(int);
+ method public int getItemDecorationCount();
+ method public androidx.recyclerview.widget.RecyclerView.LayoutManager? getLayoutManager();
+ method public int getMaxFlingVelocity();
+ method public int getMinFlingVelocity();
+ method public androidx.recyclerview.widget.RecyclerView.OnFlingListener? getOnFlingListener();
+ method public boolean getPreserveFocusAfterLayout();
+ method public androidx.recyclerview.widget.RecyclerView.RecycledViewPool getRecycledViewPool();
+ method public int getScrollState();
+ method public boolean hasFixedSize();
+ method public boolean hasNestedScrollingParent(int);
+ method public boolean hasPendingAdapterUpdates();
+ method public void invalidateItemDecorations();
+ method public boolean isAnimating();
+ method public boolean isComputingLayout();
+ method @Deprecated public boolean isLayoutFrozen();
+ method public final boolean isLayoutSuppressed();
+ method public void offsetChildrenHorizontal(@Px int);
+ method public void offsetChildrenVertical(@Px int);
+ method public void onChildAttachedToWindow(android.view.View);
+ method public void onChildDetachedFromWindow(android.view.View);
+ method public void onDraw(android.graphics.Canvas!);
+ method public void onScrollStateChanged(int);
+ method public void onScrolled(@Px int, @Px int);
+ method public void removeItemDecoration(androidx.recyclerview.widget.RecyclerView.ItemDecoration);
+ method public void removeItemDecorationAt(int);
+ method public void removeOnChildAttachStateChangeListener(androidx.recyclerview.widget.RecyclerView.OnChildAttachStateChangeListener);
+ method public void removeOnItemTouchListener(androidx.recyclerview.widget.RecyclerView.OnItemTouchListener);
+ method public void removeOnScrollListener(androidx.recyclerview.widget.RecyclerView.OnScrollListener);
+ method public void scrollToPosition(int);
+ method public void setAccessibilityDelegateCompat(androidx.recyclerview.widget.RecyclerViewAccessibilityDelegate?);
+ method public void setAdapter(androidx.recyclerview.widget.RecyclerView.Adapter?);
+ method public void setChildDrawingOrderCallback(androidx.recyclerview.widget.RecyclerView.ChildDrawingOrderCallback?);
+ method public void setEdgeEffectFactory(androidx.recyclerview.widget.RecyclerView.EdgeEffectFactory);
+ method public void setHasFixedSize(boolean);
+ method public void setItemAnimator(androidx.recyclerview.widget.RecyclerView.ItemAnimator?);
+ method public void setItemViewCacheSize(int);
+ method @Deprecated public void setLayoutFrozen(boolean);
+ method public void setLayoutManager(androidx.recyclerview.widget.RecyclerView.LayoutManager?);
+ method @Deprecated public void setLayoutTransition(android.animation.LayoutTransition!);
+ method public void setOnFlingListener(androidx.recyclerview.widget.RecyclerView.OnFlingListener?);
+ method @Deprecated public void setOnScrollListener(androidx.recyclerview.widget.RecyclerView.OnScrollListener?);
+ method public void setPreserveFocusAfterLayout(boolean);
+ method public void setRecycledViewPool(androidx.recyclerview.widget.RecyclerView.RecycledViewPool?);
+ method public void setRecyclerListener(androidx.recyclerview.widget.RecyclerView.RecyclerListener?);
+ method public void setScrollingTouchSlop(int);
+ method public void setViewCacheExtension(androidx.recyclerview.widget.RecyclerView.ViewCacheExtension?);
+ method public void smoothScrollBy(@Px int, @Px int);
+ method public void smoothScrollBy(@Px int, @Px int, android.view.animation.Interpolator?);
+ method public void smoothScrollBy(@Px int, @Px int, android.view.animation.Interpolator?, int);
+ method public void smoothScrollToPosition(int);
+ method public boolean startNestedScroll(int, int);
+ method public void stopNestedScroll(int);
+ method public void stopScroll();
+ method public final void suppressLayout(boolean);
+ method public void swapAdapter(androidx.recyclerview.widget.RecyclerView.Adapter?, boolean);
+ field public static final int HORIZONTAL = 0; // 0x0
+ field public static final int INVALID_TYPE = -1; // 0xffffffff
+ field public static final long NO_ID = -1L; // 0xffffffffffffffffL
+ field public static final int NO_POSITION = -1; // 0xffffffff
+ field public static final int SCROLL_STATE_DRAGGING = 1; // 0x1
+ field public static final int SCROLL_STATE_IDLE = 0; // 0x0
+ field public static final int SCROLL_STATE_SETTLING = 2; // 0x2
+ field public static final int TOUCH_SLOP_DEFAULT = 0; // 0x0
+ field public static final int TOUCH_SLOP_PAGING = 1; // 0x1
+ field public static final int UNDEFINED_DURATION = -2147483648; // 0x80000000
+ field public static final int VERTICAL = 1; // 0x1
+ }
+
+ public abstract static class RecyclerView.Adapter<VH extends androidx.recyclerview.widget.RecyclerView.ViewHolder> {
+ ctor public RecyclerView.Adapter();
+ method public final void bindViewHolder(VH, int);
+ method public final VH createViewHolder(android.view.ViewGroup, int);
+ method public abstract int getItemCount();
+ method public long getItemId(int);
+ method public int getItemViewType(int);
+ method public final boolean hasObservers();
+ method public final boolean hasStableIds();
+ method public final void notifyDataSetChanged();
+ method public final void notifyItemChanged(int);
+ method public final void notifyItemChanged(int, Object?);
+ method public final void notifyItemInserted(int);
+ method public final void notifyItemMoved(int, int);
+ method public final void notifyItemRangeChanged(int, int);
+ method public final void notifyItemRangeChanged(int, int, Object?);
+ method public final void notifyItemRangeInserted(int, int);
+ method public final void notifyItemRangeRemoved(int, int);
+ method public final void notifyItemRemoved(int);
+ method public void onAttachedToRecyclerView(androidx.recyclerview.widget.RecyclerView);
+ method public abstract void onBindViewHolder(VH, int);
+ method public void onBindViewHolder(VH, int, java.util.List<java.lang.Object!>);
+ method public abstract VH onCreateViewHolder(android.view.ViewGroup, int);
+ method public void onDetachedFromRecyclerView(androidx.recyclerview.widget.RecyclerView);
+ method public boolean onFailedToRecycleView(VH);
+ method public void onViewAttachedToWindow(VH);
+ method public void onViewDetachedFromWindow(VH);
+ method public void onViewRecycled(VH);
+ method public void registerAdapterDataObserver(androidx.recyclerview.widget.RecyclerView.AdapterDataObserver);
+ method public void setHasStableIds(boolean);
+ method public void unregisterAdapterDataObserver(androidx.recyclerview.widget.RecyclerView.AdapterDataObserver);
+ }
+
+ public abstract static class RecyclerView.AdapterDataObserver {
+ ctor public RecyclerView.AdapterDataObserver();
+ method public void onChanged();
+ method public void onItemRangeChanged(int, int);
+ method public void onItemRangeChanged(int, int, Object?);
+ method public void onItemRangeInserted(int, int);
+ method public void onItemRangeMoved(int, int, int);
+ method public void onItemRangeRemoved(int, int);
+ }
+
+ public static interface RecyclerView.ChildDrawingOrderCallback {
+ method public int onGetChildDrawingOrder(int, int);
+ }
+
+ public static class RecyclerView.EdgeEffectFactory {
+ ctor public RecyclerView.EdgeEffectFactory();
+ method protected android.widget.EdgeEffect createEdgeEffect(androidx.recyclerview.widget.RecyclerView, @androidx.recyclerview.widget.RecyclerView.EdgeEffectFactory.EdgeDirection int);
+ field public static final int DIRECTION_BOTTOM = 3; // 0x3
+ field public static final int DIRECTION_LEFT = 0; // 0x0
+ field public static final int DIRECTION_RIGHT = 2; // 0x2
+ field public static final int DIRECTION_TOP = 1; // 0x1
+ }
+
+ @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({androidx.recyclerview.widget.RecyclerView.EdgeEffectFactory.DIRECTION_LEFT, androidx.recyclerview.widget.RecyclerView.EdgeEffectFactory.DIRECTION_TOP, androidx.recyclerview.widget.RecyclerView.EdgeEffectFactory.DIRECTION_RIGHT, androidx.recyclerview.widget.RecyclerView.EdgeEffectFactory.DIRECTION_BOTTOM}) public static @interface RecyclerView.EdgeEffectFactory.EdgeDirection {
+ }
+
+ public abstract static class RecyclerView.ItemAnimator {
+ ctor public RecyclerView.ItemAnimator();
+ method public abstract boolean animateAppearance(androidx.recyclerview.widget.RecyclerView.ViewHolder, androidx.recyclerview.widget.RecyclerView.ItemAnimator.ItemHolderInfo?, androidx.recyclerview.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+ method public abstract boolean animateChange(androidx.recyclerview.widget.RecyclerView.ViewHolder, androidx.recyclerview.widget.RecyclerView.ViewHolder, androidx.recyclerview.widget.RecyclerView.ItemAnimator.ItemHolderInfo, androidx.recyclerview.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+ method public abstract boolean animateDisappearance(androidx.recyclerview.widget.RecyclerView.ViewHolder, androidx.recyclerview.widget.RecyclerView.ItemAnimator.ItemHolderInfo, androidx.recyclerview.widget.RecyclerView.ItemAnimator.ItemHolderInfo?);
+ method public abstract boolean animatePersistence(androidx.recyclerview.widget.RecyclerView.ViewHolder, androidx.recyclerview.widget.RecyclerView.ItemAnimator.ItemHolderInfo, androidx.recyclerview.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+ method public boolean canReuseUpdatedViewHolder(androidx.recyclerview.widget.RecyclerView.ViewHolder);
+ method public boolean canReuseUpdatedViewHolder(androidx.recyclerview.widget.RecyclerView.ViewHolder, java.util.List<java.lang.Object!>);
+ method public final void dispatchAnimationFinished(androidx.recyclerview.widget.RecyclerView.ViewHolder);
+ method public final void dispatchAnimationStarted(androidx.recyclerview.widget.RecyclerView.ViewHolder);
+ method public final void dispatchAnimationsFinished();
+ method public abstract void endAnimation(androidx.recyclerview.widget.RecyclerView.ViewHolder);
+ method public abstract void endAnimations();
+ method public long getAddDuration();
+ method public long getChangeDuration();
+ method public long getMoveDuration();
+ method public long getRemoveDuration();
+ method public abstract boolean isRunning();
+ method public final boolean isRunning(androidx.recyclerview.widget.RecyclerView.ItemAnimator.ItemAnimatorFinishedListener?);
+ method public androidx.recyclerview.widget.RecyclerView.ItemAnimator.ItemHolderInfo obtainHolderInfo();
+ method public void onAnimationFinished(androidx.recyclerview.widget.RecyclerView.ViewHolder);
+ method public void onAnimationStarted(androidx.recyclerview.widget.RecyclerView.ViewHolder);
+ method public androidx.recyclerview.widget.RecyclerView.ItemAnimator.ItemHolderInfo recordPostLayoutInformation(androidx.recyclerview.widget.RecyclerView.State, androidx.recyclerview.widget.RecyclerView.ViewHolder);
+ method public androidx.recyclerview.widget.RecyclerView.ItemAnimator.ItemHolderInfo recordPreLayoutInformation(androidx.recyclerview.widget.RecyclerView.State, androidx.recyclerview.widget.RecyclerView.ViewHolder, @androidx.recyclerview.widget.RecyclerView.ItemAnimator.AdapterChanges int, java.util.List<java.lang.Object!>);
+ method public abstract void runPendingAnimations();
+ method public void setAddDuration(long);
+ method public void setChangeDuration(long);
+ method public void setMoveDuration(long);
+ method public void setRemoveDuration(long);
+ field public static final int FLAG_APPEARED_IN_PRE_LAYOUT = 4096; // 0x1000
+ field public static final int FLAG_CHANGED = 2; // 0x2
+ field public static final int FLAG_INVALIDATED = 4; // 0x4
+ field public static final int FLAG_MOVED = 2048; // 0x800
+ field public static final int FLAG_REMOVED = 8; // 0x8
+ }
+
+ @IntDef(flag=true, value={androidx.recyclerview.widget.RecyclerView.ItemAnimator.FLAG_CHANGED, androidx.recyclerview.widget.RecyclerView.ItemAnimator.FLAG_REMOVED, androidx.recyclerview.widget.RecyclerView.ItemAnimator.FLAG_MOVED, androidx.recyclerview.widget.RecyclerView.ItemAnimator.FLAG_INVALIDATED, androidx.recyclerview.widget.RecyclerView.ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface RecyclerView.ItemAnimator.AdapterChanges {
+ }
+
+ public static interface RecyclerView.ItemAnimator.ItemAnimatorFinishedListener {
+ method public void onAnimationsFinished();
+ }
+
+ public static class RecyclerView.ItemAnimator.ItemHolderInfo {
+ ctor public RecyclerView.ItemAnimator.ItemHolderInfo();
+ method public androidx.recyclerview.widget.RecyclerView.ItemAnimator.ItemHolderInfo setFrom(androidx.recyclerview.widget.RecyclerView.ViewHolder);
+ method public androidx.recyclerview.widget.RecyclerView.ItemAnimator.ItemHolderInfo setFrom(androidx.recyclerview.widget.RecyclerView.ViewHolder, @androidx.recyclerview.widget.RecyclerView.ItemAnimator.AdapterChanges int);
+ field public int bottom;
+ field @androidx.recyclerview.widget.RecyclerView.ItemAnimator.AdapterChanges public int changeFlags;
+ field public int left;
+ field public int right;
+ field public int top;
+ }
+
+ public abstract static class RecyclerView.ItemDecoration {
+ ctor public RecyclerView.ItemDecoration();
+ method @Deprecated public void getItemOffsets(android.graphics.Rect, int, androidx.recyclerview.widget.RecyclerView);
+ method public void getItemOffsets(android.graphics.Rect, android.view.View, androidx.recyclerview.widget.RecyclerView, androidx.recyclerview.widget.RecyclerView.State);
+ method public void onDraw(android.graphics.Canvas, androidx.recyclerview.widget.RecyclerView, androidx.recyclerview.widget.RecyclerView.State);
+ method @Deprecated public void onDraw(android.graphics.Canvas, androidx.recyclerview.widget.RecyclerView);
+ method public void onDrawOver(android.graphics.Canvas, androidx.recyclerview.widget.RecyclerView, androidx.recyclerview.widget.RecyclerView.State);
+ method @Deprecated public void onDrawOver(android.graphics.Canvas, androidx.recyclerview.widget.RecyclerView);
+ }
+
+ public abstract static class RecyclerView.LayoutManager {
+ ctor public RecyclerView.LayoutManager();
+ method public void addDisappearingView(android.view.View!);
+ method public void addDisappearingView(android.view.View!, int);
+ method public void addView(android.view.View!);
+ method public void addView(android.view.View!, int);
+ method public void assertInLayoutOrScroll(String!);
+ method public void assertNotInLayoutOrScroll(String!);
+ method public void attachView(android.view.View, int, androidx.recyclerview.widget.RecyclerView.LayoutParams!);
+ method public void attachView(android.view.View, int);
+ method public void attachView(android.view.View);
+ method public void calculateItemDecorationsForChild(android.view.View, android.graphics.Rect);
+ method public boolean canScrollHorizontally();
+ method public boolean canScrollVertically();
+ method public boolean checkLayoutParams(androidx.recyclerview.widget.RecyclerView.LayoutParams!);
+ method public static int chooseSize(int, int, int);
+ method public void collectAdjacentPrefetchPositions(int, int, androidx.recyclerview.widget.RecyclerView.State!, androidx.recyclerview.widget.RecyclerView.LayoutManager.LayoutPrefetchRegistry!);
+ method public void collectInitialPrefetchPositions(int, androidx.recyclerview.widget.RecyclerView.LayoutManager.LayoutPrefetchRegistry!);
+ method public int computeHorizontalScrollExtent(androidx.recyclerview.widget.RecyclerView.State);
+ method public int computeHorizontalScrollOffset(androidx.recyclerview.widget.RecyclerView.State);
+ method public int computeHorizontalScrollRange(androidx.recyclerview.widget.RecyclerView.State);
+ method public int computeVerticalScrollExtent(androidx.recyclerview.widget.RecyclerView.State);
+ method public int computeVerticalScrollOffset(androidx.recyclerview.widget.RecyclerView.State);
+ method public int computeVerticalScrollRange(androidx.recyclerview.widget.RecyclerView.State);
+ method public void detachAndScrapAttachedViews(androidx.recyclerview.widget.RecyclerView.Recycler);
+ method public void detachAndScrapView(android.view.View, androidx.recyclerview.widget.RecyclerView.Recycler);
+ method public void detachAndScrapViewAt(int, androidx.recyclerview.widget.RecyclerView.Recycler);
+ method public void detachView(android.view.View);
+ method public void detachViewAt(int);
+ method public void endAnimation(android.view.View!);
+ method public android.view.View? findContainingItemView(android.view.View);
+ method public android.view.View? findViewByPosition(int);
+ method public abstract androidx.recyclerview.widget.RecyclerView.LayoutParams! generateDefaultLayoutParams();
+ method public androidx.recyclerview.widget.RecyclerView.LayoutParams! generateLayoutParams(android.view.ViewGroup.LayoutParams!);
+ method public androidx.recyclerview.widget.RecyclerView.LayoutParams! generateLayoutParams(android.content.Context!, android.util.AttributeSet!);
+ method public int getBaseline();
+ method public int getBottomDecorationHeight(android.view.View);
+ method public android.view.View? getChildAt(int);
+ method public int getChildCount();
+ method @Deprecated public static int getChildMeasureSpec(int, int, int, boolean);
+ method public static int getChildMeasureSpec(int, int, int, int, boolean);
+ method public boolean getClipToPadding();
+ method public int getColumnCountForAccessibility(androidx.recyclerview.widget.RecyclerView.Recycler, androidx.recyclerview.widget.RecyclerView.State);
+ method public int getDecoratedBottom(android.view.View);
+ method public void getDecoratedBoundsWithMargins(android.view.View, android.graphics.Rect);
+ method public int getDecoratedLeft(android.view.View);
+ method public int getDecoratedMeasuredHeight(android.view.View);
+ method public int getDecoratedMeasuredWidth(android.view.View);
+ method public int getDecoratedRight(android.view.View);
+ method public int getDecoratedTop(android.view.View);
+ method public android.view.View? getFocusedChild();
+ method @Px public int getHeight();
+ method public int getHeightMode();
+ method public int getItemCount();
+ method public int getItemViewType(android.view.View);
+ method public int getLayoutDirection();
+ method public int getLeftDecorationWidth(android.view.View);
+ method @Px public int getMinimumHeight();
+ method @Px public int getMinimumWidth();
+ method @Px public int getPaddingBottom();
+ method @Px public int getPaddingEnd();
+ method @Px public int getPaddingLeft();
+ method @Px public int getPaddingRight();
+ method @Px public int getPaddingStart();
+ method @Px public int getPaddingTop();
+ method public int getPosition(android.view.View);
+ method public static androidx.recyclerview.widget.RecyclerView.LayoutManager.Properties! getProperties(android.content.Context, android.util.AttributeSet?, int, int);
+ method public int getRightDecorationWidth(android.view.View);
+ method public int getRowCountForAccessibility(androidx.recyclerview.widget.RecyclerView.Recycler, androidx.recyclerview.widget.RecyclerView.State);
+ method public int getSelectionModeForAccessibility(androidx.recyclerview.widget.RecyclerView.Recycler, androidx.recyclerview.widget.RecyclerView.State);
+ method public int getTopDecorationHeight(android.view.View);
+ method public void getTransformedBoundingBox(android.view.View, boolean, android.graphics.Rect);
+ method @Px public int getWidth();
+ method public int getWidthMode();
+ method public boolean hasFocus();
+ method public void ignoreView(android.view.View);
+ method public boolean isAttachedToWindow();
+ method public boolean isAutoMeasureEnabled();
+ method public boolean isFocused();
+ method public final boolean isItemPrefetchEnabled();
+ method public boolean isLayoutHierarchical(androidx.recyclerview.widget.RecyclerView.Recycler, androidx.recyclerview.widget.RecyclerView.State);
+ method public boolean isMeasurementCacheEnabled();
+ method public boolean isSmoothScrolling();
+ method public boolean isViewPartiallyVisible(android.view.View, boolean, boolean);
+ method public void layoutDecorated(android.view.View, int, int, int, int);
+ method public void layoutDecoratedWithMargins(android.view.View, int, int, int, int);
+ method public void measureChild(android.view.View, int, int);
+ method public void measureChildWithMargins(android.view.View, int, int);
+ method public void moveView(int, int);
+ method public void offsetChildrenHorizontal(@Px int);
+ method public void offsetChildrenVertical(@Px int);
+ method public void onAdapterChanged(androidx.recyclerview.widget.RecyclerView.Adapter?, androidx.recyclerview.widget.RecyclerView.Adapter?);
+ method public boolean onAddFocusables(androidx.recyclerview.widget.RecyclerView, java.util.ArrayList<android.view.View!>, int, int);
+ method @CallSuper public void onAttachedToWindow(androidx.recyclerview.widget.RecyclerView!);
+ method @Deprecated public void onDetachedFromWindow(androidx.recyclerview.widget.RecyclerView!);
+ method @CallSuper public void onDetachedFromWindow(androidx.recyclerview.widget.RecyclerView!, androidx.recyclerview.widget.RecyclerView.Recycler!);
+ method public android.view.View? onFocusSearchFailed(android.view.View, int, androidx.recyclerview.widget.RecyclerView.Recycler, androidx.recyclerview.widget.RecyclerView.State);
+ method public void onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
+ method public void onInitializeAccessibilityEvent(androidx.recyclerview.widget.RecyclerView.Recycler, androidx.recyclerview.widget.RecyclerView.State, android.view.accessibility.AccessibilityEvent);
+ method public void onInitializeAccessibilityNodeInfo(androidx.recyclerview.widget.RecyclerView.Recycler, androidx.recyclerview.widget.RecyclerView.State, androidx.core.view.accessibility.AccessibilityNodeInfoCompat);
+ method public void onInitializeAccessibilityNodeInfoForItem(androidx.recyclerview.widget.RecyclerView.Recycler, androidx.recyclerview.widget.RecyclerView.State, android.view.View, androidx.core.view.accessibility.AccessibilityNodeInfoCompat);
+ method public android.view.View? onInterceptFocusSearch(android.view.View, int);
+ method public void onItemsAdded(androidx.recyclerview.widget.RecyclerView, int, int);
+ method public void onItemsChanged(androidx.recyclerview.widget.RecyclerView);
+ method public void onItemsMoved(androidx.recyclerview.widget.RecyclerView, int, int, int);
+ method public void onItemsRemoved(androidx.recyclerview.widget.RecyclerView, int, int);
+ method public void onItemsUpdated(androidx.recyclerview.widget.RecyclerView, int, int);
+ method public void onItemsUpdated(androidx.recyclerview.widget.RecyclerView, int, int, Object?);
+ method public void onLayoutChildren(androidx.recyclerview.widget.RecyclerView.Recycler!, androidx.recyclerview.widget.RecyclerView.State!);
+ method public void onLayoutCompleted(androidx.recyclerview.widget.RecyclerView.State!);
+ method public void onMeasure(androidx.recyclerview.widget.RecyclerView.Recycler, androidx.recyclerview.widget.RecyclerView.State, int, int);
+ method @Deprecated public boolean onRequestChildFocus(androidx.recyclerview.widget.RecyclerView, android.view.View, android.view.View?);
+ method public boolean onRequestChildFocus(androidx.recyclerview.widget.RecyclerView, androidx.recyclerview.widget.RecyclerView.State, android.view.View, android.view.View?);
+ method public void onRestoreInstanceState(android.os.Parcelable!);
+ method public android.os.Parcelable? onSaveInstanceState();
+ method public void onScrollStateChanged(int);
+ method public boolean performAccessibilityAction(androidx.recyclerview.widget.RecyclerView.Recycler, androidx.recyclerview.widget.RecyclerView.State, int, android.os.Bundle?);
+ method public boolean performAccessibilityActionForItem(androidx.recyclerview.widget.RecyclerView.Recycler, androidx.recyclerview.widget.RecyclerView.State, android.view.View, int, android.os.Bundle?);
+ method public void postOnAnimation(Runnable!);
+ method public void removeAllViews();
+ method public void removeAndRecycleAllViews(androidx.recyclerview.widget.RecyclerView.Recycler);
+ method public void removeAndRecycleView(android.view.View, androidx.recyclerview.widget.RecyclerView.Recycler);
+ method public void removeAndRecycleViewAt(int, androidx.recyclerview.widget.RecyclerView.Recycler);
+ method public boolean removeCallbacks(Runnable!);
+ method public void removeDetachedView(android.view.View);
+ method public void removeView(android.view.View!);
+ method public void removeViewAt(int);
+ method public boolean requestChildRectangleOnScreen(androidx.recyclerview.widget.RecyclerView, android.view.View, android.graphics.Rect, boolean);
+ method public boolean requestChildRectangleOnScreen(androidx.recyclerview.widget.RecyclerView, android.view.View, android.graphics.Rect, boolean, boolean);
+ method public void requestLayout();
+ method public void requestSimpleAnimationsInNextLayout();
+ method public int scrollHorizontallyBy(int, androidx.recyclerview.widget.RecyclerView.Recycler!, androidx.recyclerview.widget.RecyclerView.State!);
+ method public void scrollToPosition(int);
+ method public int scrollVerticallyBy(int, androidx.recyclerview.widget.RecyclerView.Recycler!, androidx.recyclerview.widget.RecyclerView.State!);
+ method @Deprecated public void setAutoMeasureEnabled(boolean);
+ method public final void setItemPrefetchEnabled(boolean);
+ method public void setMeasuredDimension(android.graphics.Rect!, int, int);
+ method public void setMeasuredDimension(int, int);
+ method public void setMeasurementCacheEnabled(boolean);
+ method public void smoothScrollToPosition(androidx.recyclerview.widget.RecyclerView!, androidx.recyclerview.widget.RecyclerView.State!, int);
+ method public void startSmoothScroll(androidx.recyclerview.widget.RecyclerView.SmoothScroller!);
+ method public void stopIgnoringView(android.view.View);
+ method public boolean supportsPredictiveItemAnimations();
+ }
+
+ public static interface RecyclerView.LayoutManager.LayoutPrefetchRegistry {
+ method public void addPosition(int, int);
+ }
+
+ public static class RecyclerView.LayoutManager.Properties {
+ ctor public RecyclerView.LayoutManager.Properties();
+ field public int orientation;
+ field public boolean reverseLayout;
+ field public int spanCount;
+ field public boolean stackFromEnd;
+ }
+
+ public static class RecyclerView.LayoutParams extends android.view.ViewGroup.MarginLayoutParams {
+ ctor public RecyclerView.LayoutParams(android.content.Context!, android.util.AttributeSet!);
+ ctor public RecyclerView.LayoutParams(int, int);
+ ctor public RecyclerView.LayoutParams(android.view.ViewGroup.MarginLayoutParams!);
+ ctor public RecyclerView.LayoutParams(android.view.ViewGroup.LayoutParams!);
+ ctor public RecyclerView.LayoutParams(androidx.recyclerview.widget.RecyclerView.LayoutParams!);
+ method public int getViewAdapterPosition();
+ method public int getViewLayoutPosition();
+ method @Deprecated public int getViewPosition();
+ method public boolean isItemChanged();
+ method public boolean isItemRemoved();
+ method public boolean isViewInvalid();
+ method public boolean viewNeedsUpdate();
+ }
+
+ public static interface RecyclerView.OnChildAttachStateChangeListener {
+ method public void onChildViewAttachedToWindow(android.view.View);
+ method public void onChildViewDetachedFromWindow(android.view.View);
+ }
+
+ public abstract static class RecyclerView.OnFlingListener {
+ ctor public RecyclerView.OnFlingListener();
+ method public abstract boolean onFling(int, int);
+ }
+
+ public static interface RecyclerView.OnItemTouchListener {
+ method public boolean onInterceptTouchEvent(androidx.recyclerview.widget.RecyclerView, android.view.MotionEvent);
+ method public void onRequestDisallowInterceptTouchEvent(boolean);
+ method public void onTouchEvent(androidx.recyclerview.widget.RecyclerView, android.view.MotionEvent);
+ }
+
+ public abstract static class RecyclerView.OnScrollListener {
+ ctor public RecyclerView.OnScrollListener();
+ method public void onScrollStateChanged(androidx.recyclerview.widget.RecyclerView, int);
+ method public void onScrolled(androidx.recyclerview.widget.RecyclerView, int, int);
+ }
+
+ public static class RecyclerView.RecycledViewPool {
+ ctor public RecyclerView.RecycledViewPool();
+ method public void clear();
+ method public androidx.recyclerview.widget.RecyclerView.ViewHolder? getRecycledView(int);
+ method public int getRecycledViewCount(int);
+ method public void putRecycledView(androidx.recyclerview.widget.RecyclerView.ViewHolder!);
+ method public void setMaxRecycledViews(int, int);
+ }
+
+ public final class RecyclerView.Recycler {
+ ctor public RecyclerView.Recycler();
+ method public void bindViewToPosition(android.view.View, int);
+ method public void clear();
+ method public int convertPreLayoutPositionToPostLayout(int);
+ method public java.util.List<androidx.recyclerview.widget.RecyclerView.ViewHolder!> getScrapList();
+ method public android.view.View getViewForPosition(int);
+ method public void recycleView(android.view.View);
+ method public void setViewCacheSize(int);
+ }
+
+ public static interface RecyclerView.RecyclerListener {
+ method public void onViewRecycled(androidx.recyclerview.widget.RecyclerView.ViewHolder);
+ }
+
+ public static class RecyclerView.SimpleOnItemTouchListener implements androidx.recyclerview.widget.RecyclerView.OnItemTouchListener {
+ ctor public RecyclerView.SimpleOnItemTouchListener();
+ method public boolean onInterceptTouchEvent(androidx.recyclerview.widget.RecyclerView, android.view.MotionEvent);
+ method public void onRequestDisallowInterceptTouchEvent(boolean);
+ method public void onTouchEvent(androidx.recyclerview.widget.RecyclerView, android.view.MotionEvent);
+ }
+
+ public abstract static class RecyclerView.SmoothScroller {
+ ctor public RecyclerView.SmoothScroller();
+ method public android.graphics.PointF? computeScrollVectorForPosition(int);
+ method public android.view.View! findViewByPosition(int);
+ method public int getChildCount();
+ method public int getChildPosition(android.view.View!);
+ method public androidx.recyclerview.widget.RecyclerView.LayoutManager? getLayoutManager();
+ method public int getTargetPosition();
+ method @Deprecated public void instantScrollToPosition(int);
+ method public boolean isPendingInitialRun();
+ method public boolean isRunning();
+ method protected void normalize(android.graphics.PointF);
+ method protected void onChildAttachedToWindow(android.view.View!);
+ method protected abstract void onSeekTargetStep(@Px int, @Px int, androidx.recyclerview.widget.RecyclerView.State, androidx.recyclerview.widget.RecyclerView.SmoothScroller.Action);
+ method protected abstract void onStart();
+ method protected abstract void onStop();
+ method protected abstract void onTargetFound(android.view.View, androidx.recyclerview.widget.RecyclerView.State, androidx.recyclerview.widget.RecyclerView.SmoothScroller.Action);
+ method public void setTargetPosition(int);
+ method protected final void stop();
+ }
+
+ public static class RecyclerView.SmoothScroller.Action {
+ ctor public RecyclerView.SmoothScroller.Action(@Px int, @Px int);
+ ctor public RecyclerView.SmoothScroller.Action(@Px int, @Px int, int);
+ ctor public RecyclerView.SmoothScroller.Action(@Px int, @Px int, int, android.view.animation.Interpolator?);
+ method public int getDuration();
+ method @Px public int getDx();
+ method @Px public int getDy();
+ method public android.view.animation.Interpolator? getInterpolator();
+ method public void jumpTo(int);
+ method public void setDuration(int);
+ method public void setDx(@Px int);
+ method public void setDy(@Px int);
+ method public void setInterpolator(android.view.animation.Interpolator?);
+ method public void update(@Px int, @Px int, int, android.view.animation.Interpolator?);
+ field public static final int UNDEFINED_DURATION = -2147483648; // 0x80000000
+ }
+
+ public static interface RecyclerView.SmoothScroller.ScrollVectorProvider {
+ method public android.graphics.PointF? computeScrollVectorForPosition(int);
+ }
+
+ public static class RecyclerView.State {
+ ctor public RecyclerView.State();
+ method public boolean didStructureChange();
+ method public <T> T! get(int);
+ method public int getItemCount();
+ method public int getRemainingScrollHorizontal();
+ method public int getRemainingScrollVertical();
+ method public int getTargetScrollPosition();
+ method public boolean hasTargetScrollPosition();
+ method public boolean isMeasuring();
+ method public boolean isPreLayout();
+ method public void put(int, Object!);
+ method public void remove(int);
+ method public boolean willRunPredictiveAnimations();
+ method public boolean willRunSimpleAnimations();
+ }
+
+ public abstract static class RecyclerView.ViewCacheExtension {
+ ctor public RecyclerView.ViewCacheExtension();
+ method public abstract android.view.View? getViewForPositionAndType(androidx.recyclerview.widget.RecyclerView.Recycler, int, int);
+ }
+
+ public abstract static class RecyclerView.ViewHolder {
+ ctor public RecyclerView.ViewHolder(android.view.View);
+ method public final int getAdapterPosition();
+ method public final long getItemId();
+ method public final int getItemViewType();
+ method public final int getLayoutPosition();
+ method public final int getOldPosition();
+ method @Deprecated public final int getPosition();
+ method public final boolean isRecyclable();
+ method public final void setIsRecyclable(boolean);
+ field public final android.view.View itemView;
+ }
+
+ public class RecyclerViewAccessibilityDelegate extends androidx.core.view.AccessibilityDelegateCompat {
+ ctor public RecyclerViewAccessibilityDelegate(androidx.recyclerview.widget.RecyclerView);
+ method public androidx.core.view.AccessibilityDelegateCompat getItemDelegate();
+ }
+
+ public static class RecyclerViewAccessibilityDelegate.ItemDelegate extends androidx.core.view.AccessibilityDelegateCompat {
+ ctor public RecyclerViewAccessibilityDelegate.ItemDelegate(androidx.recyclerview.widget.RecyclerViewAccessibilityDelegate);
+ }
+
+ public abstract class SimpleItemAnimator extends androidx.recyclerview.widget.RecyclerView.ItemAnimator {
+ ctor public SimpleItemAnimator();
+ method public abstract boolean animateAdd(androidx.recyclerview.widget.RecyclerView.ViewHolder!);
+ method public boolean animateAppearance(androidx.recyclerview.widget.RecyclerView.ViewHolder, androidx.recyclerview.widget.RecyclerView.ItemAnimator.ItemHolderInfo?, androidx.recyclerview.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+ method public boolean animateChange(androidx.recyclerview.widget.RecyclerView.ViewHolder, androidx.recyclerview.widget.RecyclerView.ViewHolder, androidx.recyclerview.widget.RecyclerView.ItemAnimator.ItemHolderInfo, androidx.recyclerview.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+ method public abstract boolean animateChange(androidx.recyclerview.widget.RecyclerView.ViewHolder!, androidx.recyclerview.widget.RecyclerView.ViewHolder!, int, int, int, int);
+ method public boolean animateDisappearance(androidx.recyclerview.widget.RecyclerView.ViewHolder, androidx.recyclerview.widget.RecyclerView.ItemAnimator.ItemHolderInfo, androidx.recyclerview.widget.RecyclerView.ItemAnimator.ItemHolderInfo?);
+ method public abstract boolean animateMove(androidx.recyclerview.widget.RecyclerView.ViewHolder!, int, int, int, int);
+ method public boolean animatePersistence(androidx.recyclerview.widget.RecyclerView.ViewHolder, androidx.recyclerview.widget.RecyclerView.ItemAnimator.ItemHolderInfo, androidx.recyclerview.widget.RecyclerView.ItemAnimator.ItemHolderInfo);
+ method public abstract boolean animateRemove(androidx.recyclerview.widget.RecyclerView.ViewHolder!);
+ method public final void dispatchAddFinished(androidx.recyclerview.widget.RecyclerView.ViewHolder!);
+ method public final void dispatchAddStarting(androidx.recyclerview.widget.RecyclerView.ViewHolder!);
+ method public final void dispatchChangeFinished(androidx.recyclerview.widget.RecyclerView.ViewHolder!, boolean);
+ method public final void dispatchChangeStarting(androidx.recyclerview.widget.RecyclerView.ViewHolder!, boolean);
+ method public final void dispatchMoveFinished(androidx.recyclerview.widget.RecyclerView.ViewHolder!);
+ method public final void dispatchMoveStarting(androidx.recyclerview.widget.RecyclerView.ViewHolder!);
+ method public final void dispatchRemoveFinished(androidx.recyclerview.widget.RecyclerView.ViewHolder!);
+ method public final void dispatchRemoveStarting(androidx.recyclerview.widget.RecyclerView.ViewHolder!);
+ method public boolean getSupportsChangeAnimations();
+ method public void onAddFinished(androidx.recyclerview.widget.RecyclerView.ViewHolder!);
+ method public void onAddStarting(androidx.recyclerview.widget.RecyclerView.ViewHolder!);
+ method public void onChangeFinished(androidx.recyclerview.widget.RecyclerView.ViewHolder!, boolean);
+ method public void onChangeStarting(androidx.recyclerview.widget.RecyclerView.ViewHolder!, boolean);
+ method public void onMoveFinished(androidx.recyclerview.widget.RecyclerView.ViewHolder!);
+ method public void onMoveStarting(androidx.recyclerview.widget.RecyclerView.ViewHolder!);
+ method public void onRemoveFinished(androidx.recyclerview.widget.RecyclerView.ViewHolder!);
+ method public void onRemoveStarting(androidx.recyclerview.widget.RecyclerView.ViewHolder!);
+ method public void setSupportsChangeAnimations(boolean);
+ }
+
+ public abstract class SnapHelper extends androidx.recyclerview.widget.RecyclerView.OnFlingListener {
+ ctor public SnapHelper();
+ method public void attachToRecyclerView(androidx.recyclerview.widget.RecyclerView?) throws java.lang.IllegalStateException;
+ method public abstract int[]? calculateDistanceToFinalSnap(androidx.recyclerview.widget.RecyclerView.LayoutManager, android.view.View);
+ method public int[]! calculateScrollDistance(int, int);
+ method protected androidx.recyclerview.widget.RecyclerView.SmoothScroller? createScroller(androidx.recyclerview.widget.RecyclerView.LayoutManager!);
+ method @Deprecated protected androidx.recyclerview.widget.LinearSmoothScroller? createSnapScroller(androidx.recyclerview.widget.RecyclerView.LayoutManager!);
+ method public abstract android.view.View? findSnapView(androidx.recyclerview.widget.RecyclerView.LayoutManager!);
+ method public abstract int findTargetSnapPosition(androidx.recyclerview.widget.RecyclerView.LayoutManager!, int, int);
+ method public boolean onFling(int, int);
+ }
+
+ public class SortedList<T> {
+ ctor public SortedList(Class<T!>, androidx.recyclerview.widget.SortedList.Callback<T!>);
+ ctor public SortedList(Class<T!>, androidx.recyclerview.widget.SortedList.Callback<T!>, int);
+ method public int add(T!);
+ method public void addAll(T![], boolean);
+ method public void addAll(T!...);
+ method public void addAll(java.util.Collection<T!>);
+ method public void beginBatchedUpdates();
+ method public void clear();
+ method public void endBatchedUpdates();
+ method public T! get(int) throws java.lang.IndexOutOfBoundsException;
+ method public int indexOf(T!);
+ method public void recalculatePositionOfItemAt(int);
+ method public boolean remove(T!);
+ method public T! removeItemAt(int);
+ method public void replaceAll(T![], boolean);
+ method public void replaceAll(T!...);
+ method public void replaceAll(java.util.Collection<T!>);
+ method public int size();
+ method public void updateItemAt(int, T!);
+ field public static final int INVALID_POSITION = -1; // 0xffffffff
+ }
+
+ public static class SortedList.BatchedCallback<T2> extends androidx.recyclerview.widget.SortedList.Callback<T2> {
+ ctor public SortedList.BatchedCallback(androidx.recyclerview.widget.SortedList.Callback<T2!>!);
+ method public boolean areContentsTheSame(T2!, T2!);
+ method public boolean areItemsTheSame(T2!, T2!);
+ method public int compare(T2!, T2!);
+ method public void dispatchLastEvent();
+ method public void onChanged(int, int);
+ method public void onInserted(int, int);
+ method public void onMoved(int, int);
+ method public void onRemoved(int, int);
+ }
+
+ public abstract static class SortedList.Callback<T2> implements java.util.Comparator<T2> androidx.recyclerview.widget.ListUpdateCallback {
+ ctor public SortedList.Callback();
+ method public abstract boolean areContentsTheSame(T2!, T2!);
+ method public abstract boolean areItemsTheSame(T2!, T2!);
+ method public abstract int compare(T2!, T2!);
+ method public Object? getChangePayload(T2!, T2!);
+ method public abstract void onChanged(int, int);
+ method public void onChanged(int, int, Object!);
+ }
+
+ public abstract class SortedListAdapterCallback<T2> extends androidx.recyclerview.widget.SortedList.Callback<T2> {
+ ctor public SortedListAdapterCallback(androidx.recyclerview.widget.RecyclerView.Adapter!);
+ method public void onChanged(int, int);
+ method public void onInserted(int, int);
+ method public void onMoved(int, int);
+ method public void onRemoved(int, int);
+ }
+
+ public class StaggeredGridLayoutManager extends androidx.recyclerview.widget.RecyclerView.LayoutManager implements androidx.recyclerview.widget.RecyclerView.SmoothScroller.ScrollVectorProvider {
+ ctor public StaggeredGridLayoutManager(android.content.Context!, android.util.AttributeSet!, int, int);
+ ctor public StaggeredGridLayoutManager(int, int);
+ method public android.graphics.PointF! computeScrollVectorForPosition(int);
+ method public int[]! findFirstCompletelyVisibleItemPositions(int[]!);
+ method public int[]! findFirstVisibleItemPositions(int[]!);
+ method public int[]! findLastCompletelyVisibleItemPositions(int[]!);
+ method public int[]! findLastVisibleItemPositions(int[]!);
+ method public androidx.recyclerview.widget.RecyclerView.LayoutParams! generateDefaultLayoutParams();
+ method public int getGapStrategy();
+ method public int getOrientation();
+ method public boolean getReverseLayout();
+ method public int getSpanCount();
+ method public void invalidateSpanAssignments();
+ method public void scrollToPositionWithOffset(int, int);
+ method public void setGapStrategy(int);
+ method public void setOrientation(int);
+ method public void setReverseLayout(boolean);
+ method public void setSpanCount(int);
+ field @Deprecated public static final int GAP_HANDLING_LAZY = 1; // 0x1
+ field public static final int GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS = 2; // 0x2
+ field public static final int GAP_HANDLING_NONE = 0; // 0x0
+ field public static final int HORIZONTAL = 0; // 0x0
+ field public static final int VERTICAL = 1; // 0x1
+ }
+
+ public static class StaggeredGridLayoutManager.LayoutParams extends androidx.recyclerview.widget.RecyclerView.LayoutParams {
+ ctor public StaggeredGridLayoutManager.LayoutParams(android.content.Context!, android.util.AttributeSet!);
+ ctor public StaggeredGridLayoutManager.LayoutParams(int, int);
+ ctor public StaggeredGridLayoutManager.LayoutParams(android.view.ViewGroup.MarginLayoutParams!);
+ ctor public StaggeredGridLayoutManager.LayoutParams(android.view.ViewGroup.LayoutParams!);
+ ctor public StaggeredGridLayoutManager.LayoutParams(androidx.recyclerview.widget.RecyclerView.LayoutParams!);
+ method public final int getSpanIndex();
+ method public boolean isFullSpan();
+ method public void setFullSpan(boolean);
+ field public static final int INVALID_SPAN_ID = -1; // 0xffffffff
+ }
+
+}
+
diff --git a/recyclerview/recyclerview/api/api_lint.ignore b/recyclerview/recyclerview/api/api_lint.ignore
new file mode 100644
index 0000000..7aa0720a
--- /dev/null
+++ b/recyclerview/recyclerview/api/api_lint.ignore
@@ -0,0 +1,61 @@
+// Baseline format: 1.0
+AcronymName: androidx.recyclerview.widget.ItemTouchHelper.Callback#getDefaultUIUtil():
+ Acronyms should not be capitalized in method names: was `getDefaultUIUtil`, should this be `getDefaultUiUtil`?
+AcronymName: androidx.recyclerview.widget.ItemTouchUIUtil:
+ Acronyms should not be capitalized in class names: was `ItemTouchUIUtil`, should this be `ItemTouchUiUtil`?
+AcronymName: androidx.recyclerview.widget.LinearLayoutManager#isLayoutRTL():
+ Acronyms should not be capitalized in method names: was `isLayoutRTL`, should this be `isLayoutRtl`?
+
+
+BannedThrow: androidx.recyclerview.widget.SnapHelper#attachToRecyclerView(androidx.recyclerview.widget.RecyclerView):
+ Methods must not mention RuntimeException subclasses in throws clauses (was `java.lang.IllegalStateException`)
+BannedThrow: androidx.recyclerview.widget.SortedList#get(int):
+ Methods must not mention RuntimeException subclasses in throws clauses (was `java.lang.IndexOutOfBoundsException`)
+
+
+CallbackMethodName: androidx.recyclerview.widget.AsyncListUtil.DataCallback:
+ Callback method names must follow the on<Something> style: refreshData
+CallbackMethodName: androidx.recyclerview.widget.AsyncListUtil.ViewCallback:
+ Callback method names must follow the on<Something> style: getItemRangeInto
+CallbackMethodName: androidx.recyclerview.widget.BatchingListUpdateCallback:
+ Callback method names must follow the on<Something> style: dispatchLastEvent
+CallbackMethodName: androidx.recyclerview.widget.DiffUtil.Callback:
+ Callback method names must follow the on<Something> style: getOldListSize
+CallbackMethodName: androidx.recyclerview.widget.DiffUtil.ItemCallback:
+ Callback method names must follow the on<Something> style: areItemsTheSame
+CallbackMethodName: androidx.recyclerview.widget.ItemTouchHelper.Callback:
+ Callback method names must follow the on<Something> style: getDefaultUIUtil
+CallbackMethodName: androidx.recyclerview.widget.ItemTouchHelper.SimpleCallback:
+ Callback method names must follow the on<Something> style: setDefaultSwipeDirs
+CallbackMethodName: androidx.recyclerview.widget.SortedList.BatchedCallback:
+ Callback method names must follow the on<Something> style: compare
+CallbackMethodName: androidx.recyclerview.widget.SortedList.Callback:
+ Callback method names must follow the on<Something> style: compare
+
+
+CallbackName: androidx.recyclerview.widget.RecyclerView.AdapterDataObserver:
+ Class should be named AdapterDataCallback
+
+
+ConcreteCollection: androidx.recyclerview.widget.RecyclerView#addFocusables(java.util.ArrayList<android.view.View>, int, int) parameter #0:
+ Parameter type is concrete collection (`java.util.ArrayList`); must be higher-level interface
+ConcreteCollection: androidx.recyclerview.widget.RecyclerView.LayoutManager#onAddFocusables(androidx.recyclerview.widget.RecyclerView, java.util.ArrayList<android.view.View>, int, int) parameter #1:
+ Parameter type is concrete collection (`java.util.ArrayList`); must be higher-level interface
+
+
+KotlinOperator: androidx.recyclerview.widget.RecyclerView.State#get(int):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.recyclerview.widget.SortedList#get(int):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+
+
+ListenerInterface: androidx.recyclerview.widget.RecyclerView.OnFlingListener:
+ Listeners should be an interface, or otherwise renamed Callback: OnFlingListener
+ListenerInterface: androidx.recyclerview.widget.RecyclerView.OnScrollListener:
+ Listeners should be an interface, or otherwise renamed Callback: OnScrollListener
+ListenerInterface: androidx.recyclerview.widget.RecyclerView.SimpleOnItemTouchListener:
+ Listeners should be an interface, or otherwise renamed Callback: SimpleOnItemTouchListener
+
+
+MethodNameUnits: androidx.recyclerview.widget.OrientationHelper#getTotalSpace():
+ Expected method name units to be `Bytes`, was `Space` in `getTotalSpace`
diff --git a/recyclerview/recyclerview/api/res-1.1.0-beta01.txt b/recyclerview/recyclerview/api/res-1.1.0-beta01.txt
new file mode 100644
index 0000000..475bfc43
--- /dev/null
+++ b/recyclerview/recyclerview/api/res-1.1.0-beta01.txt
@@ -0,0 +1,9 @@
+attr fastScrollEnabled
+attr fastScrollHorizontalThumbDrawable
+attr fastScrollHorizontalTrackDrawable
+attr fastScrollVerticalThumbDrawable
+attr fastScrollVerticalTrackDrawable
+attr layoutManager
+attr reverseLayout
+attr spanCount
+attr stackFromEnd
diff --git a/recyclerview/recyclerview/api/restricted_1.1.0-beta01.ignore b/recyclerview/recyclerview/api/restricted_1.1.0-beta01.ignore
new file mode 100644
index 0000000..524bce1
--- /dev/null
+++ b/recyclerview/recyclerview/api/restricted_1.1.0-beta01.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+InvalidNullConversion: androidx.recyclerview.widget.AsyncDifferConfig#getMainThreadExecutor():
+ Attempted to change method return from @NonNull to @Nullable: incompatible change for method androidx.recyclerview.widget.AsyncDifferConfig.getMainThreadExecutor()
diff --git a/recyclerview/recyclerview/api/restricted_1.1.0-beta01.txt b/recyclerview/recyclerview/api/restricted_1.1.0-beta01.txt
new file mode 100644
index 0000000..c8a8463
--- /dev/null
+++ b/recyclerview/recyclerview/api/restricted_1.1.0-beta01.txt
@@ -0,0 +1,17 @@
+// Signature format: 3.0
+package androidx.recyclerview.widget {
+
+ public final class AsyncDifferConfig<T> {
+ }
+
+ public static final class AsyncDifferConfig.Builder<T> {
+ }
+
+
+ @IntDef({androidx.recyclerview.widget.RecyclerView.HORIZONTAL, androidx.recyclerview.widget.RecyclerView.VERTICAL}) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface RecyclerView.Orientation {
+ }
+
+
+
+}
+
diff --git a/recyclerview/recyclerview/build.gradle b/recyclerview/recyclerview/build.gradle
index 7fe7a61..16d7734 100644
--- a/recyclerview/recyclerview/build.gradle
+++ b/recyclerview/recyclerview/build.gradle
@@ -11,7 +11,7 @@
dependencies {
api("androidx.annotation:annotation:1.1.0")
- api(project(":core:core"))
+ api("androidx.core:core:1.1.0-rc02")
implementation("androidx.collection:collection:1.0.0")
api("androidx.customview:customview:1.0.0")
@@ -32,6 +32,10 @@
}
android {
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_1_7
+ targetCompatibility = JavaVersion.VERSION_1_7
+ }
sourceSets {
main.res.srcDirs "res", "res-public"
}
diff --git a/recyclerview/selection/api/api_lint.ignore b/recyclerview/selection/api/api_lint.ignore
new file mode 100644
index 0000000..1940c27
--- /dev/null
+++ b/recyclerview/selection/api/api_lint.ignore
@@ -0,0 +1,17 @@
+// Baseline format: 1.0
+AutoBoxing: androidx.recyclerview.selection.StableIdKeyProvider#getKey(int):
+ Must avoid boxed primitives (`java.lang.Long`)
+AutoBoxing: androidx.recyclerview.selection.StableIdKeyProvider#getPosition(Long) parameter #0:
+ Must avoid boxed primitives (`java.lang.Long`)
+
+
+CallbackName: androidx.recyclerview.selection.SelectionTracker.SelectionObserver:
+ Class should be named SelectionCallback
+
+
+KotlinOperator: androidx.recyclerview.selection.Selection#contains(K):
+ Method can be invoked as a "in" operator from Kotlin: `contains` (this is usually desirable; just make sure it makes sense for this type of object)
+
+
+VisiblySynchronized: androidx.recyclerview.selection.OperationMonitor#isStarted():
+ Internal locks must not be exposed: method androidx.recyclerview.selection.OperationMonitor.isStarted()
diff --git a/recyclerview/selection/build.gradle b/recyclerview/selection/build.gradle
index 17b00dc..85b293d 100644
--- a/recyclerview/selection/build.gradle
+++ b/recyclerview/selection/build.gradle
@@ -42,7 +42,7 @@
androidx {
name = "Android RecyclerView Selection"
publish = Publish.SNAPSHOT_AND_RELEASE
- mavenVersion = LibraryVersions.RECYCLERVIEW
+ mavenVersion = LibraryVersions.RECYCLERVIEW_SELECTION
mavenGroup = LibraryGroups.RECYCLERVIEW
inceptionYear = "2017"
description = "Library providing item selection framework for RecyclerView. Support for touch based and band selection is provided."
diff --git a/remotecallback/api/api_lint.ignore b/remotecallback/api/api_lint.ignore
new file mode 100644
index 0000000..9d77312
--- /dev/null
+++ b/remotecallback/api/api_lint.ignore
@@ -0,0 +1,23 @@
+// Baseline format: 1.0
+CallbackMethodName: androidx.remotecallback.RemoteCallback:
+ Callback method names must follow the on<Something> style: getType
+
+
+ContextFirst: androidx.remotecallback.RemoteCallback#create(Class<T>, android.content.Context) parameter #1:
+ Context is distinct, so it must be the first argument (method `create`)
+
+
+ContextNameSuffix: androidx.remotecallback.AppWidgetProviderWithCallbacks:
+ Inconsistent class name; should be `<Foo>Receiver`, was `AppWidgetProviderWithCallbacks`
+ContextNameSuffix: androidx.remotecallback.BroadcastReceiverWithCallbacks:
+ Inconsistent class name; should be `<Foo>Receiver`, was `BroadcastReceiverWithCallbacks`
+ContextNameSuffix: androidx.remotecallback.ContentProviderWithCallbacks:
+ Inconsistent class name; should be `<Foo>Provider`, was `ContentProviderWithCallbacks`
+
+
+SingularCallback: androidx.remotecallback.AppWidgetProviderWithCallbacks:
+ Callback class names should be singular: AppWidgetProviderWithCallbacks
+SingularCallback: androidx.remotecallback.BroadcastReceiverWithCallbacks:
+ Callback class names should be singular: BroadcastReceiverWithCallbacks
+SingularCallback: androidx.remotecallback.ContentProviderWithCallbacks:
+ Callback class names should be singular: ContentProviderWithCallbacks
diff --git a/room/common/api/api_lint.ignore b/room/common/api/api_lint.ignore
new file mode 100644
index 0000000..d34c89c2
--- /dev/null
+++ b/room/common/api/api_lint.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+AcronymName: androidx.room.ColumnInfo.SQLiteTypeAffinity:
+ Acronyms should not be capitalized in class names: was `SQLiteTypeAffinity`, should this be `SqLiteTypeAffinity`?
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/PrepackageTest.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/PrepackageTest.java
index 4d14618..63b017a 100644
--- a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/PrepackageTest.java
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/PrepackageTest.java
@@ -35,8 +35,7 @@
import androidx.sqlite.db.SupportSQLiteDatabase;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.FlakyTest;
-import androidx.test.filters.SmallTest;
+import androidx.test.filters.MediumTest;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -48,8 +47,7 @@
import java.io.InputStream;
import java.io.OutputStream;
-@SmallTest
-@FlakyTest
+@MediumTest
@RunWith(AndroidJUnit4.class)
public class PrepackageTest {
@@ -64,6 +62,8 @@
ProductDao dao = database.getProductDao();
assertThat(dao.countProducts(), is(2));
+
+ database.close();
}
@Test
@@ -85,6 +85,8 @@
assertThat(throwable, instanceOf(IllegalStateException.class));
assertThat(throwable.getMessage(),
containsString("Pre-packaged database has an invalid schema"));
+
+ database.close();
}
@Test
@@ -105,6 +107,8 @@
}
assertThat(throwable, instanceOf(RuntimeException.class));
assertThat(throwable.getCause(), instanceOf(FileNotFoundException.class));
+
+ database.close();
}
@Test
@@ -121,6 +125,8 @@
ProductDao dao = database.getProductDao();
assertThat(dao.countProducts(), is(2));
+
+ database.close();
}
@Test
@@ -142,6 +148,8 @@
assertThat(throwable, instanceOf(IllegalStateException.class));
assertThat(throwable.getMessage(),
containsString("Pre-packaged database has an invalid schema"));
+
+ database.close();
}
@Test
@@ -168,6 +176,8 @@
.build();
dao = database.getProductDao();
assertThat(dao.countProducts(), is(3));
+
+ database.close();
}
@Test
@@ -183,6 +193,8 @@
ProductDao dao = database.getProductDao();
assertThat(dao.countProducts(), is(0));
+
+ database.close();
}
@Test
@@ -204,6 +216,8 @@
ProductDao dao = database.getProductDao();
assertThat(dao.countProducts(), is(3));
assertThat(dao.getProductById(3).name, is("Mofongo"));
+
+ database.close();
}
@Test
@@ -218,6 +232,8 @@
ProductDao dao = database.getProductDao();
assertThat(dao.countProducts(), is(0));
+
+ database.close();
}
@Test
@@ -242,6 +258,8 @@
.build();
dao = database_v2.getProductDao();
assertThat(dao.countProducts(), is(3));
+
+ database_v2.close();
}
@Test
@@ -266,6 +284,8 @@
.build();
dao = database_v2.getProductDao();
assertThat(dao.countProducts(), is(0));
+
+ database_v2.close();
}
@Test
@@ -298,6 +318,46 @@
dao = database_v2.getProductDao();
assertThat(dao.countProducts(), is(3));
assertThat(dao.getProductById(3).name, is("Mofongo"));
+
+ database_v2.close();
+ }
+
+ @Test
+ public void createFromAssert_multiInstanceCopy() throws InterruptedException {
+ Context context = ApplicationProvider.getApplicationContext();
+ context.deleteDatabase("products.db");
+
+ ProductsDatabase database1 = Room.databaseBuilder(
+ context, ProductsDatabase.class, "products.db")
+ .createFromAsset("databases/products_big.db")
+ .build();
+
+ ProductsDatabase database2 = Room.databaseBuilder(
+ context, ProductsDatabase.class, "products.db")
+ .createFromAsset("databases/products_big.db")
+ .build();
+
+ Thread t1 = new Thread("DB Thread A") {
+ @Override
+ public void run() {
+ database1.getProductDao().countProducts();
+ }
+ };
+ Thread t2 = new Thread("DB Thread B") {
+ @Override
+ public void run() {
+ database2.getProductDao().countProducts();
+ }
+ };
+
+ t1.start();
+ t2.start();
+
+ t1.join();
+ t2.join();
+
+ database1.close();
+ database2.close();
}
@Test
@@ -317,6 +377,8 @@
ProductDao dao = database.getProductDao();
assertThat(dao.countProducts(), is(2));
+
+ database.close();
}
@Test
@@ -349,6 +411,8 @@
.build();
dao = database_v2.getProductDao();
assertThat(dao.countProducts(), is(0));
+
+ database_v2.close();
}
@Test
@@ -367,6 +431,8 @@
ProductDao dao = database.getProductDao();
assertThat(dao.countProducts(), is(2));
+
+ database.close();
}
@Test
@@ -393,6 +459,8 @@
assertThat(throwable, instanceOf(IllegalStateException.class));
assertThat(throwable.getMessage(),
containsString("Pre-packaged database has an invalid schema"));
+
+ database.close();
}
@Test
@@ -411,6 +479,8 @@
ProductDao dao = database.getProductDao();
assertThat(dao.countProducts(), is(2));
+
+ database.close();
}
@Test
@@ -437,6 +507,8 @@
assertThat(throwable, instanceOf(IllegalStateException.class));
assertThat(throwable.getMessage(),
containsString("Pre-packaged database has an invalid schema"));
+
+ database.close();
}
@Database(entities = Product.class, version = 1, exportSchema = false)
diff --git a/room/integration-tests/testapp/src/main/assets/databases/products_big.db b/room/integration-tests/testapp/src/main/assets/databases/products_big.db
new file mode 100644
index 0000000..e87a5e0
--- /dev/null
+++ b/room/integration-tests/testapp/src/main/assets/databases/products_big.db
Binary files differ
diff --git a/room/runtime/api/api_lint.ignore b/room/runtime/api/api_lint.ignore
new file mode 100644
index 0000000..d491809
--- /dev/null
+++ b/room/runtime/api/api_lint.ignore
@@ -0,0 +1,7 @@
+// Baseline format: 1.0
+CallbackName: androidx.room.InvalidationTracker.Observer:
+ Class should be named Callback
+
+
+RegistrationName: androidx.room.RoomDatabase.Builder#addCallback(androidx.room.RoomDatabase.Callback):
+ Callback methods should be named register/unregister; was addCallback
diff --git a/room/runtime/api/restricted_2.2.0-alpha01.txt b/room/runtime/api/restricted_2.2.0-alpha01.txt
index 5b983bd..0f8eb25 100644
--- a/room/runtime/api/restricted_2.2.0-alpha01.txt
+++ b/room/runtime/api/restricted_2.2.0-alpha01.txt
@@ -117,6 +117,12 @@
package androidx.room.util {
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class CopyLock {
+ ctor public CopyLock(String, java.io.File, boolean);
+ method public void lock();
+ method public void unlock();
+ }
+
@RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class CursorUtil {
method public static android.database.Cursor copyAndClose(android.database.Cursor);
method public static int getColumnIndex(android.database.Cursor, String);
@@ -129,6 +135,10 @@
method public static int readVersion(java.io.File) throws java.io.IOException;
}
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class FileUtil {
+ method public static void copy(java.nio.channels.ReadableByteChannel, java.nio.channels.FileChannel) throws java.io.IOException;
+ }
+
@RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class FtsTableInfo {
ctor public FtsTableInfo(String!, java.util.Set<java.lang.String!>!, java.util.Set<java.lang.String!>!);
ctor public FtsTableInfo(String!, java.util.Set<java.lang.String!>!, String!);
diff --git a/room/runtime/api/restricted_current.txt b/room/runtime/api/restricted_current.txt
index 5b983bd..0f8eb25 100644
--- a/room/runtime/api/restricted_current.txt
+++ b/room/runtime/api/restricted_current.txt
@@ -117,6 +117,12 @@
package androidx.room.util {
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class CopyLock {
+ ctor public CopyLock(String, java.io.File, boolean);
+ method public void lock();
+ method public void unlock();
+ }
+
@RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class CursorUtil {
method public static android.database.Cursor copyAndClose(android.database.Cursor);
method public static int getColumnIndex(android.database.Cursor, String);
@@ -129,6 +135,10 @@
method public static int readVersion(java.io.File) throws java.io.IOException;
}
+ @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class FileUtil {
+ method public static void copy(java.nio.channels.ReadableByteChannel, java.nio.channels.FileChannel) throws java.io.IOException;
+ }
+
@RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class FtsTableInfo {
ctor public FtsTableInfo(String!, java.util.Set<java.lang.String!>!, java.util.Set<java.lang.String!>!);
ctor public FtsTableInfo(String!, java.util.Set<java.lang.String!>!, String!);
diff --git a/room/runtime/src/main/java/androidx/room/RoomDatabase.java b/room/runtime/src/main/java/androidx/room/RoomDatabase.java
index 8627873..d5e0f98 100644
--- a/room/runtime/src/main/java/androidx/room/RoomDatabase.java
+++ b/room/runtime/src/main/java/androidx/room/RoomDatabase.java
@@ -569,7 +569,7 @@
* pre-packaged database schema utilizing the exported schema files generated when
* {@link Database#exportSchema()} is enabled.
* <p>
- * This method is not valid if this {@link Builder} is for an in memory database.
+ * This method is not supported for an in memory database {@link Builder}.
*
* @param databaseFilePath The file path within the 'assets/' directory of where the
* database file is located.
@@ -593,7 +593,7 @@
* pre-packaged database schema utilizing the exported schema files generated when
* {@link Database#exportSchema()} is enabled.
* <p>
- * This method is not valid if this {@link Builder} is for an in memory database.
+ * This method is not supported for an in memory database {@link Builder}.
*
* @param databaseFile The database file.
*
diff --git a/room/runtime/src/main/java/androidx/room/SQLiteCopyOpenHelper.java b/room/runtime/src/main/java/androidx/room/SQLiteCopyOpenHelper.java
index 038046e..eefdde4 100644
--- a/room/runtime/src/main/java/androidx/room/SQLiteCopyOpenHelper.java
+++ b/room/runtime/src/main/java/androidx/room/SQLiteCopyOpenHelper.java
@@ -23,7 +23,9 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
+import androidx.room.util.CopyLock;
import androidx.room.util.DBUtil;
+import androidx.room.util.FileUtil;
import androidx.sqlite.db.SupportSQLiteDatabase;
import androidx.sqlite.db.SupportSQLiteOpenHelper;
@@ -31,8 +33,9 @@
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
+import java.nio.channels.Channels;
+import java.nio.channels.FileChannel;
+import java.nio.channels.ReadableByteChannel;
/**
* An open helper that will copy & open a pre-populated database if it doesn't exists in internal
@@ -111,76 +114,90 @@
private void verifyDatabaseFile() {
String databaseName = getDatabaseName();
File databaseFile = mContext.getDatabasePath(databaseName);
- if (!databaseFile.exists()) {
- try {
- copyDatabaseFile(databaseFile);
- return;
- } catch (IOException e) {
- throw new RuntimeException("Unable to copy database file.", e);
- }
- }
-
- if (mDatabaseConfiguration == null) {
- return;
- }
-
- // A database file is present, check if we need to re-copy it.
- int currentVersion;
+ boolean processLevelLock = mDatabaseConfiguration == null
+ || mDatabaseConfiguration.multiInstanceInvalidation;
+ CopyLock copyLock = new CopyLock(databaseName, mContext.getFilesDir(), processLevelLock);
try {
- currentVersion = DBUtil.readVersion(databaseFile);
- } catch (IOException e) {
- Log.w(Room.LOG_TAG, "Unable to read database version.", e);
- return;
- }
+ // Acquire a copy lock, this lock works across threads and processes, preventing
+ // concurrent copy attempts from occurring.
+ copyLock.lock();
- if (currentVersion == mDatabaseVersion) {
- return;
- }
-
- if (mDatabaseConfiguration.isMigrationRequired(currentVersion, mDatabaseVersion)) {
- return;
- }
-
- if (mContext.deleteDatabase(databaseName)) {
- try {
- copyDatabaseFile(databaseFile);
- } catch (IOException e) {
- // We are more forgiving copying a database on a destructive migration since there
- // is already a database file that can be opened.
- Log.w(Room.LOG_TAG, "Unable to copy database file.", e);
+ if (!databaseFile.exists()) {
+ try {
+ // No database file found, copy and be done.
+ copyDatabaseFile(databaseFile);
+ return;
+ } catch (IOException e) {
+ throw new RuntimeException("Unable to copy database file.", e);
+ }
}
+
+ if (mDatabaseConfiguration == null) {
+ return;
+ }
+
+ // A database file is present, check if we need to re-copy it.
+ int currentVersion;
+ try {
+ currentVersion = DBUtil.readVersion(databaseFile);
+ } catch (IOException e) {
+ Log.w(Room.LOG_TAG, "Unable to read database version.", e);
+ return;
+ }
+
+ if (currentVersion == mDatabaseVersion) {
+ return;
+ }
+
+ if (mDatabaseConfiguration.isMigrationRequired(currentVersion, mDatabaseVersion)) {
+ // From the current version to the desired version a migration is required, i.e.
+ // we won't be performing a copy destructive migration.
+ return;
+ }
+
+ if (mContext.deleteDatabase(databaseName)) {
+ try {
+ copyDatabaseFile(databaseFile);
+ } catch (IOException e) {
+ // We are more forgiving copying a database on a destructive migration since
+ // there is already a database file that can be opened.
+ Log.w(Room.LOG_TAG, "Unable to copy database file.", e);
+ }
+ } else {
+ Log.w(Room.LOG_TAG, "Failed to delete database file ("
+ + databaseName + ") for a copy destructive migration.");
+ }
+ } finally {
+ copyLock.unlock();
}
}
private void copyDatabaseFile(File destinationFile) throws IOException {
+ ReadableByteChannel input;
+ if (mCopyFromAssetPath != null) {
+ input = Channels.newChannel(mContext.getAssets().open(mCopyFromAssetPath));
+ } else if (mCopyFromFile != null) {
+ input = new FileInputStream(mCopyFromFile).getChannel();
+ } else {
+ throw new IllegalStateException("copyFromAssetPath and copyFromFile == null!");
+ }
+
+ // An intermediate file is used so that we never end up with a half-copied database file
+ // in the internal directory.
+ File intermediateFile = File.createTempFile("room-copy-helper", ".tmp");
+ FileChannel output = new FileOutputStream(intermediateFile).getChannel();
+ FileUtil.copy(input, output);
+
File parent = destinationFile.getParentFile();
if (parent != null && !parent.exists() && !parent.mkdirs()) {
- throw new IOException("Unable to create directories for "
+ throw new IOException("Failed to create directories for "
+ destinationFile.getAbsolutePath());
}
- InputStream input;
- if (mCopyFromAssetPath != null) {
- input = mContext.getAssets().open(mCopyFromAssetPath);
- } else if (mCopyFromFile != null) {
- input = new FileInputStream(mCopyFromFile);
- } else {
- throw new IllegalStateException("copyFromAssetPath and copyFromFile = null!");
- }
- OutputStream output = new FileOutputStream(destinationFile);
- copy(input, output);
- }
-
- private void copy(InputStream input, OutputStream output) throws IOException {
- try {
- int length;
- byte[] buffer = new byte[1024 * 4];
- while ((length = input.read(buffer)) > 0) {
- output.write(buffer, 0, length);
- }
- } finally {
- input.close();
- output.close();
+ if (!intermediateFile.renameTo(destinationFile)) {
+ throw new IOException("Failed to move intermediate file ("
+ + intermediateFile.getAbsolutePath() + ") to destination ("
+ + destinationFile.getAbsolutePath() + ").");
}
}
}
diff --git a/room/runtime/src/main/java/androidx/room/util/CopyLock.java b/room/runtime/src/main/java/androidx/room/util/CopyLock.java
new file mode 100644
index 0000000..3750315f
--- /dev/null
+++ b/room/runtime/src/main/java/androidx/room/util/CopyLock.java
@@ -0,0 +1,112 @@
+/*
+ * 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.room.util;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.channels.FileChannel;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * Utility class for in-process and multi-process key-based lock mechanism for safely copying
+ * database files.
+ * <p>
+ * Acquiring the lock will be quick if no other thread or process has a lock with the same key.
+ * But if the lock is already held then acquiring it will block, until the other thread or process
+ * releases the lock. Note that the key and lock directory must be the same to achieve
+ * synchronization.
+ * <p>
+ * Locking is done via two levels:
+ * <ol>
+ * <li>
+ * Thread locking within the same JVM process is done via a map of String key to ReentrantLock
+ * objects.
+ * <li>
+ * Multi-process locking is done via a dummy file whose name contains the key and FileLock
+ * objects.
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+public class CopyLock {
+
+ // in-process lock map
+ private static final Map<String, Lock> sThreadLocks = new HashMap<>();
+
+ private final File mCopyLockFile;
+ private final Lock mThreadLock;
+ private final boolean mFileLevelLock;
+ private FileChannel mLockChannel;
+
+ /**
+ * Creates a lock with {@code name} and using {@code lockDir} as the directory for the
+ * lock files.
+ * @param name the name of this lock.
+ * @param lockDir the directory where the lock files will be located.
+ * @param processLock whether to use file for process level locking or not.
+ */
+ public CopyLock(@NonNull String name, @NonNull File lockDir, boolean processLock) {
+ mCopyLockFile = new File(lockDir, name + ".lck");
+ mThreadLock = getThreadLock(mCopyLockFile.getAbsolutePath());
+ mFileLevelLock = processLock;
+ }
+
+ /**
+ * Attempts to grab the lock, blocking if already held by another thread or process.
+ */
+ public void lock() {
+ mThreadLock.lock();
+ if (mFileLevelLock) {
+ try {
+ mLockChannel = new FileOutputStream(mCopyLockFile).getChannel();
+ mLockChannel.lock();
+ } catch (IOException e) {
+ throw new IllegalStateException("Unable to grab copy lock.", e);
+ }
+ }
+ }
+
+ /**
+ * Releases the lock.
+ */
+ public void unlock() {
+ if (mLockChannel != null) {
+ try {
+ mLockChannel.close();
+ } catch (IOException ignored) { }
+ }
+ mThreadLock.unlock();
+ }
+
+ private static Lock getThreadLock(String key) {
+ synchronized (sThreadLocks) {
+ Lock threadLock = sThreadLocks.get(key);
+ if (threadLock == null) {
+ threadLock = new ReentrantLock();
+ sThreadLocks.put(key, threadLock);
+ }
+ return threadLock;
+ }
+ }
+}
diff --git a/room/runtime/src/main/java/androidx/room/util/DBUtil.java b/room/runtime/src/main/java/androidx/room/util/DBUtil.java
index c6986ac..f7afd26 100644
--- a/room/runtime/src/main/java/androidx/room/util/DBUtil.java
+++ b/room/runtime/src/main/java/androidx/room/util/DBUtil.java
@@ -117,7 +117,7 @@
try {
ByteBuffer buffer = ByteBuffer.allocate(4);
input = new FileInputStream(databaseFile).getChannel();
- input.lock(60, 4, true);
+ input.tryLock(60, 4, true);
input.position(60);
int read = input.read(buffer);
if (read != 4) {
diff --git a/room/runtime/src/main/java/androidx/room/util/FileUtil.java b/room/runtime/src/main/java/androidx/room/util/FileUtil.java
new file mode 100644
index 0000000..a221ff7
--- /dev/null
+++ b/room/runtime/src/main/java/androidx/room/util/FileUtil.java
@@ -0,0 +1,71 @@
+/*
+ * 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.room.util;
+
+import android.annotation.SuppressLint;
+import android.os.Build;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.channels.Channels;
+import java.nio.channels.FileChannel;
+import java.nio.channels.ReadableByteChannel;
+
+/**
+ * File utilities for Room
+ *
+ * @hide
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
+public class FileUtil {
+
+ /**
+ * Copies data from the input channel to the output file channel.
+ *
+ * @param input the input channel to copy.
+ * @param output the output channel to copy.
+ * @throws IOException if there is an I/O error.
+ */
+ @SuppressLint("LambdaLast")
+ public static void copy(@NonNull ReadableByteChannel input, @NonNull FileChannel output)
+ throws IOException {
+ try {
+ if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
+ output.transferFrom(input, 0, Long.MAX_VALUE);
+ } else {
+ InputStream inputStream = Channels.newInputStream(input);
+ OutputStream outputStream = Channels.newOutputStream(output);
+ int length;
+ byte[] buffer = new byte[1024 * 4];
+ while ((length = inputStream.read(buffer)) > 0) {
+ outputStream.write(buffer, 0, length);
+ }
+ }
+ output.force(false);
+ } finally {
+ input.close();
+ output.close();
+ }
+ }
+
+ private FileUtil() {
+ }
+}
diff --git a/room/runtime/src/test/java/androidx/room/SQLiteCopyOpenHelperTest.kt b/room/runtime/src/test/java/androidx/room/SQLiteCopyOpenHelperTest.kt
new file mode 100644
index 0000000..bb539aa
--- /dev/null
+++ b/room/runtime/src/test/java/androidx/room/SQLiteCopyOpenHelperTest.kt
@@ -0,0 +1,267 @@
+/*
+ * 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.room
+
+import android.content.Context
+import android.content.res.AssetManager
+import androidx.sqlite.db.SupportSQLiteOpenHelper
+import org.junit.Assert.assertEquals
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import java.io.File
+import java.io.FileInputStream
+import java.io.IOException
+import java.io.InputStream
+import java.io.RandomAccessFile
+import java.nio.ByteBuffer
+import java.util.concurrent.TimeUnit
+import java.util.concurrent.TimeoutException
+import java.util.concurrent.atomic.AtomicInteger
+import kotlin.concurrent.thread
+
+@RunWith(JUnit4::class)
+class SQLiteCopyOpenHelperTest {
+
+ companion object {
+ const val DB_NAME = "test.db"
+ const val DB_VERSION = 0
+ }
+
+ @get:Rule
+ val tempDirectory = TemporaryFolder()
+
+ val context: Context = mock(Context::class.java)
+ val assetManager: AssetManager = mock(AssetManager::class.java)
+ val delegate: SupportSQLiteOpenHelper = mock(SQLiteCopyOpenHelper::class.java)
+ val configuration: DatabaseConfiguration = mock(DatabaseConfiguration::class.java)
+
+ @Test
+ fun singleCopy() {
+ val copyFile = tempDirectory.newFile("toCopy.db")
+ writeDatabaseVersion(copyFile)
+ setupMocks(tempDirectory.root, copyFile)
+
+ val openHelper = createOpenHelper(copyFile)
+ openHelper.writableDatabase
+ openHelper.writableDatabase
+
+ verify(delegate).databaseName
+ verify(context).getDatabasePath(DB_NAME)
+ verify(assetManager).open("toCopy.db")
+ verifyNoMoreInteractions(configuration)
+
+ assertEquals(1, getAndIncrementAccessCount(copyFile))
+ }
+
+ @Test
+ fun singleCopy_multiInstance() {
+ val copyFile = tempDirectory.newFile("toCopy.db")
+ writeDatabaseVersion(copyFile)
+ setupMocks(tempDirectory.root, copyFile)
+
+ createOpenHelper(copyFile).writableDatabase
+ createOpenHelper(copyFile).writableDatabase
+
+ verify(delegate, times(2)).databaseName
+ verify(context, times(2)).getDatabasePath(DB_NAME)
+ verify(assetManager).open("toCopy.db")
+ verifyNoMoreInteractions(configuration)
+
+ assertEquals(1, getAndIncrementAccessCount(copyFile))
+ }
+
+ @Test
+ fun singleCopy_multiThread() {
+ val copyFile = tempDirectory.newFile("toCopy.db")
+ writeDatabaseVersion(copyFile)
+ setupMocks(tempDirectory.root, copyFile)
+
+ val t1 = thread(name = "DB Thread A") {
+ createOpenHelper(copyFile).writableDatabase
+ }
+ val t2 = thread(name = "DB Thread B") {
+ createOpenHelper(copyFile).writableDatabase
+ }
+
+ t1.join()
+ t2.join()
+
+ verify(delegate, times(2)).databaseName
+ verify(context, times(2)).getDatabasePath(DB_NAME)
+ verify(assetManager).open("toCopy.db")
+ verifyNoMoreInteractions(configuration)
+
+ assertEquals(1, getAndIncrementAccessCount(copyFile))
+ }
+
+ @Test
+ fun singleCopy_multiProcess() {
+ val copyFile = tempDirectory.newFile("toCopy.db")
+ writeDatabaseVersion(copyFile)
+
+ val processes = List(2) { spawnCopyProcess(copyFile) }
+ try {
+ processes.forEach {
+ val exited = it.waitFor(5, TimeUnit.SECONDS)
+ if (!exited) {
+ throw TimeoutException("Timed Out waiting for copy process to finish.")
+ }
+ val exitCode = it.exitValue()
+ if (exitCode != 0) {
+ throw IllegalStateException("Copy process exited with non-zero code. " +
+ "Code: $exitCode")
+ }
+ }
+ } finally {
+ processes.forEach { it.destroy() }
+ }
+
+ assertEquals(1, getAndIncrementAccessCount(copyFile))
+ }
+
+ @Test
+ fun firstCopyFails_multiThread() {
+ val copyFile = tempDirectory.newFile("toCopy.db")
+ writeDatabaseVersion(copyFile)
+ val openCount = AtomicInteger()
+ setupMocks(tempDirectory.root, copyFile) {
+ if (openCount.getAndIncrement() == 0) {
+ throw IOException("Fake IO Error")
+ }
+ }
+
+ val exceptions = mutableListOf<Exception>()
+ val t1 = thread(name = "DB Thread A") {
+ try {
+ createOpenHelper(copyFile).writableDatabase
+ } catch (e: Exception) {
+ exceptions.add(e)
+ }
+ }
+ val t2 = thread(name = "DB Thread B") {
+ try {
+ createOpenHelper(copyFile).writableDatabase
+ } catch (e: Exception) {
+ exceptions.add(e)
+ }
+ }
+
+ t1.join()
+ t2.join()
+
+ verify(delegate, times(2)).databaseName
+ verify(context, times(2)).getDatabasePath(DB_NAME)
+ verify(assetManager, times(2)).open("toCopy.db")
+ verifyNoMoreInteractions(configuration)
+
+ assertEquals(2, getAndIncrementAccessCount(copyFile))
+ assertEquals(1, exceptions.size)
+ }
+
+ internal fun setupMocks(tmpDir: File, copyFromFile: File, onAssetOpen: () -> Unit = {}) {
+ `when`(delegate.databaseName).thenReturn(DB_NAME)
+ `when`(context.getDatabasePath(DB_NAME)).thenReturn(File(tmpDir, DB_NAME))
+ configuration::class.java.getField("multiInstanceInvalidation").let { field ->
+ field.isAccessible = true
+ field.setBoolean(configuration, true)
+ }
+ `when`(context.filesDir).thenReturn(tmpDir)
+ `when`(context.assets).thenReturn(assetManager)
+ `when`(assetManager.open(copyFromFile.name)).thenAnswer {
+ getAndIncrementAccessCount(copyFromFile) // increment file access counter
+ onAssetOpen()
+ return@thenAnswer object : InputStream() {
+ val delegate by lazy { FileInputStream(copyFromFile) }
+ override fun read(): Int {
+ Thread.sleep(10) // simulate slow reading, as if this was a big file
+ return delegate.read()
+ }
+ }
+ }
+ }
+
+ internal fun createOpenHelper(copyFromAssetFile: File) =
+ SQLiteCopyOpenHelper(
+ context,
+ copyFromAssetFile.name,
+ null,
+ DB_VERSION,
+ delegate
+ ).apply { setDatabaseConfiguration(configuration) }
+
+ // Writes sqlite user database version in a file, located at offset 60.
+ private fun writeDatabaseVersion(file: File) {
+ val buffer = ByteBuffer.allocate(4)
+ RandomAccessFile(file, "rw").channel.use {
+ buffer.putInt(DB_VERSION)
+ buffer.rewind()
+ it.write(buffer, 60)
+ }
+ }
+
+ // Get and increment a made-up file access counter located at the first 4 bytes of a file.
+ private fun getAndIncrementAccessCount(file: File): Int {
+ val buffer = ByteBuffer.allocate(4)
+ return RandomAccessFile(file, "rw").channel.use {
+ it.lock()
+ it.read(buffer, 0)
+ buffer.rewind()
+ val count = buffer.int
+ buffer.rewind()
+ buffer.putInt(count + 1)
+ buffer.rewind()
+ it.write(buffer, 0)
+ return@use count
+ }
+ }
+
+ // Spawns a new Java process to perform a copy using the open helper.
+ private fun spawnCopyProcess(copyFromFile: File): Process {
+ val javaBin = System.getProperty("java.home") +
+ File.separator + "bin" + File.separator + "java"
+ val classpath = System.getProperty("java.class.path")
+ val mainClass = RoomCopyTestProcess::class.java.canonicalName
+ val tmpDirPath = tempDirectory.root.absolutePath
+ val copyFromFilePath = copyFromFile.absolutePath
+ return ProcessBuilder(javaBin, "-cp", classpath, mainClass, tmpDirPath, copyFromFilePath)
+ .redirectOutput(ProcessBuilder.Redirect.INHERIT)
+ .redirectErrorStream(true)
+ .start()
+ }
+}
+
+// Main class that will run in a separate process from the test. Used to verify multi-process lock.
+class RoomCopyTestProcess {
+ companion object {
+ @JvmStatic
+ fun main(args: Array<String>) {
+ val tmpDir = File(args[0])
+ val copyFromFile = File(args[1])
+ val openHelper = SQLiteCopyOpenHelperTest().apply { setupMocks(tmpDir, copyFromFile) }
+ .createOpenHelper(copyFromFile)
+ openHelper.writableDatabase
+ }
+ }
+}
\ No newline at end of file
diff --git a/security/crypto/api/api_lint.ignore b/security/crypto/api/api_lint.ignore
new file mode 100644
index 0000000..2635915
--- /dev/null
+++ b/security/crypto/api/api_lint.ignore
@@ -0,0 +1,15 @@
+// Baseline format: 1.0
+ContextFirst: androidx.security.crypto.EncryptedFile.Builder#Builder(java.io.File, android.content.Context, String, androidx.security.crypto.EncryptedFile.FileEncryptionScheme) parameter #1:
+ Context is distinct, so it must be the first argument (method `Builder`)
+ContextFirst: androidx.security.crypto.EncryptedSharedPreferences#create(String, String, android.content.Context, androidx.security.crypto.EncryptedSharedPreferences.PrefKeyEncryptionScheme, androidx.security.crypto.EncryptedSharedPreferences.PrefValueEncryptionScheme) parameter #2:
+ Context is distinct, so it must be the first argument (method `create`)
+
+
+KotlinOperator: androidx.security.crypto.EncryptedSharedPreferences#contains(String):
+ Method can be invoked as a "in" operator from Kotlin: `contains` (this is usually desirable; just make sure it makes sense for this type of object)
+
+
+RegistrationName: androidx.security.crypto.EncryptedSharedPreferences#registerOnSharedPreferenceChangeListener(android.content.SharedPreferences.OnSharedPreferenceChangeListener):
+ Listener methods should be named add/remove; was registerOnSharedPreferenceChangeListener
+RegistrationName: androidx.security.crypto.EncryptedSharedPreferences#unregisterOnSharedPreferenceChangeListener(android.content.SharedPreferences.OnSharedPreferenceChangeListener):
+ Listener methods should be named add/remove; was unregisterOnSharedPreferenceChangeListener
diff --git a/settings.gradle b/settings.gradle
index bee0394..67c8116 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -40,6 +40,10 @@
includeProject(":activity:integration-tests:testapp", "activity/integration-tests/testapp")
includeProject(":ads-identifier", "ads/ads-identifier")
includeProject(":annotation", "annotations")
+includeProject(":annotation:annotation-sampled", "annotation/annotation-sampled")
+includeProject(":annotation:annotation-experimental", "annotation/annotation-experimental")
+includeProject(":annotation:annotation-experimental-lint", "annotation/annotation-experimental-lint")
+includeProject(":annotation:annotation-experimental-lint-integration-tests", "annotation/annotation-experimental-lint/integration-tests")
includeProject(":animation", "animation")
includeProject(":animation:testing", "animation/testing")
includeProject(":animation:integration-tests:testapp", "animation/integration-tests/testapp")
diff --git a/slices/core/api/api_lint.ignore b/slices/core/api/api_lint.ignore
new file mode 100644
index 0000000..cfd172f
--- /dev/null
+++ b/slices/core/api/api_lint.ignore
@@ -0,0 +1,15 @@
+// Baseline format: 1.0
+ActionValue: androidx.slice.Slice#EXTRA_SELECTION:
+ Inconsistent extra value; expected `androidx.slice.extra.SELECTION`, was `android.app.slice.extra.SELECTION`
+
+
+ContextFirst: androidx.slice.SliceConvert#wrap(android.app.slice.Slice, android.content.Context) parameter #1:
+ Context is distinct, so it must be the first argument (method `wrap`)
+
+
+ContextNameSuffix: androidx.slice.SliceProviderWithCallbacks:
+ Inconsistent class name; should be `<Foo>Provider`, was `SliceProviderWithCallbacks`
+
+
+SingularCallback: androidx.slice.SliceProviderWithCallbacks:
+ Callback class names should be singular: SliceProviderWithCallbacks
diff --git a/slices/view/api/api_lint.ignore b/slices/view/api/api_lint.ignore
new file mode 100644
index 0000000..7e7ad54
--- /dev/null
+++ b/slices/view/api/api_lint.ignore
@@ -0,0 +1,9 @@
+// Baseline format: 1.0
+BannedThrow: androidx.slice.SliceUtils#serializeSlice(androidx.slice.Slice, android.content.Context, java.io.OutputStream, androidx.slice.SliceUtils.SerializeOptions):
+ Methods must not mention RuntimeException subclasses in throws clauses (was `java.lang.IllegalArgumentException`)
+
+
+ContextFirst: androidx.slice.SliceUtils#serializeSlice(androidx.slice.Slice, android.content.Context, java.io.OutputStream, androidx.slice.SliceUtils.SerializeOptions) parameter #1:
+ Context is distinct, so it must be the first argument (method `serializeSlice`)
+ContextFirst: androidx.slice.SliceUtils.SliceActionListener#onSliceAction(android.net.Uri, android.content.Context, android.content.Intent) parameter #1:
+ Context is distinct, so it must be the first argument (method `onSliceAction`)
diff --git a/slidingpanelayout/api/api_lint.ignore b/slidingpanelayout/api/api_lint.ignore
new file mode 100644
index 0000000..0c6e84a
--- /dev/null
+++ b/slidingpanelayout/api/api_lint.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+ListenerInterface: androidx.slidingpanelayout.widget.SlidingPaneLayout.SimplePanelSlideListener:
+ Listeners should be an interface, or otherwise renamed Callback: SimplePanelSlideListener
diff --git a/sqlite/sqlite-framework/api/api_lint.ignore b/sqlite/sqlite-framework/api/api_lint.ignore
new file mode 100644
index 0000000..e498b61
--- /dev/null
+++ b/sqlite/sqlite-framework/api/api_lint.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+AcronymName: androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory:
+ Acronyms should not be capitalized in class names: was `FrameworkSQLiteOpenHelperFactory`, should this be `FrameworkSqLiteOpenHelperFactory`?
diff --git a/sqlite/sqlite-ktx/api/api_lint.ignore b/sqlite/sqlite-ktx/api/api_lint.ignore
new file mode 100644
index 0000000..3948bf5
--- /dev/null
+++ b/sqlite/sqlite-ktx/api/api_lint.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+AcronymName: androidx.sqlite.db.SupportSQLiteDatabaseKt:
+ Acronyms should not be capitalized in class names: was `SupportSQLiteDatabaseKt`, should this be `SupportSqLiteDatabaseKt`?
diff --git a/sqlite/sqlite/api/api_lint.ignore b/sqlite/sqlite/api/api_lint.ignore
new file mode 100644
index 0000000..81bb000
--- /dev/null
+++ b/sqlite/sqlite/api/api_lint.ignore
@@ -0,0 +1,19 @@
+// Baseline format: 1.0
+AcronymName: androidx.sqlite.db.SimpleSQLiteQuery:
+ Acronyms should not be capitalized in class names: was `SimpleSQLiteQuery`, should this be `SimpleSqLiteQuery`?
+AcronymName: androidx.sqlite.db.SupportSQLiteDatabase:
+ Acronyms should not be capitalized in class names: was `SupportSQLiteDatabase`, should this be `SupportSqLiteDatabase`?
+AcronymName: androidx.sqlite.db.SupportSQLiteDatabase#execSQL(String):
+ Acronyms should not be capitalized in method names: was `execSQL`, should this be `execSql`?
+AcronymName: androidx.sqlite.db.SupportSQLiteDatabase#execSQL(String, Object[]):
+ Acronyms should not be capitalized in method names: was `execSQL`, should this be `execSql`?
+AcronymName: androidx.sqlite.db.SupportSQLiteOpenHelper:
+ Acronyms should not be capitalized in class names: was `SupportSQLiteOpenHelper`, should this be `SupportSqLiteOpenHelper`?
+AcronymName: androidx.sqlite.db.SupportSQLiteProgram:
+ Acronyms should not be capitalized in class names: was `SupportSQLiteProgram`, should this be `SupportSqLiteProgram`?
+AcronymName: androidx.sqlite.db.SupportSQLiteQuery:
+ Acronyms should not be capitalized in class names: was `SupportSQLiteQuery`, should this be `SupportSqLiteQuery`?
+AcronymName: androidx.sqlite.db.SupportSQLiteQueryBuilder:
+ Acronyms should not be capitalized in class names: was `SupportSQLiteQueryBuilder`, should this be `SupportSqLiteQueryBuilder`?
+AcronymName: androidx.sqlite.db.SupportSQLiteStatement:
+ Acronyms should not be capitalized in class names: was `SupportSQLiteStatement`, should this be `SupportSqLiteStatement`?
diff --git a/textclassifier/api/api_lint.ignore b/textclassifier/api/api_lint.ignore
new file mode 100644
index 0000000..235b2b8
--- /dev/null
+++ b/textclassifier/api/api_lint.ignore
@@ -0,0 +1,15 @@
+// Baseline format: 1.0
+AutoBoxing: androidx.textclassifier.ConversationActions.Message#getReferenceTime():
+ Must avoid boxed primitives (`java.lang.Long`)
+AutoBoxing: androidx.textclassifier.ConversationActions.Message.Builder#setReferenceTime(Long) parameter #0:
+ Must avoid boxed primitives (`java.lang.Long`)
+AutoBoxing: androidx.textclassifier.TextClassification.Request#getReferenceTime():
+ Must avoid boxed primitives (`java.lang.Long`)
+AutoBoxing: androidx.textclassifier.TextClassification.Request.Builder#setReferenceTime(Long) parameter #0:
+ Must avoid boxed primitives (`java.lang.Long`)
+AutoBoxing: androidx.textclassifier.TextLinks.Request#getReferenceTime():
+ Must avoid boxed primitives (`java.lang.Long`)
+AutoBoxing: androidx.textclassifier.TextLinks.Request.Builder#setReferenceTime(Long) parameter #0:
+ Must avoid boxed primitives (`java.lang.Long`)
+AutoBoxing: androidx.textclassifier.TextLinksParams.Builder#setReferenceTime(Long) parameter #0:
+ Must avoid boxed primitives (`java.lang.Long`)
diff --git a/transition/api/api_lint.ignore b/transition/api/api_lint.ignore
new file mode 100644
index 0000000..0a7434f
--- /dev/null
+++ b/transition/api/api_lint.ignore
@@ -0,0 +1,9 @@
+// Baseline format: 1.0
+ContextFirst: androidx.transition.Scene#getSceneForLayout(android.view.ViewGroup, int, android.content.Context) parameter #2:
+ Context is distinct, so it must be the first argument (method `getSceneForLayout`)
+
+
+NoClone: androidx.transition.Transition#clone():
+ Provide an explicit copy constructor instead of implementing `clone()`
+NoClone: androidx.transition.TransitionSet#clone():
+ Provide an explicit copy constructor instead of implementing `clone()`
diff --git a/tv-provider/api/api_lint.ignore b/tv-provider/api/api_lint.ignore
new file mode 100644
index 0000000..dcf4d5f
--- /dev/null
+++ b/tv-provider/api/api_lint.ignore
@@ -0,0 +1,51 @@
+// Baseline format: 1.0
+ActionValue: androidx.tvprovider.media.tv.TvContractCompat#ACTION_INITIALIZE_PROGRAMS:
+ Inconsistent action value; expected `androidx.tvprovider.media.tv.action.INITIALIZE_PROGRAMS`, was `android.media.tv.action.INITIALIZE_PROGRAMS`
+ActionValue: androidx.tvprovider.media.tv.TvContractCompat#ACTION_PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT:
+ Inconsistent action value; expected `androidx.tvprovider.media.tv.action.PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT`, was `android.media.tv.action.PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT`
+ActionValue: androidx.tvprovider.media.tv.TvContractCompat#ACTION_PREVIEW_PROGRAM_BROWSABLE_DISABLED:
+ Inconsistent action value; expected `androidx.tvprovider.media.tv.action.PREVIEW_PROGRAM_BROWSABLE_DISABLED`, was `android.media.tv.action.PREVIEW_PROGRAM_BROWSABLE_DISABLED`
+ActionValue: androidx.tvprovider.media.tv.TvContractCompat#ACTION_REQUEST_CHANNEL_BROWSABLE:
+ Inconsistent action value; expected `androidx.tvprovider.media.tv.action.REQUEST_CHANNEL_BROWSABLE`, was `android.media.tv.action.REQUEST_CHANNEL_BROWSABLE`
+ActionValue: androidx.tvprovider.media.tv.TvContractCompat#ACTION_WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED:
+ Inconsistent action value; expected `androidx.tvprovider.media.tv.action.WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED`, was `android.media.tv.action.WATCH_NEXT_PROGRAM_BROWSABLE_DISABLED`
+ActionValue: androidx.tvprovider.media.tv.TvContractCompat#EXTRA_CHANNEL_ID:
+ Inconsistent extra value; expected `androidx.tvprovider.media.tv.extra.CHANNEL_ID`, was `android.media.tv.extra.CHANNEL_ID`
+ActionValue: androidx.tvprovider.media.tv.TvContractCompat#EXTRA_PREVIEW_PROGRAM_ID:
+ Inconsistent extra value; expected `androidx.tvprovider.media.tv.extra.PREVIEW_PROGRAM_ID`, was `android.media.tv.extra.PREVIEW_PROGRAM_ID`
+ActionValue: androidx.tvprovider.media.tv.TvContractCompat#EXTRA_WATCH_NEXT_PROGRAM_ID:
+ Inconsistent extra value; expected `androidx.tvprovider.media.tv.extra.WATCH_NEXT_PROGRAM_ID`, was `android.media.tv.extra.WATCH_NEXT_PROGRAM_ID`
+
+
+AutoBoxing: androidx.tvprovider.media.tv.Channel#getInternalProviderFlag1():
+ Must avoid boxed primitives (`java.lang.Long`)
+AutoBoxing: androidx.tvprovider.media.tv.Channel#getInternalProviderFlag2():
+ Must avoid boxed primitives (`java.lang.Long`)
+AutoBoxing: androidx.tvprovider.media.tv.Channel#getInternalProviderFlag3():
+ Must avoid boxed primitives (`java.lang.Long`)
+AutoBoxing: androidx.tvprovider.media.tv.Channel#getInternalProviderFlag4():
+ Must avoid boxed primitives (`java.lang.Long`)
+AutoBoxing: androidx.tvprovider.media.tv.PreviewChannel#getInternalProviderFlag1():
+ Must avoid boxed primitives (`java.lang.Long`)
+AutoBoxing: androidx.tvprovider.media.tv.PreviewChannel#getInternalProviderFlag2():
+ Must avoid boxed primitives (`java.lang.Long`)
+AutoBoxing: androidx.tvprovider.media.tv.PreviewChannel#getInternalProviderFlag3():
+ Must avoid boxed primitives (`java.lang.Long`)
+AutoBoxing: androidx.tvprovider.media.tv.PreviewChannel#getInternalProviderFlag4():
+ Must avoid boxed primitives (`java.lang.Long`)
+
+
+EqualsAndHashCode: androidx.tvprovider.media.tv.PreviewProgram#equals(Object):
+ Must override both equals and hashCode; missing one in androidx.tvprovider.media.tv.PreviewProgram
+EqualsAndHashCode: androidx.tvprovider.media.tv.WatchNextProgram#equals(Object):
+ Must override both equals and hashCode; missing one in androidx.tvprovider.media.tv.WatchNextProgram
+
+
+IntentName: androidx.tvprovider.media.tv.TvContractCompat.PreviewPrograms#COLUMN_INTERACTION_COUNT:
+ Intent action constant name must be ACTION_FOO: COLUMN_INTERACTION_COUNT
+IntentName: androidx.tvprovider.media.tv.TvContractCompat.PreviewPrograms#COLUMN_INTERACTION_TYPE:
+ Intent action constant name must be ACTION_FOO: COLUMN_INTERACTION_TYPE
+IntentName: androidx.tvprovider.media.tv.TvContractCompat.WatchNextPrograms#COLUMN_INTERACTION_COUNT:
+ Intent action constant name must be ACTION_FOO: COLUMN_INTERACTION_COUNT
+IntentName: androidx.tvprovider.media.tv.TvContractCompat.WatchNextPrograms#COLUMN_INTERACTION_TYPE:
+ Intent action constant name must be ACTION_FOO: COLUMN_INTERACTION_TYPE
diff --git a/ui/android-text/api/restricted_1.0.0-alpha01.txt b/ui/android-text/api/restricted_1.0.0-alpha01.txt
index 088333e..7ab4008 100644
--- a/ui/android-text/api/restricted_1.0.0-alpha01.txt
+++ b/ui/android-text/api/restricted_1.0.0-alpha01.txt
@@ -80,6 +80,7 @@
@RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public class BaselineShiftSpan extends android.text.style.MetricAffectingSpan {
ctor public BaselineShiftSpan(float);
+ method public float getMultiplier();
method public void updateDrawState(android.text.TextPaint!);
method public void updateMeasureState(android.text.TextPaint);
}
diff --git a/ui/android-text/api/restricted_current.txt b/ui/android-text/api/restricted_current.txt
index 088333e..7ab4008 100644
--- a/ui/android-text/api/restricted_current.txt
+++ b/ui/android-text/api/restricted_current.txt
@@ -80,6 +80,7 @@
@RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public class BaselineShiftSpan extends android.text.style.MetricAffectingSpan {
ctor public BaselineShiftSpan(float);
+ method public float getMultiplier();
method public void updateDrawState(android.text.TextPaint!);
method public void updateMeasureState(android.text.TextPaint);
}
diff --git a/ui/android-text/src/main/java/androidx/text/style/BaselineShiftSpan.java b/ui/android-text/src/main/java/androidx/text/style/BaselineShiftSpan.java
index 3a7703d4..7b6ebc2 100644
--- a/ui/android-text/src/main/java/androidx/text/style/BaselineShiftSpan.java
+++ b/ui/android-text/src/main/java/androidx/text/style/BaselineShiftSpan.java
@@ -49,4 +49,8 @@
public void updateDrawState(TextPaint textPaint) {
textPaint.baselineShift += textPaint.ascent() * mMultiplier;
}
+
+ public float getMultiplier() {
+ return mMultiplier;
+ }
}
diff --git a/ui/core/api/api_lint.ignore b/ui/core/api/api_lint.ignore
new file mode 100644
index 0000000..bfdb136
--- /dev/null
+++ b/ui/core/api/api_lint.ignore
@@ -0,0 +1,537 @@
+// Baseline format: 1.0
+AcronymName: androidx.ui.core.Dp#div-KkBJKWw(float):
+ Acronyms should not be capitalized in method names: was `div-KkBJKWw`, should this be `div-KkBjkWw`?
+AcronymName: androidx.ui.core.Dp#times-KkBJKWw(float):
+ Acronyms should not be capitalized in method names: was `times-KkBJKWw`, should this be `times-KkBjkWw`?
+AcronymName: androidx.ui.core.DpCubed#div-KkBJKWw(float, float):
+ Acronyms should not be capitalized in method names: was `div-KkBJKWw`, should this be `div-KkBjkWw`?
+AcronymName: androidx.ui.core.DpInverse#compareTo-ASDJgtI(float, float):
+ Acronyms should not be capitalized in method names: was `compareTo-ASDJgtI`, should this be `compareTo-AsdJgtI`?
+AcronymName: androidx.ui.core.DpInverse#minus-ASDJgtI(float, float):
+ Acronyms should not be capitalized in method names: was `minus-ASDJgtI`, should this be `minus-AsdJgtI`?
+AcronymName: androidx.ui.core.DpInverse#plus-ASDJgtI(float, float):
+ Acronyms should not be capitalized in method names: was `plus-ASDJgtI`, should this be `plus-AsdJgtI`?
+AcronymName: androidx.ui.core.DpInverse#times-KkBJKWw(float, float):
+ Acronyms should not be capitalized in method names: was `times-KkBJKWw`, should this be `times-KkBjkWw`?
+AcronymName: androidx.ui.core.DpSquared#compareTo-KkBJKWw(float, float):
+ Acronyms should not be capitalized in method names: was `compareTo-KkBJKWw`, should this be `compareTo-KkBjkWw`?
+AcronymName: androidx.ui.core.DpSquared#div-KkBJKWw(float, float):
+ Acronyms should not be capitalized in method names: was `div-KkBJKWw`, should this be `div-KkBjkWw`?
+AcronymName: androidx.ui.core.DpSquared#minus-KkBJKWw(float, float):
+ Acronyms should not be capitalized in method names: was `minus-KkBJKWw`, should this be `minus-KkBjkWw`?
+AcronymName: androidx.ui.core.DpSquared#plus-KkBJKWw(float, float):
+ Acronyms should not be capitalized in method names: was `plus-KkBJKWw`, should this be `plus-KkBjkWw`?
+AcronymName: androidx.ui.core.Px#div-kVJEwbQ(float):
+ Acronyms should not be capitalized in method names: was `div-kVJEwbQ`, should this be `div-kVjEwbQ`?
+AcronymName: androidx.ui.core.Px#times-kVJEwbQ(float):
+ Acronyms should not be capitalized in method names: was `times-kVJEwbQ`, should this be `times-kVjEwbQ`?
+AcronymName: androidx.ui.core.PxCubed#div-kVJEwbQ(float, float):
+ Acronyms should not be capitalized in method names: was `div-kVJEwbQ`, should this be `div-kVjEwbQ`?
+AcronymName: androidx.ui.core.PxInverse#compareTo-MXEAIGs(float, float):
+ Acronyms should not be capitalized in method names: was `compareTo-MXEAIGs`, should this be `compareTo-MxeaiGs`?
+AcronymName: androidx.ui.core.PxInverse#minus-MXEAIGs(float, float):
+ Acronyms should not be capitalized in method names: was `minus-MXEAIGs`, should this be `minus-MxeaiGs`?
+AcronymName: androidx.ui.core.PxInverse#plus-MXEAIGs(float, float):
+ Acronyms should not be capitalized in method names: was `plus-MXEAIGs`, should this be `plus-MxeaiGs`?
+AcronymName: androidx.ui.core.PxInverse#times-kVJEwbQ(float, float):
+ Acronyms should not be capitalized in method names: was `times-kVJEwbQ`, should this be `times-kVjEwbQ`?
+AcronymName: androidx.ui.core.PxSquared#compareTo-kVJEwbQ(float, float):
+ Acronyms should not be capitalized in method names: was `compareTo-kVJEwbQ`, should this be `compareTo-kVjEwbQ`?
+AcronymName: androidx.ui.core.PxSquared#div-kVJEwbQ(float, float):
+ Acronyms should not be capitalized in method names: was `div-kVJEwbQ`, should this be `div-kVjEwbQ`?
+AcronymName: androidx.ui.core.PxSquared#minus-kVJEwbQ(float, float):
+ Acronyms should not be capitalized in method names: was `minus-kVJEwbQ`, should this be `minus-kVjEwbQ`?
+AcronymName: androidx.ui.core.PxSquared#plus-kVJEwbQ(float, float):
+ Acronyms should not be capitalized in method names: was `plus-kVJEwbQ`, should this be `plus-kVjEwbQ`?
+AcronymName: androidx.ui.engine.geometry.Rect.Companion#fromLTRB(float, float, float, float):
+ Acronyms should not be capitalized in method names: was `fromLTRB`, should this be `fromLtrb`?
+AcronymName: androidx.ui.engine.geometry.Rect.Companion#fromLTWH(float, float, float, float):
+ Acronyms should not be capitalized in method names: was `fromLTWH`, should this be `fromLtwh`?
+AcronymName: androidx.ui.painting.Canvas#drawDRRect(androidx.ui.engine.geometry.RRect, androidx.ui.engine.geometry.RRect, androidx.ui.painting.Paint):
+ Acronyms should not be capitalized in method names: was `drawDRRect`, should this be `drawDrRect`?
+
+
+AutoBoxing: androidx.ui.vectormath64.Matrix4#scale(Object, Float, Float) parameter #1:
+ Must avoid boxed primitives (`java.lang.Float`)
+AutoBoxing: androidx.ui.vectormath64.Matrix4#scale(Object, Float, Float) parameter #2:
+ Must avoid boxed primitives (`java.lang.Float`)
+AutoBoxing: androidx.ui.vectormath64.Matrix4Kt#getAsScale(androidx.ui.vectormath64.Matrix4):
+ Must avoid boxed primitives (`java.lang.Float`)
+
+
+DocumentExceptions: androidx.ui.graphics.ColorKt#Color(float, float, float, float, androidx.ui.graphics.ColorSpace):
+ Method ColorKt.Color appears to be throwing java.lang.IllegalArgumentException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.ui.painting.Path#arcToPoint(androidx.ui.engine.geometry.Offset, androidx.ui.engine.geometry.Radius, float, boolean, boolean):
+ Method Path.arcToPoint appears to be throwing java.lang.UnsupportedOperationException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.ui.painting.Path#conicTo(float, float, float, float, float):
+ Method Path.conicTo appears to be throwing java.lang.UnsupportedOperationException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.ui.painting.Path#relativeConicTo(float, float, float, float, float):
+ Method Path.relativeConicTo appears to be throwing java.lang.UnsupportedOperationException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.ui.painting.Path.Companion#combine(androidx.ui.painting.PathOperation, androidx.ui.painting.Path, androidx.ui.painting.Path):
+ Method Companion.combine appears to be throwing java.lang.IllegalArgumentException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.ui.vectormath64.Matrix2#get(androidx.ui.vectormath64.MatrixColumn):
+ Method Matrix2.get appears to be throwing java.lang.IllegalArgumentException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.ui.vectormath64.Matrix2#get(int):
+ Method Matrix2.get appears to be throwing java.lang.IllegalArgumentException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.ui.vectormath64.Matrix3#get(androidx.ui.vectormath64.MatrixColumn):
+ Method Matrix3.get appears to be throwing java.lang.IllegalArgumentException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.ui.vectormath64.Matrix3#get(int):
+ Method Matrix3.get appears to be throwing java.lang.IllegalArgumentException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.ui.vectormath64.Matrix4#get(int):
+ Method Matrix4.get appears to be throwing java.lang.IllegalArgumentException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.ui.vectormath64.Vector2#get(androidx.ui.vectormath64.VectorComponent):
+ Method Vector2.get appears to be throwing java.lang.IllegalArgumentException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.ui.vectormath64.Vector2#get(int):
+ Method Vector2.get appears to be throwing java.lang.IllegalArgumentException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.ui.vectormath64.Vector2#set(androidx.ui.vectormath64.VectorComponent, float):
+ Method Vector2.set appears to be throwing java.lang.IllegalArgumentException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.ui.vectormath64.Vector2#set(int, float):
+ Method Vector2.set appears to be throwing java.lang.IllegalArgumentException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.ui.vectormath64.Vector3#get(androidx.ui.vectormath64.VectorComponent):
+ Method Vector3.get appears to be throwing java.lang.IllegalArgumentException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.ui.vectormath64.Vector3#get(int):
+ Method Vector3.get appears to be throwing java.lang.IllegalArgumentException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.ui.vectormath64.Vector3#set(androidx.ui.vectormath64.VectorComponent, float):
+ Method Vector3.set appears to be throwing java.lang.IllegalArgumentException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.ui.vectormath64.Vector3#set(int, float):
+ Method Vector3.set appears to be throwing java.lang.IllegalArgumentException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.ui.vectormath64.Vector4#get(int):
+ Method Vector4.get appears to be throwing java.lang.IllegalArgumentException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.ui.vectormath64.Vector4#set(int, float):
+ Method Vector4.set appears to be throwing java.lang.IllegalArgumentException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+
+
+EndsWithImpl: androidx.ui.core.DensityReceiverImpl:
+ Don't expose your implementation details: `DensityReceiverImpl` ends with `Impl`
+
+
+KotlinOperator: androidx.ui.core.Dp#div(androidx.ui.core.Dp):
+ Method can be invoked as a binary operator from Kotlin: `div` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.Dp#div(float):
+ Method can be invoked as a binary operator from Kotlin: `div` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.Dp#div(int):
+ Method can be invoked as a binary operator from Kotlin: `div` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.Dp#minus(androidx.ui.core.Dp):
+ Method can be invoked as a binary operator from Kotlin: `minus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.Dp#plus(androidx.ui.core.Dp):
+ Method can be invoked as a binary operator from Kotlin: `plus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.Dp#times(androidx.ui.core.Dp):
+ Method can be invoked as a binary operator from Kotlin: `times` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.Dp#times(float):
+ Method can be invoked as a binary operator from Kotlin: `times` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.Dp#times(int):
+ Method can be invoked as a binary operator from Kotlin: `times` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.Dp#unaryMinus():
+ Method can be invoked as a unary operator from Kotlin: `unaryMinus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.Duration#div(double):
+ Method can be invoked as a binary operator from Kotlin: `div` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.Duration#div(int):
+ Method can be invoked as a binary operator from Kotlin: `div` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.Duration#minus(androidx.ui.core.Duration):
+ Method can be invoked as a binary operator from Kotlin: `minus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.Duration#plus(androidx.ui.core.Duration):
+ Method can be invoked as a binary operator from Kotlin: `plus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.Duration#plus(androidx.ui.core.Timestamp):
+ Method can be invoked as a binary operator from Kotlin: `plus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.Duration#times(double):
+ Method can be invoked as a binary operator from Kotlin: `times` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.Duration#times(int):
+ Method can be invoked as a binary operator from Kotlin: `times` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.IntPx#div(double):
+ Method can be invoked as a binary operator from Kotlin: `div` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.IntPx#div(float):
+ Method can be invoked as a binary operator from Kotlin: `div` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.IntPx#div(int):
+ Method can be invoked as a binary operator from Kotlin: `div` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.IntPx#minus(androidx.ui.core.IntPx):
+ Method can be invoked as a binary operator from Kotlin: `minus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.IntPx#plus(androidx.ui.core.IntPx):
+ Method can be invoked as a binary operator from Kotlin: `plus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.IntPx#times(double):
+ Method can be invoked as a binary operator from Kotlin: `times` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.IntPx#times(float):
+ Method can be invoked as a binary operator from Kotlin: `times` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.IntPx#times(int):
+ Method can be invoked as a binary operator from Kotlin: `times` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.IntPx#unaryMinus():
+ Method can be invoked as a unary operator from Kotlin: `unaryMinus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.IntPxPosition#minus(androidx.ui.core.IntPxPosition):
+ Method can be invoked as a binary operator from Kotlin: `minus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.IntPxPosition#plus(androidx.ui.core.IntPxPosition):
+ Method can be invoked as a binary operator from Kotlin: `plus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.Position#minus(androidx.ui.core.Position):
+ Method can be invoked as a binary operator from Kotlin: `minus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.Position#plus(androidx.ui.core.Position):
+ Method can be invoked as a binary operator from Kotlin: `plus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.Px#div(androidx.ui.core.Px):
+ Method can be invoked as a binary operator from Kotlin: `div` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.Px#div(float):
+ Method can be invoked as a binary operator from Kotlin: `div` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.Px#div(int):
+ Method can be invoked as a binary operator from Kotlin: `div` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.Px#minus(androidx.ui.core.IntPx):
+ Method can be invoked as a binary operator from Kotlin: `minus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.Px#minus(androidx.ui.core.Px):
+ Method can be invoked as a binary operator from Kotlin: `minus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.Px#plus(androidx.ui.core.IntPx):
+ Method can be invoked as a binary operator from Kotlin: `plus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.Px#plus(androidx.ui.core.Px):
+ Method can be invoked as a binary operator from Kotlin: `plus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.Px#times(androidx.ui.core.Px):
+ Method can be invoked as a binary operator from Kotlin: `times` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.Px#times(float):
+ Method can be invoked as a binary operator from Kotlin: `times` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.Px#times(int):
+ Method can be invoked as a binary operator from Kotlin: `times` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.Px#unaryMinus():
+ Method can be invoked as a unary operator from Kotlin: `unaryMinus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.PxPosition#minus(androidx.ui.core.IntPxPosition):
+ Method can be invoked as a binary operator from Kotlin: `minus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.PxPosition#minus(androidx.ui.core.PxPosition):
+ Method can be invoked as a binary operator from Kotlin: `minus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.PxPosition#plus(androidx.ui.core.IntPxPosition):
+ Method can be invoked as a binary operator from Kotlin: `plus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.PxPosition#plus(androidx.ui.core.PxPosition):
+ Method can be invoked as a binary operator from Kotlin: `plus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.PxPosition#unaryMinus():
+ Method can be invoked as a unary operator from Kotlin: `unaryMinus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.Sp#div(androidx.ui.core.Sp):
+ Method can be invoked as a binary operator from Kotlin: `div` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.Sp#div(float):
+ Method can be invoked as a binary operator from Kotlin: `div` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.Sp#div(int):
+ Method can be invoked as a binary operator from Kotlin: `div` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.Sp#minus(androidx.ui.core.Sp):
+ Method can be invoked as a binary operator from Kotlin: `minus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.Sp#plus(androidx.ui.core.Sp):
+ Method can be invoked as a binary operator from Kotlin: `plus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.Sp#times(float):
+ Method can be invoked as a binary operator from Kotlin: `times` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.Sp#times(int):
+ Method can be invoked as a binary operator from Kotlin: `times` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.Sp#unaryMinus():
+ Method can be invoked as a unary operator from Kotlin: `unaryMinus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.TextRange#contains(androidx.ui.core.TextRange):
+ Method can be invoked as a "in" operator from Kotlin: `contains` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.TextRange#contains(int):
+ Method can be invoked as a "in" operator from Kotlin: `contains` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.Timestamp#minus(androidx.ui.core.Duration):
+ Method can be invoked as a binary operator from Kotlin: `minus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.Timestamp#minus(androidx.ui.core.Timestamp):
+ Method can be invoked as a binary operator from Kotlin: `minus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.Timestamp#plus(androidx.ui.core.Duration):
+ Method can be invoked as a binary operator from Kotlin: `plus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.core.Velocity#unaryMinus():
+ Method can be invoked as a unary operator from Kotlin: `unaryMinus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.engine.geometry.Offset#div(float):
+ Method can be invoked as a binary operator from Kotlin: `div` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.engine.geometry.Offset#minus(androidx.ui.engine.geometry.Offset):
+ Method can be invoked as a binary operator from Kotlin: `minus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.engine.geometry.Offset#plus(androidx.ui.engine.geometry.Offset):
+ Method can be invoked as a binary operator from Kotlin: `plus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.engine.geometry.Offset#rem(float):
+ Method can be invoked as a binary operator from Kotlin: `rem` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.engine.geometry.Offset#times(float):
+ Method can be invoked as a binary operator from Kotlin: `times` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.engine.geometry.Offset#unaryMinus():
+ Method can be invoked as a unary operator from Kotlin: `unaryMinus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.engine.geometry.RRect#contains(androidx.ui.engine.geometry.Offset):
+ Method can be invoked as a "in" operator from Kotlin: `contains` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.engine.geometry.Radius#div(float):
+ Method can be invoked as a binary operator from Kotlin: `div` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.engine.geometry.Radius#minus(androidx.ui.engine.geometry.Radius):
+ Method can be invoked as a binary operator from Kotlin: `minus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.engine.geometry.Radius#plus(androidx.ui.engine.geometry.Radius):
+ Method can be invoked as a binary operator from Kotlin: `plus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.engine.geometry.Radius#rem(float):
+ Method can be invoked as a binary operator from Kotlin: `rem` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.engine.geometry.Radius#times(float):
+ Method can be invoked as a binary operator from Kotlin: `times` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.engine.geometry.Radius#unaryMinus():
+ Method can be invoked as a unary operator from Kotlin: `unaryMinus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.engine.geometry.Rect#contains(androidx.ui.engine.geometry.Offset):
+ Method can be invoked as a "in" operator from Kotlin: `contains` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.engine.geometry.Size#contains(androidx.ui.engine.geometry.Offset):
+ Method can be invoked as a "in" operator from Kotlin: `contains` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.engine.geometry.Size#div(float):
+ Method can be invoked as a binary operator from Kotlin: `div` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.engine.geometry.Size#minus(androidx.ui.engine.geometry.Offset):
+ Method can be invoked as a binary operator from Kotlin: `minus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.engine.geometry.Size#minus(androidx.ui.engine.geometry.Size):
+ Method can be invoked as a binary operator from Kotlin: `minus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.engine.geometry.Size#plus(androidx.ui.engine.geometry.Offset):
+ Method can be invoked as a binary operator from Kotlin: `plus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.engine.geometry.Size#rem(float):
+ Method can be invoked as a binary operator from Kotlin: `rem` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.engine.geometry.Size#times(float):
+ Method can be invoked as a binary operator from Kotlin: `times` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.graphics.ColorSpace.Companion#get(androidx.ui.graphics.ColorSpace.Named):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.painting.Path#contains(androidx.ui.engine.geometry.Offset):
+ Method can be invoked as a "in" operator from Kotlin: `contains` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix2#dec():
+ Method can be invoked as a pre/postfix inc/decrement operator from Kotlin: `dec` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix2#div(float):
+ Method can be invoked as a binary operator from Kotlin: `div` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix2#get(androidx.ui.vectormath64.MatrixColumn):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix2#get(androidx.ui.vectormath64.MatrixColumn, int):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix2#get(int):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix2#get(int, int):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix2#inc():
+ Method can be invoked as a pre/postfix inc/decrement operator from Kotlin: `inc` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix2#minus(float):
+ Method can be invoked as a binary operator from Kotlin: `minus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix2#plus(float):
+ Method can be invoked as a binary operator from Kotlin: `plus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix2#set(int, androidx.ui.vectormath64.Vector2):
+ Method can be invoked with an indexing operator from Kotlin: `set` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix2#set(int, int, float):
+ Method can be invoked with an indexing operator from Kotlin: `set` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix2#times(androidx.ui.vectormath64.Matrix2):
+ Method can be invoked as a binary operator from Kotlin: `times` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix2#times(androidx.ui.vectormath64.Vector2):
+ Method can be invoked as a binary operator from Kotlin: `times` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix2#times(float):
+ Method can be invoked as a binary operator from Kotlin: `times` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix2#unaryMinus():
+ Method can be invoked as a unary operator from Kotlin: `unaryMinus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix3#dec():
+ Method can be invoked as a pre/postfix inc/decrement operator from Kotlin: `dec` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix3#div(float):
+ Method can be invoked as a binary operator from Kotlin: `div` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix3#get(androidx.ui.vectormath64.MatrixColumn):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix3#get(androidx.ui.vectormath64.MatrixColumn, int):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix3#get(int):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix3#get(int, int):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix3#inc():
+ Method can be invoked as a pre/postfix inc/decrement operator from Kotlin: `inc` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix3#minus(float):
+ Method can be invoked as a binary operator from Kotlin: `minus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix3#plus(float):
+ Method can be invoked as a binary operator from Kotlin: `plus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix3#set(int, androidx.ui.vectormath64.Vector3):
+ Method can be invoked with an indexing operator from Kotlin: `set` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix3#set(int, int, float):
+ Method can be invoked with an indexing operator from Kotlin: `set` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix3#times(androidx.ui.vectormath64.Matrix3):
+ Method can be invoked as a binary operator from Kotlin: `times` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix3#times(androidx.ui.vectormath64.Vector3):
+ Method can be invoked as a binary operator from Kotlin: `times` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix3#times(float):
+ Method can be invoked as a binary operator from Kotlin: `times` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix3#unaryMinus():
+ Method can be invoked as a unary operator from Kotlin: `unaryMinus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix4#dec():
+ Method can be invoked as a pre/postfix inc/decrement operator from Kotlin: `dec` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix4#div(float):
+ Method can be invoked as a binary operator from Kotlin: `div` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix4#get(androidx.ui.vectormath64.MatrixColumn):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix4#get(androidx.ui.vectormath64.MatrixColumn, int):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix4#get(int):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix4#get(int, int):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix4#inc():
+ Method can be invoked as a pre/postfix inc/decrement operator from Kotlin: `inc` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix4#minus(float):
+ Method can be invoked as a binary operator from Kotlin: `minus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix4#plus(float):
+ Method can be invoked as a binary operator from Kotlin: `plus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix4#set(int, androidx.ui.vectormath64.Vector4):
+ Method can be invoked with an indexing operator from Kotlin: `set` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix4#set(int, int, float):
+ Method can be invoked with an indexing operator from Kotlin: `set` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix4#times(androidx.ui.vectormath64.Matrix4):
+ Method can be invoked as a binary operator from Kotlin: `times` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix4#times(androidx.ui.vectormath64.Vector4):
+ Method can be invoked as a binary operator from Kotlin: `times` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix4#times(float):
+ Method can be invoked as a binary operator from Kotlin: `times` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix4#timesAssign(androidx.ui.vectormath64.Matrix4):
+ Method can be invoked as a compound assignment operator from Kotlin: `timesAssign` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Matrix4#unaryMinus():
+ Method can be invoked as a unary operator from Kotlin: `unaryMinus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector2#dec():
+ Method can be invoked as a pre/postfix inc/decrement operator from Kotlin: `dec` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector2#div(androidx.ui.vectormath64.Vector2):
+ Method can be invoked as a binary operator from Kotlin: `div` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector2#div(float):
+ Method can be invoked as a binary operator from Kotlin: `div` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector2#get(androidx.ui.vectormath64.VectorComponent):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector2#get(androidx.ui.vectormath64.VectorComponent, androidx.ui.vectormath64.VectorComponent):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector2#get(int):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector2#get(int, int):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector2#inc():
+ Method can be invoked as a pre/postfix inc/decrement operator from Kotlin: `inc` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector2#minus(androidx.ui.vectormath64.Vector2):
+ Method can be invoked as a binary operator from Kotlin: `minus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector2#minus(float):
+ Method can be invoked as a binary operator from Kotlin: `minus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector2#plus(androidx.ui.vectormath64.Vector2):
+ Method can be invoked as a binary operator from Kotlin: `plus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector2#plus(float):
+ Method can be invoked as a binary operator from Kotlin: `plus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector2#set(androidx.ui.vectormath64.VectorComponent, androidx.ui.vectormath64.VectorComponent, float):
+ Method can be invoked with an indexing operator from Kotlin: `set` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector2#set(androidx.ui.vectormath64.VectorComponent, float):
+ Method can be invoked with an indexing operator from Kotlin: `set` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector2#set(int, float):
+ Method can be invoked with an indexing operator from Kotlin: `set` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector2#set(int, int, float):
+ Method can be invoked with an indexing operator from Kotlin: `set` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector2#times(androidx.ui.vectormath64.Vector2):
+ Method can be invoked as a binary operator from Kotlin: `times` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector2#times(float):
+ Method can be invoked as a binary operator from Kotlin: `times` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector2#unaryMinus():
+ Method can be invoked as a unary operator from Kotlin: `unaryMinus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector3#dec():
+ Method can be invoked as a pre/postfix inc/decrement operator from Kotlin: `dec` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector3#div(androidx.ui.vectormath64.Vector2):
+ Method can be invoked as a binary operator from Kotlin: `div` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector3#div(androidx.ui.vectormath64.Vector3):
+ Method can be invoked as a binary operator from Kotlin: `div` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector3#div(float):
+ Method can be invoked as a binary operator from Kotlin: `div` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector3#get(androidx.ui.vectormath64.VectorComponent):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector3#get(androidx.ui.vectormath64.VectorComponent, androidx.ui.vectormath64.VectorComponent):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector3#get(androidx.ui.vectormath64.VectorComponent, androidx.ui.vectormath64.VectorComponent, androidx.ui.vectormath64.VectorComponent):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector3#get(int):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector3#get(int, int):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector3#get(int, int, int):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector3#inc():
+ Method can be invoked as a pre/postfix inc/decrement operator from Kotlin: `inc` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector3#minus(androidx.ui.vectormath64.Vector2):
+ Method can be invoked as a binary operator from Kotlin: `minus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector3#minus(androidx.ui.vectormath64.Vector3):
+ Method can be invoked as a binary operator from Kotlin: `minus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector3#minus(float):
+ Method can be invoked as a binary operator from Kotlin: `minus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector3#plus(androidx.ui.vectormath64.Vector2):
+ Method can be invoked as a binary operator from Kotlin: `plus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector3#plus(androidx.ui.vectormath64.Vector3):
+ Method can be invoked as a binary operator from Kotlin: `plus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector3#plus(float):
+ Method can be invoked as a binary operator from Kotlin: `plus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector3#set(androidx.ui.vectormath64.VectorComponent, androidx.ui.vectormath64.VectorComponent, androidx.ui.vectormath64.VectorComponent, float):
+ Method can be invoked with an indexing operator from Kotlin: `set` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector3#set(androidx.ui.vectormath64.VectorComponent, androidx.ui.vectormath64.VectorComponent, float):
+ Method can be invoked with an indexing operator from Kotlin: `set` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector3#set(androidx.ui.vectormath64.VectorComponent, float):
+ Method can be invoked with an indexing operator from Kotlin: `set` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector3#set(int, float):
+ Method can be invoked with an indexing operator from Kotlin: `set` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector3#set(int, int, float):
+ Method can be invoked with an indexing operator from Kotlin: `set` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector3#set(int, int, int, float):
+ Method can be invoked with an indexing operator from Kotlin: `set` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector3#times(androidx.ui.vectormath64.Vector2):
+ Method can be invoked as a binary operator from Kotlin: `times` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector3#times(androidx.ui.vectormath64.Vector3):
+ Method can be invoked as a binary operator from Kotlin: `times` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector3#times(float):
+ Method can be invoked as a binary operator from Kotlin: `times` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector3#unaryMinus():
+ Method can be invoked as a unary operator from Kotlin: `unaryMinus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector4#dec():
+ Method can be invoked as a pre/postfix inc/decrement operator from Kotlin: `dec` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector4#div(androidx.ui.vectormath64.Vector2):
+ Method can be invoked as a binary operator from Kotlin: `div` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector4#div(androidx.ui.vectormath64.Vector3):
+ Method can be invoked as a binary operator from Kotlin: `div` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector4#div(androidx.ui.vectormath64.Vector4):
+ Method can be invoked as a binary operator from Kotlin: `div` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector4#div(float):
+ Method can be invoked as a binary operator from Kotlin: `div` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector4#get(androidx.ui.vectormath64.VectorComponent):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector4#get(androidx.ui.vectormath64.VectorComponent, androidx.ui.vectormath64.VectorComponent):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector4#get(androidx.ui.vectormath64.VectorComponent, androidx.ui.vectormath64.VectorComponent, androidx.ui.vectormath64.VectorComponent):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector4#get(androidx.ui.vectormath64.VectorComponent, androidx.ui.vectormath64.VectorComponent, androidx.ui.vectormath64.VectorComponent, androidx.ui.vectormath64.VectorComponent):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector4#get(int):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector4#get(int, int):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector4#get(int, int, int):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector4#get(int, int, int, int):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector4#inc():
+ Method can be invoked as a pre/postfix inc/decrement operator from Kotlin: `inc` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector4#minus(androidx.ui.vectormath64.Vector2):
+ Method can be invoked as a binary operator from Kotlin: `minus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector4#minus(androidx.ui.vectormath64.Vector3):
+ Method can be invoked as a binary operator from Kotlin: `minus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector4#minus(androidx.ui.vectormath64.Vector4):
+ Method can be invoked as a binary operator from Kotlin: `minus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector4#minus(float):
+ Method can be invoked as a binary operator from Kotlin: `minus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector4#plus(androidx.ui.vectormath64.Vector2):
+ Method can be invoked as a binary operator from Kotlin: `plus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector4#plus(androidx.ui.vectormath64.Vector3):
+ Method can be invoked as a binary operator from Kotlin: `plus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector4#plus(androidx.ui.vectormath64.Vector4):
+ Method can be invoked as a binary operator from Kotlin: `plus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector4#plus(float):
+ Method can be invoked as a binary operator from Kotlin: `plus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector4#set(androidx.ui.vectormath64.VectorComponent, androidx.ui.vectormath64.VectorComponent, androidx.ui.vectormath64.VectorComponent, androidx.ui.vectormath64.VectorComponent, float):
+ Method can be invoked with an indexing operator from Kotlin: `set` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector4#set(androidx.ui.vectormath64.VectorComponent, androidx.ui.vectormath64.VectorComponent, androidx.ui.vectormath64.VectorComponent, float):
+ Method can be invoked with an indexing operator from Kotlin: `set` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector4#set(androidx.ui.vectormath64.VectorComponent, androidx.ui.vectormath64.VectorComponent, float):
+ Method can be invoked with an indexing operator from Kotlin: `set` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector4#set(androidx.ui.vectormath64.VectorComponent, float):
+ Method can be invoked with an indexing operator from Kotlin: `set` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector4#set(int, float):
+ Method can be invoked with an indexing operator from Kotlin: `set` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector4#set(int, int, float):
+ Method can be invoked with an indexing operator from Kotlin: `set` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector4#set(int, int, int, float):
+ Method can be invoked with an indexing operator from Kotlin: `set` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector4#set(int, int, int, int, float):
+ Method can be invoked with an indexing operator from Kotlin: `set` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector4#times(androidx.ui.vectormath64.Vector2):
+ Method can be invoked as a binary operator from Kotlin: `times` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector4#times(androidx.ui.vectormath64.Vector3):
+ Method can be invoked as a binary operator from Kotlin: `times` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector4#times(androidx.ui.vectormath64.Vector4):
+ Method can be invoked as a binary operator from Kotlin: `times` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector4#times(float):
+ Method can be invoked as a binary operator from Kotlin: `times` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.vectormath64.Vector4#unaryMinus():
+ Method can be invoked as a unary operator from Kotlin: `unaryMinus` (this is usually desirable; just make sure it makes sense for this type of object)
+
+
+MethodNameUnits: androidx.ui.core.Durations#inSeconds(androidx.ui.core.Duration):
+ Returned time values must be in milliseconds, was `inSeconds`
+
+
+UniqueKotlinOperator: androidx.ui.vectormath64.Matrix4#times(androidx.ui.vectormath64.Matrix4):
+ Only one of `times` and `timesAssign` methods should be present for Kotlin
+UniqueKotlinOperator: androidx.ui.vectormath64.Matrix4#times(androidx.ui.vectormath64.Vector4):
+ Only one of `times` and `timesAssign` methods should be present for Kotlin
+UniqueKotlinOperator: androidx.ui.vectormath64.Matrix4#times(float):
+ Only one of `times` and `timesAssign` methods should be present for Kotlin
diff --git a/ui/framework/api/1.0.0-alpha01.txt b/ui/framework/api/1.0.0-alpha01.txt
index 2efa517..1990900 100644
--- a/ui/framework/api/1.0.0-alpha01.txt
+++ b/ui/framework/api/1.0.0-alpha01.txt
@@ -135,8 +135,8 @@
public final class TextKt {
ctor public TextKt();
method public static void CurrentTextStyleProvider(androidx.ui.painting.TextStyle value, kotlin.jvm.functions.Function0<kotlin.Unit> children);
- method public static void Text(androidx.ui.painting.ParagraphStyle? paragraphStyle = null, boolean softWrap = true, androidx.ui.rendering.paragraph.TextOverflow overflow = androidx.ui.core.TextKt.DefaultOverflow, float textScaleFactor = 1.0f, Integer? maxLines = androidx.ui.core.TextKt.DefaultMaxLines, androidx.ui.graphics.Color selectionColor = androidx.ui.core.TextKt.DefaultSelectionColor, kotlin.jvm.functions.Function1<? super androidx.ui.core.TextSpanScope,kotlin.Unit> child);
- method public static void Text(String text, androidx.ui.painting.TextStyle? style = null, androidx.ui.painting.ParagraphStyle? paragraphStyle = null, boolean softWrap = true, androidx.ui.rendering.paragraph.TextOverflow overflow = androidx.ui.core.TextKt.DefaultOverflow, Integer? maxLines = androidx.ui.core.TextKt.DefaultMaxLines);
+ method public static void Text(androidx.ui.painting.TextStyle? style = null, androidx.ui.painting.ParagraphStyle? paragraphStyle = null, boolean softWrap = true, androidx.ui.rendering.paragraph.TextOverflow overflow = androidx.ui.core.TextKt.DefaultOverflow, float textScaleFactor = 1.0f, Integer? maxLines = androidx.ui.core.TextKt.DefaultMaxLines, androidx.ui.graphics.Color selectionColor = androidx.ui.core.TextKt.DefaultSelectionColor, kotlin.jvm.functions.Function1<? super androidx.ui.core.TextSpanScope,kotlin.Unit> child);
+ method public static void Text(String text, androidx.ui.painting.TextStyle? style = null, androidx.ui.painting.ParagraphStyle? paragraphStyle = null, boolean softWrap = true, androidx.ui.rendering.paragraph.TextOverflow overflow = androidx.ui.core.TextKt.DefaultOverflow, float textScaleFactor = 1.0f, Integer? maxLines = androidx.ui.core.TextKt.DefaultMaxLines, androidx.ui.graphics.Color selectionColor = androidx.ui.core.TextKt.DefaultSelectionColor);
method public static void Text(androidx.ui.painting.AnnotatedString text, androidx.ui.painting.TextStyle? style = null, androidx.ui.painting.ParagraphStyle? paragraphStyle = null, boolean softWrap = true, androidx.ui.rendering.paragraph.TextOverflow overflow = androidx.ui.core.TextKt.DefaultOverflow, float textScaleFactor = 1.0f, Integer? maxLines = androidx.ui.core.TextKt.DefaultMaxLines, androidx.ui.graphics.Color selectionColor = androidx.ui.core.TextKt.DefaultSelectionColor);
method public static androidx.compose.Effect<androidx.ui.painting.TextStyle> currentTextStyle();
}
diff --git a/ui/framework/api/api_lint.ignore b/ui/framework/api/api_lint.ignore
new file mode 100644
index 0000000..81840ff
--- /dev/null
+++ b/ui/framework/api/api_lint.ignore
@@ -0,0 +1,25 @@
+// Baseline format: 1.0
+AutoBoxing: androidx.ui.core.TextKt#Text(String, androidx.ui.painting.TextStyle, androidx.ui.painting.ParagraphStyle, boolean, androidx.ui.rendering.paragraph.TextOverflow, float, Integer, androidx.ui.graphics.Color) parameter #6:
+ Must avoid boxed primitives (`java.lang.Integer`)
+AutoBoxing: androidx.ui.core.TextKt#Text(androidx.ui.painting.AnnotatedString, androidx.ui.painting.TextStyle, androidx.ui.painting.ParagraphStyle, boolean, androidx.ui.rendering.paragraph.TextOverflow, float, Integer, androidx.ui.graphics.Color) parameter #6:
+ Must avoid boxed primitives (`java.lang.Integer`)
+AutoBoxing: androidx.ui.core.TextKt#Text(androidx.ui.painting.TextStyle, androidx.ui.painting.ParagraphStyle, boolean, androidx.ui.rendering.paragraph.TextOverflow, float, Integer, androidx.ui.graphics.Color, kotlin.jvm.functions.Function1<? super androidx.ui.core.TextSpanScope,kotlin.Unit>) parameter #5:
+ Must avoid boxed primitives (`java.lang.Integer`)
+
+
+BannedThrow: androidx.ui.core.vectorgraphics.PathCommandKt#toPathCommand(char):
+ Methods must not mention RuntimeException subclasses in throws clauses (was `java.lang.IllegalArgumentException`)
+BannedThrow: androidx.ui.core.vectorgraphics.PathParser#parsePathString(String):
+ Methods must not mention RuntimeException subclasses in throws clauses (was `java.lang.IllegalArgumentException`)
+
+
+CallbackName: androidx.ui.core.gesture.DragObserver:
+ Class should be named DragCallback
+
+
+DocumentExceptions: androidx.ui.core.vectorgraphics.BrushKt#obtainBrush(Object):
+ Method BrushKt.obtainBrush appears to be throwing java.lang.IllegalArgumentException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+
+
+KotlinOperator: androidx.ui.core.LayoutReceiver#get(java.util.List<? extends androidx.ui.core.Measurable>, kotlin.jvm.functions.Function0<kotlin.Unit>):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
diff --git a/ui/framework/api/current.txt b/ui/framework/api/current.txt
index 2efa517..1990900 100644
--- a/ui/framework/api/current.txt
+++ b/ui/framework/api/current.txt
@@ -135,8 +135,8 @@
public final class TextKt {
ctor public TextKt();
method public static void CurrentTextStyleProvider(androidx.ui.painting.TextStyle value, kotlin.jvm.functions.Function0<kotlin.Unit> children);
- method public static void Text(androidx.ui.painting.ParagraphStyle? paragraphStyle = null, boolean softWrap = true, androidx.ui.rendering.paragraph.TextOverflow overflow = androidx.ui.core.TextKt.DefaultOverflow, float textScaleFactor = 1.0f, Integer? maxLines = androidx.ui.core.TextKt.DefaultMaxLines, androidx.ui.graphics.Color selectionColor = androidx.ui.core.TextKt.DefaultSelectionColor, kotlin.jvm.functions.Function1<? super androidx.ui.core.TextSpanScope,kotlin.Unit> child);
- method public static void Text(String text, androidx.ui.painting.TextStyle? style = null, androidx.ui.painting.ParagraphStyle? paragraphStyle = null, boolean softWrap = true, androidx.ui.rendering.paragraph.TextOverflow overflow = androidx.ui.core.TextKt.DefaultOverflow, Integer? maxLines = androidx.ui.core.TextKt.DefaultMaxLines);
+ method public static void Text(androidx.ui.painting.TextStyle? style = null, androidx.ui.painting.ParagraphStyle? paragraphStyle = null, boolean softWrap = true, androidx.ui.rendering.paragraph.TextOverflow overflow = androidx.ui.core.TextKt.DefaultOverflow, float textScaleFactor = 1.0f, Integer? maxLines = androidx.ui.core.TextKt.DefaultMaxLines, androidx.ui.graphics.Color selectionColor = androidx.ui.core.TextKt.DefaultSelectionColor, kotlin.jvm.functions.Function1<? super androidx.ui.core.TextSpanScope,kotlin.Unit> child);
+ method public static void Text(String text, androidx.ui.painting.TextStyle? style = null, androidx.ui.painting.ParagraphStyle? paragraphStyle = null, boolean softWrap = true, androidx.ui.rendering.paragraph.TextOverflow overflow = androidx.ui.core.TextKt.DefaultOverflow, float textScaleFactor = 1.0f, Integer? maxLines = androidx.ui.core.TextKt.DefaultMaxLines, androidx.ui.graphics.Color selectionColor = androidx.ui.core.TextKt.DefaultSelectionColor);
method public static void Text(androidx.ui.painting.AnnotatedString text, androidx.ui.painting.TextStyle? style = null, androidx.ui.painting.ParagraphStyle? paragraphStyle = null, boolean softWrap = true, androidx.ui.rendering.paragraph.TextOverflow overflow = androidx.ui.core.TextKt.DefaultOverflow, float textScaleFactor = 1.0f, Integer? maxLines = androidx.ui.core.TextKt.DefaultMaxLines, androidx.ui.graphics.Color selectionColor = androidx.ui.core.TextKt.DefaultSelectionColor);
method public static androidx.compose.Effect<androidx.ui.painting.TextStyle> currentTextStyle();
}
diff --git a/ui/framework/src/main/java/androidx/ui/core/Text.kt b/ui/framework/src/main/java/androidx/ui/core/Text.kt
index 16bee07..6028f5b 100644
--- a/ui/framework/src/main/java/androidx/ui/core/Text.kt
+++ b/ui/framework/src/main/java/androidx/ui/core/Text.kt
@@ -58,6 +58,14 @@
*/
@Composable
fun Text(
+ /**
+ * Style configuration that applies at character level such as color, font etc.
+ */
+ style: TextStyle? = null,
+ /**
+ * Style configuration that applies only to paragraphs such as text alignment, or text
+ * direction.
+ */
paragraphStyle: ParagraphStyle? = null,
/**
* Whether the text should break at soft line breaks.
@@ -91,20 +99,9 @@
compose(rootTextSpan, ref, child)
+onDispose { disposeComposition(rootTextSpan, ref) }
- // TODO(haoyuchang): this trick should be removed. right now those attributes not in
- // ParagraphStyle won't work
- val textSpan = if (rootTextSpan.children.size == 1 &&
- rootTextSpan.style == null &&
- rootTextSpan.text == null
- ) {
- rootTextSpan.children[0]
- } else {
- rootTextSpan
- }
-
Text(
- text = textSpan.toAnnotatedString(includeRootStyle = false),
- style = textSpan.style,
+ text = rootTextSpan.toAnnotatedString(),
+ style = style,
paragraphStyle = paragraphStyle,
softWrap = softWrap,
overflow = overflow,
@@ -127,7 +124,9 @@
paragraphStyle: ParagraphStyle? = null,
softWrap: Boolean = DefaultSoftWrap,
overflow: TextOverflow = DefaultOverflow,
- maxLines: Int? = DefaultMaxLines
+ textScaleFactor: Float = 1.0f,
+ maxLines: Int? = DefaultMaxLines,
+ selectionColor: Color = DefaultSelectionColor
) {
Text(
text = AnnotatedString(text),
@@ -135,9 +134,9 @@
paragraphStyle = paragraphStyle,
softWrap = softWrap,
overflow = overflow,
- textScaleFactor = 1.0f,
+ textScaleFactor = textScaleFactor,
maxLines = maxLines,
- selectionColor = DefaultSelectionColor
+ selectionColor = selectionColor
)
}
diff --git a/ui/layout/api/api_lint.ignore b/ui/layout/api/api_lint.ignore
new file mode 100644
index 0000000..e1bb2f7
--- /dev/null
+++ b/ui/layout/api/api_lint.ignore
@@ -0,0 +1,7 @@
+// Baseline format: 1.0
+DocumentExceptions: androidx.ui.layout.FlexChildren#expanded(float, kotlin.jvm.functions.Function0<kotlin.Unit>):
+ Method FlexChildren.expanded appears to be throwing java.lang.IllegalArgumentException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.ui.layout.FlexChildren#flexible(float, kotlin.jvm.functions.Function0<kotlin.Unit>):
+ Method FlexChildren.flexible appears to be throwing java.lang.IllegalArgumentException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.ui.layout.ScrollerKt#VerticalScroller(androidx.ui.layout.ScrollerPosition, kotlin.jvm.functions.Function2<? super androidx.ui.core.Px,? super androidx.ui.core.Px,kotlin.Unit>, kotlin.jvm.functions.Function0<kotlin.Unit>):
+ Method ScrollerKt.VerticalScroller appears to be throwing java.lang.IllegalStateException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
diff --git a/ui/material/api/1.0.0-alpha01.txt b/ui/material/api/1.0.0-alpha01.txt
index 0476ae4..fb7f861 100644
--- a/ui/material/api/1.0.0-alpha01.txt
+++ b/ui/material/api/1.0.0-alpha01.txt
@@ -1,24 +1,4 @@
// Signature format: 3.0
-package {
-
- public final class ProgressIndicatorKt {
- ctor public ProgressIndicatorKt();
- method public static void CircularProgressIndicator(@FloatRange(from=0.0, to=1.0) float progress, androidx.ui.graphics.Color color = +themeColor({
- primary
-}));
- method public static void CircularProgressIndicator(androidx.ui.graphics.Color color = +themeColor({
- primary
-}));
- method public static void LinearProgressIndicator(@FloatRange(from=0.0, to=1.0) float progress, androidx.ui.graphics.Color color = +themeColor({
- primary
-}));
- method public static void LinearProgressIndicator(androidx.ui.graphics.Color color = +themeColor({
- primary
-}));
- }
-
-}
-
package androidx.ui.baseui {
public final class ClickableKt {
@@ -322,6 +302,22 @@
method public androidx.ui.painting.TextStyle getSubtitle2();
}
+ public final class ProgressIndicatorKt {
+ ctor public ProgressIndicatorKt();
+ method public static void CircularProgressIndicator(@FloatRange(from=0.0, to=1.0) float progress, androidx.ui.graphics.Color color = +themeColor({
+ primary
+}));
+ method public static void CircularProgressIndicator(androidx.ui.graphics.Color color = +themeColor({
+ primary
+}));
+ method public static void LinearProgressIndicator(@FloatRange(from=0.0, to=1.0) float progress, androidx.ui.graphics.Color color = +themeColor({
+ primary
+}));
+ method public static void LinearProgressIndicator(androidx.ui.graphics.Color color = +themeColor({
+ primary
+}));
+ }
+
public final class RadioButtonKt {
ctor public RadioButtonKt();
method public static void RadioButton(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit>? onSelect, androidx.ui.graphics.Color color = +themeColor({
diff --git a/ui/material/api/api_lint.ignore b/ui/material/api/api_lint.ignore
new file mode 100644
index 0000000..e5bfe7a
--- /dev/null
+++ b/ui/material/api/api_lint.ignore
@@ -0,0 +1,29 @@
+// Baseline format: 1.0
+DocumentExceptions: androidx.ui.baseui.DeterminateProgressIndicatorKt#DeterminateProgressIndicator(float, kotlin.jvm.functions.Function0<kotlin.Unit>):
+ Method DeterminateProgressIndicatorKt.DeterminateProgressIndicator appears to be throwing java.lang.IllegalArgumentException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.ui.baseui.shape.corner.CornerSizeKt#CornerSize(float):
+ Method CornerSizeKt.CornerSize appears to be throwing java.lang.IllegalArgumentException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+
+
+KotlinOperator: androidx.ui.material.borders.BorderRadius#div(float):
+ Method can be invoked as a binary operator from Kotlin: `div` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.material.borders.BorderRadius#minus(androidx.ui.material.borders.BorderRadiusGeometry):
+ Method can be invoked as a binary operator from Kotlin: `minus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.material.borders.BorderRadius#plus(androidx.ui.material.borders.BorderRadius):
+ Method can be invoked as a binary operator from Kotlin: `plus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.material.borders.BorderRadius#rem(float):
+ Method can be invoked as a binary operator from Kotlin: `rem` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.material.borders.BorderRadius#times(float):
+ Method can be invoked as a binary operator from Kotlin: `times` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.material.borders.BorderRadius#unaryMinus():
+ Method can be invoked as a unary operator from Kotlin: `unaryMinus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.material.borders.BorderRadiusGeometry#div(float):
+ Method can be invoked as a binary operator from Kotlin: `div` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.material.borders.BorderRadiusGeometry#rem(float):
+ Method can be invoked as a binary operator from Kotlin: `rem` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.material.borders.BorderRadiusGeometry#times(float):
+ Method can be invoked as a binary operator from Kotlin: `times` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.material.borders.BorderRadiusGeometry#unaryMinus():
+ Method can be invoked as a unary operator from Kotlin: `unaryMinus` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.material.borders.ShapeBorder#plus(androidx.ui.material.borders.ShapeBorder):
+ Method can be invoked as a binary operator from Kotlin: `plus` (this is usually desirable; just make sure it makes sense for this type of object)
diff --git a/ui/material/api/current.txt b/ui/material/api/current.txt
index 0476ae4..fb7f861 100644
--- a/ui/material/api/current.txt
+++ b/ui/material/api/current.txt
@@ -1,24 +1,4 @@
// Signature format: 3.0
-package {
-
- public final class ProgressIndicatorKt {
- ctor public ProgressIndicatorKt();
- method public static void CircularProgressIndicator(@FloatRange(from=0.0, to=1.0) float progress, androidx.ui.graphics.Color color = +themeColor({
- primary
-}));
- method public static void CircularProgressIndicator(androidx.ui.graphics.Color color = +themeColor({
- primary
-}));
- method public static void LinearProgressIndicator(@FloatRange(from=0.0, to=1.0) float progress, androidx.ui.graphics.Color color = +themeColor({
- primary
-}));
- method public static void LinearProgressIndicator(androidx.ui.graphics.Color color = +themeColor({
- primary
-}));
- }
-
-}
-
package androidx.ui.baseui {
public final class ClickableKt {
@@ -322,6 +302,22 @@
method public androidx.ui.painting.TextStyle getSubtitle2();
}
+ public final class ProgressIndicatorKt {
+ ctor public ProgressIndicatorKt();
+ method public static void CircularProgressIndicator(@FloatRange(from=0.0, to=1.0) float progress, androidx.ui.graphics.Color color = +themeColor({
+ primary
+}));
+ method public static void CircularProgressIndicator(androidx.ui.graphics.Color color = +themeColor({
+ primary
+}));
+ method public static void LinearProgressIndicator(@FloatRange(from=0.0, to=1.0) float progress, androidx.ui.graphics.Color color = +themeColor({
+ primary
+}));
+ method public static void LinearProgressIndicator(androidx.ui.graphics.Color color = +themeColor({
+ primary
+}));
+ }
+
public final class RadioButtonKt {
ctor public RadioButtonKt();
method public static void RadioButton(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit>? onSelect, androidx.ui.graphics.Color color = +themeColor({
diff --git a/ui/material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/ProgressIndicatorActivity.kt b/ui/material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/ProgressIndicatorActivity.kt
index 75dbcc5..4ae5f23 100644
--- a/ui/material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/ProgressIndicatorActivity.kt
+++ b/ui/material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/ProgressIndicatorActivity.kt
@@ -16,8 +16,6 @@
package androidx.ui.material.demos
-import CircularProgressIndicator
-import LinearProgressIndicator
import android.app.Activity
import android.os.Bundle
import android.os.Handler
@@ -25,6 +23,8 @@
import androidx.ui.layout.FlexColumn
import androidx.ui.layout.MainAxisAlignment.Companion.SpaceEvenly
import androidx.ui.layout.Row
+import androidx.ui.material.CircularProgressIndicator
+import androidx.ui.material.LinearProgressIndicator
import androidx.ui.material.MaterialTheme
import androidx.ui.graphics.Color
import androidx.compose.Composable
diff --git a/ui/material/src/androidTest/java/androidx/ui/material/ProgressIndicatorTest.kt b/ui/material/src/androidTest/java/androidx/ui/material/ProgressIndicatorTest.kt
index 93bbc13..087fd5e 100644
--- a/ui/material/src/androidTest/java/androidx/ui/material/ProgressIndicatorTest.kt
+++ b/ui/material/src/androidTest/java/androidx/ui/material/ProgressIndicatorTest.kt
@@ -15,8 +15,6 @@
*/
package androidx.ui.material
-import CircularProgressIndicator
-import LinearProgressIndicator
import androidx.compose.composer
import androidx.compose.Model
import androidx.test.filters.LargeTest
diff --git a/ui/material/src/main/java/androidx/ui/material/ProgressIndicator.kt b/ui/material/src/main/java/androidx/ui/material/ProgressIndicator.kt
index 76ec1b3..96ab387 100644
--- a/ui/material/src/main/java/androidx/ui/material/ProgressIndicator.kt
+++ b/ui/material/src/main/java/androidx/ui/material/ProgressIndicator.kt
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+package androidx.ui.material
+
import androidx.animation.CubicBezierEasing
import androidx.animation.FloatPropKey
import androidx.animation.Infinite
@@ -39,11 +41,8 @@
import androidx.ui.engine.geometry.Rect
import androidx.ui.graphics.Color
import androidx.ui.layout.Container
-import androidx.ui.layout.EdgeInsets
import androidx.ui.layout.Padding
import androidx.ui.layout.Wrap
-import androidx.ui.material.MaterialColors
-import androidx.ui.material.themeColor
import androidx.ui.painting.Canvas
import androidx.ui.painting.Paint
import androidx.ui.painting.PaintingStyle
@@ -285,7 +284,7 @@
@Composable
private fun CircularIndicatorContainer(@Children children: @Composable() () -> Unit) {
Wrap {
- Padding(padding = EdgeInsets(CircularIndicatorPadding)) {
+ Padding(CircularIndicatorPadding) {
Container(width = CircularIndicatorDiameter, height = CircularIndicatorDiameter) {
children()
}
diff --git a/ui/platform/api/api_lint.ignore b/ui/platform/api/api_lint.ignore
new file mode 100644
index 0000000..9bcd6f7
--- /dev/null
+++ b/ui/platform/api/api_lint.ignore
@@ -0,0 +1,27 @@
+// Baseline format: 1.0
+AutoBoxing: androidx.ui.core.semantics.SemanticsConfiguration#getScrollExtentMax():
+ Must avoid boxed primitives (`java.lang.Float`)
+AutoBoxing: androidx.ui.core.semantics.SemanticsConfiguration#getScrollExtentMin():
+ Must avoid boxed primitives (`java.lang.Float`)
+AutoBoxing: androidx.ui.core.semantics.SemanticsConfiguration#getScrollPosition():
+ Must avoid boxed primitives (`java.lang.Float`)
+AutoBoxing: androidx.ui.core.semantics.SemanticsConfiguration#setScrollExtentMax(Float) parameter #0:
+ Must avoid boxed primitives (`java.lang.Float`)
+AutoBoxing: androidx.ui.core.semantics.SemanticsConfiguration#setScrollExtentMin(Float) parameter #0:
+ Must avoid boxed primitives (`java.lang.Float`)
+AutoBoxing: androidx.ui.core.semantics.SemanticsConfiguration#setScrollPosition(Float) parameter #0:
+ Must avoid boxed primitives (`java.lang.Float`)
+
+
+DocumentExceptions: androidx.ui.core.AndroidCraneView#onAttach(androidx.ui.core.ComponentNode):
+ Method AndroidCraneView.onAttach appears to be throwing java.lang.IllegalStateException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.ui.core.ComponentNodesKt#childToLocal(androidx.ui.core.LayoutNode, androidx.ui.core.LayoutNode, androidx.ui.core.PxPosition):
+ Method ComponentNodesKt.childToLocal appears to be throwing java.lang.IllegalStateException; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+
+
+EndsWithImpl: androidx.ui.core.SemanticsTreeNodeImpl:
+ Don't expose your implementation details: `SemanticsTreeNodeImpl` ends with `Impl`
+
+
+KotlinOperator: androidx.ui.core.ComponentNode#get(int):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
diff --git a/ui/test/api/api_lint.ignore b/ui/test/api/api_lint.ignore
new file mode 100644
index 0000000..8fd9fca
--- /dev/null
+++ b/ui/test/api/api_lint.ignore
@@ -0,0 +1,15 @@
+// Baseline format: 1.0
+DocumentExceptions: androidx.ui.test.ActionsKt#doClick(androidx.ui.test.SemanticsNodeInteraction):
+ Method ActionsKt.doClick appears to be throwing java.lang.AssertionError; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.ui.test.AssertionsKt#assertCountEquals(java.util.List<androidx.ui.test.SemanticsNodeInteraction>, int):
+ Method AssertionsKt.assertCountEquals appears to be throwing java.lang.AssertionError; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.ui.test.AssertionsKt#assertNoLongerExists(androidx.ui.test.SemanticsNodeInteraction):
+ Method AssertionsKt.assertNoLongerExists appears to be throwing java.lang.AssertionError; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.ui.test.AssertionsKt#assertStillExists(androidx.ui.test.SemanticsNodeInteraction):
+ Method AssertionsKt.assertStillExists appears to be throwing java.lang.AssertionError; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+DocumentExceptions: androidx.ui.test.GoldenSemanticsKt#assertEquals(androidx.ui.core.semantics.SemanticsConfiguration, androidx.ui.core.semantics.SemanticsConfiguration):
+ Method GoldenSemanticsKt.assertEquals appears to be throwing java.lang.AssertionError; this should be listed in the documentation; see https://android.github.io/kotlin-guides/interop.html#document-exceptions
+
+
+ForbiddenSuperClass: androidx.ui.test.android.DefaultTestActivity:
+ DefaultTestActivity should not extend `Activity`. Activity subclasses are impossible to compose. Expose a composable API instead.
diff --git a/ui/text/api/1.0.0-alpha01.txt b/ui/text/api/1.0.0-alpha01.txt
index 018dc87..c70c422 100644
--- a/ui/text/api/1.0.0-alpha01.txt
+++ b/ui/text/api/1.0.0-alpha01.txt
@@ -66,7 +66,6 @@
public final class Paragraph {
method public float getBaseline();
method public androidx.ui.engine.geometry.Rect getCursorRect(int offset);
- method public androidx.ui.painting.TextStyle getDefaultTextStyle();
method public boolean getDidExceedMaxLines();
method public float getHeight();
method public float getLineHeight(int lineIndex);
@@ -75,11 +74,8 @@
method public float getLineWidth(int lineIndex);
method public float getMaxIntrinsicWidth();
method public float getMinIntrinsicWidth();
- method public androidx.ui.engine.text.ParagraphStyle getParagraphStyle();
method public androidx.ui.painting.Path getPathForRange(int start, int end);
method public int getPositionForOffset(androidx.ui.engine.geometry.Offset offset);
- method public String getText();
- method public java.util.List<androidx.ui.painting.AnnotatedString.Item<androidx.ui.painting.TextStyle>> getTextStyles();
method public float getWidth();
method public androidx.ui.services.text_editing.TextRange getWordBoundary(int offset);
method public void layout(androidx.ui.engine.text.ParagraphConstraints constraints);
@@ -100,39 +96,23 @@
}
public final class ParagraphStyle {
- ctor public ParagraphStyle(androidx.ui.engine.text.TextAlign? textAlign, androidx.ui.engine.text.TextDirection? textDirection, androidx.ui.engine.text.TextIndent? textIndent, Float? lineHeight, androidx.ui.engine.text.FontWeight? fontWeight, androidx.ui.engine.text.FontStyle? fontStyle, Integer? maxLines, androidx.ui.engine.text.font.FontFamily? fontFamily, Float? fontSize, androidx.ui.engine.text.FontSynthesis? fontSynthesis, Boolean? ellipsis, androidx.ui.engine.window.Locale? locale);
+ ctor public ParagraphStyle(androidx.ui.engine.text.TextAlign? textAlign, androidx.ui.engine.text.TextDirection? textDirection, androidx.ui.engine.text.TextIndent? textIndent, Float? lineHeight, Integer? maxLines, Boolean? ellipsis);
ctor public ParagraphStyle();
method public androidx.ui.engine.text.TextAlign? component1();
- method public androidx.ui.engine.text.FontSynthesis? component10();
- method public Boolean? component11();
- method public androidx.ui.engine.window.Locale? component12();
method public androidx.ui.engine.text.TextDirection? component2();
method public androidx.ui.engine.text.TextIndent? component3();
method public Float? component4();
- method public androidx.ui.engine.text.FontWeight? component5();
- method public androidx.ui.engine.text.FontStyle? component6();
- method public Integer? component7();
- method public androidx.ui.engine.text.font.FontFamily? component8();
- method public Float? component9();
- method public androidx.ui.engine.text.ParagraphStyle copy(androidx.ui.engine.text.TextAlign? textAlign, androidx.ui.engine.text.TextDirection? textDirection, androidx.ui.engine.text.TextIndent? textIndent, Float? lineHeight, androidx.ui.engine.text.FontWeight? fontWeight, androidx.ui.engine.text.FontStyle? fontStyle, Integer? maxLines, androidx.ui.engine.text.font.FontFamily? fontFamily, Float? fontSize, androidx.ui.engine.text.FontSynthesis? fontSynthesis, Boolean? ellipsis, androidx.ui.engine.window.Locale? locale);
+ method public Integer? component5();
+ method public Boolean? component6();
+ method public androidx.ui.engine.text.ParagraphStyle copy(androidx.ui.engine.text.TextAlign? textAlign, androidx.ui.engine.text.TextDirection? textDirection, androidx.ui.engine.text.TextIndent? textIndent, Float? lineHeight, Integer? maxLines, Boolean? ellipsis);
method public Boolean? getEllipsis();
- method public androidx.ui.engine.text.font.FontFamily? getFontFamily();
- method public Float? getFontSize();
- method public androidx.ui.engine.text.FontStyle? getFontStyle();
- method public androidx.ui.engine.text.FontSynthesis? getFontSynthesis();
- method public androidx.ui.engine.text.FontWeight? getFontWeight();
method public Float? getLineHeight();
- method public androidx.ui.engine.window.Locale? getLocale();
method public Integer? getMaxLines();
method public androidx.ui.engine.text.TextAlign? getTextAlign();
method public androidx.ui.engine.text.TextDirection? getTextDirection();
method public androidx.ui.engine.text.TextIndent? getTextIndent();
}
- public final class ParagraphStyleKt {
- ctor public ParagraphStyleKt();
- }
-
public enum TextAlign {
enum_constant public static final androidx.ui.engine.text.TextAlign Center;
enum_constant public static final androidx.ui.engine.text.TextAlign End;
@@ -223,44 +203,6 @@
method public static androidx.ui.engine.text.TextIndent lerp(androidx.ui.engine.text.TextIndent a, androidx.ui.engine.text.TextIndent b, float t);
}
- public final class TextStyle {
- ctor public TextStyle(androidx.ui.graphics.Color? color, androidx.ui.engine.text.TextDecoration? decoration, androidx.ui.engine.text.FontWeight? fontWeight, androidx.ui.engine.text.FontStyle? fontStyle, androidx.ui.engine.text.font.FontFamily? fontFamily, Float? fontSize, Float? fontSizeScale, String? fontFeatureSettings, Float? letterSpacing, Float? wordSpacing, androidx.ui.engine.text.BaselineShift? baselineShift, androidx.ui.engine.text.TextGeometricTransform? textGeometricTransform, androidx.ui.engine.window.Locale? locale, androidx.ui.graphics.Color? background, androidx.ui.engine.text.FontSynthesis? fontSynthesis, androidx.ui.painting.Shadow? shadow);
- ctor public TextStyle();
- method public androidx.ui.graphics.Color? component1();
- method public Float? component10();
- method public androidx.ui.engine.text.BaselineShift? component11();
- method public androidx.ui.engine.text.TextGeometricTransform? component12();
- method public androidx.ui.engine.window.Locale? component13();
- method public androidx.ui.graphics.Color? component14();
- method public androidx.ui.engine.text.FontSynthesis? component15();
- method public androidx.ui.painting.Shadow? component16();
- method public androidx.ui.engine.text.TextDecoration? component2();
- method public androidx.ui.engine.text.FontWeight? component3();
- method public androidx.ui.engine.text.FontStyle? component4();
- method public androidx.ui.engine.text.font.FontFamily? component5();
- method public Float? component6();
- method public Float? component7();
- method public String? component8();
- method public Float? component9();
- method public androidx.ui.engine.text.TextStyle copy(androidx.ui.graphics.Color? color, androidx.ui.engine.text.TextDecoration? decoration, androidx.ui.engine.text.FontWeight? fontWeight, androidx.ui.engine.text.FontStyle? fontStyle, androidx.ui.engine.text.font.FontFamily? fontFamily, Float? fontSize, Float? fontSizeScale, String? fontFeatureSettings, Float? letterSpacing, Float? wordSpacing, androidx.ui.engine.text.BaselineShift? baselineShift, androidx.ui.engine.text.TextGeometricTransform? textGeometricTransform, androidx.ui.engine.window.Locale? locale, androidx.ui.graphics.Color? background, androidx.ui.engine.text.FontSynthesis? fontSynthesis, androidx.ui.painting.Shadow? shadow);
- method public androidx.ui.graphics.Color? getBackground();
- method public androidx.ui.engine.text.BaselineShift? getBaselineShift();
- method public androidx.ui.graphics.Color? getColor();
- method public androidx.ui.engine.text.TextDecoration? getDecoration();
- method public androidx.ui.engine.text.font.FontFamily? getFontFamily();
- method public String? getFontFeatureSettings();
- method public Float? getFontSize();
- method public Float? getFontSizeScale();
- method public androidx.ui.engine.text.FontStyle? getFontStyle();
- method public androidx.ui.engine.text.FontSynthesis? getFontSynthesis();
- method public androidx.ui.engine.text.FontWeight? getFontWeight();
- method public Float? getLetterSpacing();
- method public androidx.ui.engine.window.Locale? getLocale();
- method public androidx.ui.painting.Shadow? getShadow();
- method public androidx.ui.engine.text.TextGeometricTransform? getTextGeometricTransform();
- method public Float? getWordSpacing();
- }
-
}
package androidx.ui.engine.text.font {
@@ -338,7 +280,6 @@
public final class ParagraphAndroidKt {
ctor public ParagraphAndroidKt();
- field public static final char LINE_FEED = 10; // 0x000a '\n'
}
}
@@ -413,8 +354,8 @@
method public int getPositionForOffset(androidx.ui.engine.geometry.Offset offset);
method public float getPreferredLineHeight();
method public androidx.ui.engine.geometry.Size getSize();
+ method public androidx.ui.painting.TextStyle? getStyle();
method public androidx.ui.painting.AnnotatedString? getText();
- method public androidx.ui.painting.TextStyle? getTextStyle();
method public float getWidth();
method public androidx.ui.services.text_editing.TextRange getWordBoundary(int position);
method public void layout(androidx.ui.core.Constraints constraints);
@@ -422,7 +363,6 @@
method public void paintBackground(int start, int end, androidx.ui.graphics.Color color, androidx.ui.painting.Canvas canvas, androidx.ui.engine.geometry.Offset offset);
method public void paintCursor(int offset, androidx.ui.painting.Canvas canvas);
method public void setText(androidx.ui.painting.AnnotatedString? value);
- method public void setTextStyle(androidx.ui.painting.TextStyle? value);
property public final boolean didExceedMaxLines;
property public final float height;
property public final float maxIntrinsicWidth;
@@ -430,13 +370,11 @@
property public final float preferredLineHeight;
property public final androidx.ui.engine.geometry.Size size;
property public final androidx.ui.painting.AnnotatedString? text;
- property public final androidx.ui.painting.TextStyle? textStyle;
property public final float width;
}
public final class TextPainterKt {
ctor public TextPainterKt();
- method public static float applyFloatingPointHack(float layoutValue);
}
public final class TextSpanKt {
@@ -444,16 +382,16 @@
}
public final class TextStyle {
- ctor public TextStyle(androidx.ui.graphics.Color? color, Float? fontSize, Float? fontSizeScale, androidx.ui.engine.text.FontWeight? fontWeight, androidx.ui.engine.text.FontStyle? fontStyle, androidx.ui.engine.text.FontSynthesis? fontSynthesis, String? fontFeatureSettings, Float? letterSpacing, Float? wordSpacing, androidx.ui.engine.text.BaselineShift? baselineShift, androidx.ui.engine.text.TextGeometricTransform? textGeometricTransform, androidx.ui.engine.window.Locale? locale, androidx.ui.graphics.Color? background, androidx.ui.engine.text.TextDecoration? decoration, androidx.ui.engine.text.font.FontFamily? fontFamily, androidx.ui.painting.Shadow? shadow, String? debugLabel);
+ ctor public TextStyle(androidx.ui.graphics.Color? color, Float? fontSize, Float? fontSizeScale, androidx.ui.engine.text.FontWeight? fontWeight, androidx.ui.engine.text.FontStyle? fontStyle, androidx.ui.engine.text.FontSynthesis? fontSynthesis, androidx.ui.engine.text.font.FontFamily? fontFamily, String? fontFeatureSettings, Float? letterSpacing, Float? wordSpacing, androidx.ui.engine.text.BaselineShift? baselineShift, androidx.ui.engine.text.TextGeometricTransform? textGeometricTransform, androidx.ui.engine.window.Locale? locale, androidx.ui.graphics.Color? background, androidx.ui.engine.text.TextDecoration? decoration, androidx.ui.painting.Shadow? shadow, String? debugLabel);
ctor public TextStyle();
method public androidx.ui.painting.basictypes.RenderComparison compareTo(androidx.ui.painting.TextStyle other);
method public androidx.ui.graphics.Color? component1();
- method public androidx.ui.engine.text.BaselineShift? component10();
- method public androidx.ui.engine.text.TextGeometricTransform? component11();
- method public androidx.ui.engine.window.Locale? component12();
- method public androidx.ui.graphics.Color? component13();
- method public androidx.ui.engine.text.TextDecoration? component14();
- method public androidx.ui.engine.text.font.FontFamily? component15();
+ method public Float? component10();
+ method public androidx.ui.engine.text.BaselineShift? component11();
+ method public androidx.ui.engine.text.TextGeometricTransform? component12();
+ method public androidx.ui.engine.window.Locale? component13();
+ method public androidx.ui.graphics.Color? component14();
+ method public androidx.ui.engine.text.TextDecoration? component15();
method public androidx.ui.painting.Shadow? component16();
method public String? component17();
method public Float? component2();
@@ -461,10 +399,10 @@
method public androidx.ui.engine.text.FontWeight? component4();
method public androidx.ui.engine.text.FontStyle? component5();
method public androidx.ui.engine.text.FontSynthesis? component6();
- method public String? component7();
- method public Float? component8();
+ method public androidx.ui.engine.text.font.FontFamily? component7();
+ method public String? component8();
method public Float? component9();
- method public androidx.ui.painting.TextStyle copy(androidx.ui.graphics.Color? color, Float? fontSize, Float? fontSizeScale, androidx.ui.engine.text.FontWeight? fontWeight, androidx.ui.engine.text.FontStyle? fontStyle, androidx.ui.engine.text.FontSynthesis? fontSynthesis, String? fontFeatureSettings, Float? letterSpacing, Float? wordSpacing, androidx.ui.engine.text.BaselineShift? baselineShift, androidx.ui.engine.text.TextGeometricTransform? textGeometricTransform, androidx.ui.engine.window.Locale? locale, androidx.ui.graphics.Color? background, androidx.ui.engine.text.TextDecoration? decoration, androidx.ui.engine.text.font.FontFamily? fontFamily, androidx.ui.painting.Shadow? shadow, String? debugLabel);
+ method public androidx.ui.painting.TextStyle copy(androidx.ui.graphics.Color? color, Float? fontSize, Float? fontSizeScale, androidx.ui.engine.text.FontWeight? fontWeight, androidx.ui.engine.text.FontStyle? fontStyle, androidx.ui.engine.text.FontSynthesis? fontSynthesis, androidx.ui.engine.text.font.FontFamily? fontFamily, String? fontFeatureSettings, Float? letterSpacing, Float? wordSpacing, androidx.ui.engine.text.BaselineShift? baselineShift, androidx.ui.engine.text.TextGeometricTransform? textGeometricTransform, androidx.ui.engine.window.Locale? locale, androidx.ui.graphics.Color? background, androidx.ui.engine.text.TextDecoration? decoration, androidx.ui.painting.Shadow? shadow, String? debugLabel);
method public androidx.ui.graphics.Color? getBackground();
method public androidx.ui.engine.text.BaselineShift? getBaselineShift();
method public androidx.ui.graphics.Color? getColor();
@@ -479,10 +417,8 @@
method public androidx.ui.engine.text.FontWeight? getFontWeight();
method public Float? getLetterSpacing();
method public androidx.ui.engine.window.Locale? getLocale();
- method public androidx.ui.engine.text.ParagraphStyle getParagraphStyle(androidx.ui.engine.text.TextAlign? textAlign = null, androidx.ui.engine.text.TextDirection? textDirection = null, androidx.ui.engine.text.TextIndent? textIndent = null, Float? lineHeight = null, float textScaleFactor = 1.0f, Boolean? ellipsis = null, Integer? maxLines = null, androidx.ui.engine.window.Locale? locale = null);
method public androidx.ui.painting.Shadow? getShadow();
method public androidx.ui.engine.text.TextGeometricTransform? getTextGeometricTransform();
- method public androidx.ui.engine.text.TextStyle getTextStyle(float textScaleFactor = 1.0f);
method public Float? getWordSpacing();
method public androidx.ui.painting.TextStyle merge(androidx.ui.painting.TextStyle? other = null);
method public void setFontFamily(androidx.ui.engine.text.font.FontFamily? p);
diff --git a/ui/text/api/api_lint.ignore b/ui/text/api/api_lint.ignore
new file mode 100644
index 0000000..d98ed24
--- /dev/null
+++ b/ui/text/api/api_lint.ignore
@@ -0,0 +1,93 @@
+// Baseline format: 1.0
+AcronymName: androidx.ui.engine.text.TextBox.Companion#fromLTRBD(float, float, float, float, androidx.ui.engine.text.TextDirection):
+ Acronyms should not be capitalized in method names: was `fromLTRBD`, should this be `fromLtrbd`?
+AcronymName: androidx.ui.engine.text.TextIndent.Companion#getNONE():
+ Acronyms should not be capitalized in method names: was `getNONE`, should this be `getNone`?
+
+
+AutoBoxing: androidx.ui.engine.text.ParagraphStyle#ParagraphStyle(androidx.ui.engine.text.TextAlign, androidx.ui.engine.text.TextDirection, androidx.ui.engine.text.TextIndent, Float, Integer, Boolean) parameter #3:
+ Must avoid boxed primitives (`java.lang.Float`)
+AutoBoxing: androidx.ui.engine.text.ParagraphStyle#ParagraphStyle(androidx.ui.engine.text.TextAlign, androidx.ui.engine.text.TextDirection, androidx.ui.engine.text.TextIndent, Float, Integer, Boolean) parameter #4:
+ Must avoid boxed primitives (`java.lang.Integer`)
+AutoBoxing: androidx.ui.engine.text.ParagraphStyle#component4():
+ Must avoid boxed primitives (`java.lang.Float`)
+AutoBoxing: androidx.ui.engine.text.ParagraphStyle#component5():
+ Must avoid boxed primitives (`java.lang.Integer`)
+AutoBoxing: androidx.ui.engine.text.ParagraphStyle#copy(androidx.ui.engine.text.TextAlign, androidx.ui.engine.text.TextDirection, androidx.ui.engine.text.TextIndent, Float, Integer, Boolean) parameter #3:
+ Must avoid boxed primitives (`java.lang.Float`)
+AutoBoxing: androidx.ui.engine.text.ParagraphStyle#copy(androidx.ui.engine.text.TextAlign, androidx.ui.engine.text.TextDirection, androidx.ui.engine.text.TextIndent, Float, Integer, Boolean) parameter #4:
+ Must avoid boxed primitives (`java.lang.Integer`)
+AutoBoxing: androidx.ui.engine.text.ParagraphStyle#getLineHeight():
+ Must avoid boxed primitives (`java.lang.Float`)
+AutoBoxing: androidx.ui.engine.text.ParagraphStyle#getMaxLines():
+ Must avoid boxed primitives (`java.lang.Integer`)
+AutoBoxing: androidx.ui.engine.text.TextGeometricTransform#TextGeometricTransform(Float, Float) parameter #0:
+ Must avoid boxed primitives (`java.lang.Float`)
+AutoBoxing: androidx.ui.engine.text.TextGeometricTransform#TextGeometricTransform(Float, Float) parameter #1:
+ Must avoid boxed primitives (`java.lang.Float`)
+AutoBoxing: androidx.ui.engine.text.TextGeometricTransform#component1():
+ Must avoid boxed primitives (`java.lang.Float`)
+AutoBoxing: androidx.ui.engine.text.TextGeometricTransform#component2():
+ Must avoid boxed primitives (`java.lang.Float`)
+AutoBoxing: androidx.ui.engine.text.TextGeometricTransform#copy(Float, Float) parameter #0:
+ Must avoid boxed primitives (`java.lang.Float`)
+AutoBoxing: androidx.ui.engine.text.TextGeometricTransform#copy(Float, Float) parameter #1:
+ Must avoid boxed primitives (`java.lang.Float`)
+AutoBoxing: androidx.ui.engine.text.TextGeometricTransform#getScaleX():
+ Must avoid boxed primitives (`java.lang.Float`)
+AutoBoxing: androidx.ui.engine.text.TextGeometricTransform#getSkewX():
+ Must avoid boxed primitives (`java.lang.Float`)
+AutoBoxing: androidx.ui.painting.ParagraphStyle#ParagraphStyle(androidx.ui.engine.text.TextAlign, androidx.ui.engine.text.TextDirection, Float, androidx.ui.engine.text.TextIndent) parameter #2:
+ Must avoid boxed primitives (`java.lang.Float`)
+AutoBoxing: androidx.ui.painting.ParagraphStyle#component3():
+ Must avoid boxed primitives (`java.lang.Float`)
+AutoBoxing: androidx.ui.painting.ParagraphStyle#copy(androidx.ui.engine.text.TextAlign, androidx.ui.engine.text.TextDirection, Float, androidx.ui.engine.text.TextIndent) parameter #2:
+ Must avoid boxed primitives (`java.lang.Float`)
+AutoBoxing: androidx.ui.painting.ParagraphStyle#getLineHeight():
+ Must avoid boxed primitives (`java.lang.Float`)
+AutoBoxing: androidx.ui.painting.TextPainter#TextPainter(androidx.ui.painting.AnnotatedString, androidx.ui.painting.TextStyle, androidx.ui.painting.ParagraphStyle, float, Integer, boolean, androidx.ui.rendering.paragraph.TextOverflow, androidx.ui.engine.window.Locale) parameter #4:
+ Must avoid boxed primitives (`java.lang.Integer`)
+AutoBoxing: androidx.ui.painting.TextStyle#TextStyle(androidx.ui.graphics.Color, Float, Float, androidx.ui.engine.text.FontWeight, androidx.ui.engine.text.FontStyle, androidx.ui.engine.text.FontSynthesis, androidx.ui.engine.text.font.FontFamily, String, Float, Float, androidx.ui.engine.text.BaselineShift, androidx.ui.engine.text.TextGeometricTransform, androidx.ui.engine.window.Locale, androidx.ui.graphics.Color, androidx.ui.engine.text.TextDecoration, androidx.ui.painting.Shadow, String) parameter #1:
+ Must avoid boxed primitives (`java.lang.Float`)
+AutoBoxing: androidx.ui.painting.TextStyle#TextStyle(androidx.ui.graphics.Color, Float, Float, androidx.ui.engine.text.FontWeight, androidx.ui.engine.text.FontStyle, androidx.ui.engine.text.FontSynthesis, androidx.ui.engine.text.font.FontFamily, String, Float, Float, androidx.ui.engine.text.BaselineShift, androidx.ui.engine.text.TextGeometricTransform, androidx.ui.engine.window.Locale, androidx.ui.graphics.Color, androidx.ui.engine.text.TextDecoration, androidx.ui.painting.Shadow, String) parameter #2:
+ Must avoid boxed primitives (`java.lang.Float`)
+AutoBoxing: androidx.ui.painting.TextStyle#TextStyle(androidx.ui.graphics.Color, Float, Float, androidx.ui.engine.text.FontWeight, androidx.ui.engine.text.FontStyle, androidx.ui.engine.text.FontSynthesis, androidx.ui.engine.text.font.FontFamily, String, Float, Float, androidx.ui.engine.text.BaselineShift, androidx.ui.engine.text.TextGeometricTransform, androidx.ui.engine.window.Locale, androidx.ui.graphics.Color, androidx.ui.engine.text.TextDecoration, androidx.ui.painting.Shadow, String) parameter #8:
+ Must avoid boxed primitives (`java.lang.Float`)
+AutoBoxing: androidx.ui.painting.TextStyle#TextStyle(androidx.ui.graphics.Color, Float, Float, androidx.ui.engine.text.FontWeight, androidx.ui.engine.text.FontStyle, androidx.ui.engine.text.FontSynthesis, androidx.ui.engine.text.font.FontFamily, String, Float, Float, androidx.ui.engine.text.BaselineShift, androidx.ui.engine.text.TextGeometricTransform, androidx.ui.engine.window.Locale, androidx.ui.graphics.Color, androidx.ui.engine.text.TextDecoration, androidx.ui.painting.Shadow, String) parameter #9:
+ Must avoid boxed primitives (`java.lang.Float`)
+AutoBoxing: androidx.ui.painting.TextStyle#component10():
+ Must avoid boxed primitives (`java.lang.Float`)
+AutoBoxing: androidx.ui.painting.TextStyle#component2():
+ Must avoid boxed primitives (`java.lang.Float`)
+AutoBoxing: androidx.ui.painting.TextStyle#component3():
+ Must avoid boxed primitives (`java.lang.Float`)
+AutoBoxing: androidx.ui.painting.TextStyle#component9():
+ Must avoid boxed primitives (`java.lang.Float`)
+AutoBoxing: androidx.ui.painting.TextStyle#copy(androidx.ui.graphics.Color, Float, Float, androidx.ui.engine.text.FontWeight, androidx.ui.engine.text.FontStyle, androidx.ui.engine.text.FontSynthesis, androidx.ui.engine.text.font.FontFamily, String, Float, Float, androidx.ui.engine.text.BaselineShift, androidx.ui.engine.text.TextGeometricTransform, androidx.ui.engine.window.Locale, androidx.ui.graphics.Color, androidx.ui.engine.text.TextDecoration, androidx.ui.painting.Shadow, String) parameter #1:
+ Must avoid boxed primitives (`java.lang.Float`)
+AutoBoxing: androidx.ui.painting.TextStyle#copy(androidx.ui.graphics.Color, Float, Float, androidx.ui.engine.text.FontWeight, androidx.ui.engine.text.FontStyle, androidx.ui.engine.text.FontSynthesis, androidx.ui.engine.text.font.FontFamily, String, Float, Float, androidx.ui.engine.text.BaselineShift, androidx.ui.engine.text.TextGeometricTransform, androidx.ui.engine.window.Locale, androidx.ui.graphics.Color, androidx.ui.engine.text.TextDecoration, androidx.ui.painting.Shadow, String) parameter #2:
+ Must avoid boxed primitives (`java.lang.Float`)
+AutoBoxing: androidx.ui.painting.TextStyle#copy(androidx.ui.graphics.Color, Float, Float, androidx.ui.engine.text.FontWeight, androidx.ui.engine.text.FontStyle, androidx.ui.engine.text.FontSynthesis, androidx.ui.engine.text.font.FontFamily, String, Float, Float, androidx.ui.engine.text.BaselineShift, androidx.ui.engine.text.TextGeometricTransform, androidx.ui.engine.window.Locale, androidx.ui.graphics.Color, androidx.ui.engine.text.TextDecoration, androidx.ui.painting.Shadow, String) parameter #8:
+ Must avoid boxed primitives (`java.lang.Float`)
+AutoBoxing: androidx.ui.painting.TextStyle#copy(androidx.ui.graphics.Color, Float, Float, androidx.ui.engine.text.FontWeight, androidx.ui.engine.text.FontStyle, androidx.ui.engine.text.FontSynthesis, androidx.ui.engine.text.font.FontFamily, String, Float, Float, androidx.ui.engine.text.BaselineShift, androidx.ui.engine.text.TextGeometricTransform, androidx.ui.engine.window.Locale, androidx.ui.graphics.Color, androidx.ui.engine.text.TextDecoration, androidx.ui.painting.Shadow, String) parameter #9:
+ Must avoid boxed primitives (`java.lang.Float`)
+AutoBoxing: androidx.ui.painting.TextStyle#getFontSize():
+ Must avoid boxed primitives (`java.lang.Float`)
+AutoBoxing: androidx.ui.painting.TextStyle#getFontSizeScale():
+ Must avoid boxed primitives (`java.lang.Float`)
+AutoBoxing: androidx.ui.painting.TextStyle#getLetterSpacing():
+ Must avoid boxed primitives (`java.lang.Float`)
+AutoBoxing: androidx.ui.painting.TextStyle#getWordSpacing():
+ Must avoid boxed primitives (`java.lang.Float`)
+
+
+KotlinOperator: androidx.ui.engine.text.TextDecoration#contains(androidx.ui.engine.text.TextDecoration):
+ Method can be invoked as a "in" operator from Kotlin: `contains` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.engine.text.font.FontFamily#contains(androidx.ui.engine.text.font.Font):
+ Method can be invoked as a "in" operator from Kotlin: `contains` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.engine.text.font.FontFamily#get(int):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.engine.text.font.FontFamilyList#contains(androidx.ui.engine.text.font.FontFamily):
+ Method can be invoked as a "in" operator from Kotlin: `contains` (this is usually desirable; just make sure it makes sense for this type of object)
+KotlinOperator: androidx.ui.engine.text.font.FontFamilyList#get(int):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
diff --git a/ui/text/api/current.txt b/ui/text/api/current.txt
index 018dc87..c70c422 100644
--- a/ui/text/api/current.txt
+++ b/ui/text/api/current.txt
@@ -66,7 +66,6 @@
public final class Paragraph {
method public float getBaseline();
method public androidx.ui.engine.geometry.Rect getCursorRect(int offset);
- method public androidx.ui.painting.TextStyle getDefaultTextStyle();
method public boolean getDidExceedMaxLines();
method public float getHeight();
method public float getLineHeight(int lineIndex);
@@ -75,11 +74,8 @@
method public float getLineWidth(int lineIndex);
method public float getMaxIntrinsicWidth();
method public float getMinIntrinsicWidth();
- method public androidx.ui.engine.text.ParagraphStyle getParagraphStyle();
method public androidx.ui.painting.Path getPathForRange(int start, int end);
method public int getPositionForOffset(androidx.ui.engine.geometry.Offset offset);
- method public String getText();
- method public java.util.List<androidx.ui.painting.AnnotatedString.Item<androidx.ui.painting.TextStyle>> getTextStyles();
method public float getWidth();
method public androidx.ui.services.text_editing.TextRange getWordBoundary(int offset);
method public void layout(androidx.ui.engine.text.ParagraphConstraints constraints);
@@ -100,39 +96,23 @@
}
public final class ParagraphStyle {
- ctor public ParagraphStyle(androidx.ui.engine.text.TextAlign? textAlign, androidx.ui.engine.text.TextDirection? textDirection, androidx.ui.engine.text.TextIndent? textIndent, Float? lineHeight, androidx.ui.engine.text.FontWeight? fontWeight, androidx.ui.engine.text.FontStyle? fontStyle, Integer? maxLines, androidx.ui.engine.text.font.FontFamily? fontFamily, Float? fontSize, androidx.ui.engine.text.FontSynthesis? fontSynthesis, Boolean? ellipsis, androidx.ui.engine.window.Locale? locale);
+ ctor public ParagraphStyle(androidx.ui.engine.text.TextAlign? textAlign, androidx.ui.engine.text.TextDirection? textDirection, androidx.ui.engine.text.TextIndent? textIndent, Float? lineHeight, Integer? maxLines, Boolean? ellipsis);
ctor public ParagraphStyle();
method public androidx.ui.engine.text.TextAlign? component1();
- method public androidx.ui.engine.text.FontSynthesis? component10();
- method public Boolean? component11();
- method public androidx.ui.engine.window.Locale? component12();
method public androidx.ui.engine.text.TextDirection? component2();
method public androidx.ui.engine.text.TextIndent? component3();
method public Float? component4();
- method public androidx.ui.engine.text.FontWeight? component5();
- method public androidx.ui.engine.text.FontStyle? component6();
- method public Integer? component7();
- method public androidx.ui.engine.text.font.FontFamily? component8();
- method public Float? component9();
- method public androidx.ui.engine.text.ParagraphStyle copy(androidx.ui.engine.text.TextAlign? textAlign, androidx.ui.engine.text.TextDirection? textDirection, androidx.ui.engine.text.TextIndent? textIndent, Float? lineHeight, androidx.ui.engine.text.FontWeight? fontWeight, androidx.ui.engine.text.FontStyle? fontStyle, Integer? maxLines, androidx.ui.engine.text.font.FontFamily? fontFamily, Float? fontSize, androidx.ui.engine.text.FontSynthesis? fontSynthesis, Boolean? ellipsis, androidx.ui.engine.window.Locale? locale);
+ method public Integer? component5();
+ method public Boolean? component6();
+ method public androidx.ui.engine.text.ParagraphStyle copy(androidx.ui.engine.text.TextAlign? textAlign, androidx.ui.engine.text.TextDirection? textDirection, androidx.ui.engine.text.TextIndent? textIndent, Float? lineHeight, Integer? maxLines, Boolean? ellipsis);
method public Boolean? getEllipsis();
- method public androidx.ui.engine.text.font.FontFamily? getFontFamily();
- method public Float? getFontSize();
- method public androidx.ui.engine.text.FontStyle? getFontStyle();
- method public androidx.ui.engine.text.FontSynthesis? getFontSynthesis();
- method public androidx.ui.engine.text.FontWeight? getFontWeight();
method public Float? getLineHeight();
- method public androidx.ui.engine.window.Locale? getLocale();
method public Integer? getMaxLines();
method public androidx.ui.engine.text.TextAlign? getTextAlign();
method public androidx.ui.engine.text.TextDirection? getTextDirection();
method public androidx.ui.engine.text.TextIndent? getTextIndent();
}
- public final class ParagraphStyleKt {
- ctor public ParagraphStyleKt();
- }
-
public enum TextAlign {
enum_constant public static final androidx.ui.engine.text.TextAlign Center;
enum_constant public static final androidx.ui.engine.text.TextAlign End;
@@ -223,44 +203,6 @@
method public static androidx.ui.engine.text.TextIndent lerp(androidx.ui.engine.text.TextIndent a, androidx.ui.engine.text.TextIndent b, float t);
}
- public final class TextStyle {
- ctor public TextStyle(androidx.ui.graphics.Color? color, androidx.ui.engine.text.TextDecoration? decoration, androidx.ui.engine.text.FontWeight? fontWeight, androidx.ui.engine.text.FontStyle? fontStyle, androidx.ui.engine.text.font.FontFamily? fontFamily, Float? fontSize, Float? fontSizeScale, String? fontFeatureSettings, Float? letterSpacing, Float? wordSpacing, androidx.ui.engine.text.BaselineShift? baselineShift, androidx.ui.engine.text.TextGeometricTransform? textGeometricTransform, androidx.ui.engine.window.Locale? locale, androidx.ui.graphics.Color? background, androidx.ui.engine.text.FontSynthesis? fontSynthesis, androidx.ui.painting.Shadow? shadow);
- ctor public TextStyle();
- method public androidx.ui.graphics.Color? component1();
- method public Float? component10();
- method public androidx.ui.engine.text.BaselineShift? component11();
- method public androidx.ui.engine.text.TextGeometricTransform? component12();
- method public androidx.ui.engine.window.Locale? component13();
- method public androidx.ui.graphics.Color? component14();
- method public androidx.ui.engine.text.FontSynthesis? component15();
- method public androidx.ui.painting.Shadow? component16();
- method public androidx.ui.engine.text.TextDecoration? component2();
- method public androidx.ui.engine.text.FontWeight? component3();
- method public androidx.ui.engine.text.FontStyle? component4();
- method public androidx.ui.engine.text.font.FontFamily? component5();
- method public Float? component6();
- method public Float? component7();
- method public String? component8();
- method public Float? component9();
- method public androidx.ui.engine.text.TextStyle copy(androidx.ui.graphics.Color? color, androidx.ui.engine.text.TextDecoration? decoration, androidx.ui.engine.text.FontWeight? fontWeight, androidx.ui.engine.text.FontStyle? fontStyle, androidx.ui.engine.text.font.FontFamily? fontFamily, Float? fontSize, Float? fontSizeScale, String? fontFeatureSettings, Float? letterSpacing, Float? wordSpacing, androidx.ui.engine.text.BaselineShift? baselineShift, androidx.ui.engine.text.TextGeometricTransform? textGeometricTransform, androidx.ui.engine.window.Locale? locale, androidx.ui.graphics.Color? background, androidx.ui.engine.text.FontSynthesis? fontSynthesis, androidx.ui.painting.Shadow? shadow);
- method public androidx.ui.graphics.Color? getBackground();
- method public androidx.ui.engine.text.BaselineShift? getBaselineShift();
- method public androidx.ui.graphics.Color? getColor();
- method public androidx.ui.engine.text.TextDecoration? getDecoration();
- method public androidx.ui.engine.text.font.FontFamily? getFontFamily();
- method public String? getFontFeatureSettings();
- method public Float? getFontSize();
- method public Float? getFontSizeScale();
- method public androidx.ui.engine.text.FontStyle? getFontStyle();
- method public androidx.ui.engine.text.FontSynthesis? getFontSynthesis();
- method public androidx.ui.engine.text.FontWeight? getFontWeight();
- method public Float? getLetterSpacing();
- method public androidx.ui.engine.window.Locale? getLocale();
- method public androidx.ui.painting.Shadow? getShadow();
- method public androidx.ui.engine.text.TextGeometricTransform? getTextGeometricTransform();
- method public Float? getWordSpacing();
- }
-
}
package androidx.ui.engine.text.font {
@@ -338,7 +280,6 @@
public final class ParagraphAndroidKt {
ctor public ParagraphAndroidKt();
- field public static final char LINE_FEED = 10; // 0x000a '\n'
}
}
@@ -413,8 +354,8 @@
method public int getPositionForOffset(androidx.ui.engine.geometry.Offset offset);
method public float getPreferredLineHeight();
method public androidx.ui.engine.geometry.Size getSize();
+ method public androidx.ui.painting.TextStyle? getStyle();
method public androidx.ui.painting.AnnotatedString? getText();
- method public androidx.ui.painting.TextStyle? getTextStyle();
method public float getWidth();
method public androidx.ui.services.text_editing.TextRange getWordBoundary(int position);
method public void layout(androidx.ui.core.Constraints constraints);
@@ -422,7 +363,6 @@
method public void paintBackground(int start, int end, androidx.ui.graphics.Color color, androidx.ui.painting.Canvas canvas, androidx.ui.engine.geometry.Offset offset);
method public void paintCursor(int offset, androidx.ui.painting.Canvas canvas);
method public void setText(androidx.ui.painting.AnnotatedString? value);
- method public void setTextStyle(androidx.ui.painting.TextStyle? value);
property public final boolean didExceedMaxLines;
property public final float height;
property public final float maxIntrinsicWidth;
@@ -430,13 +370,11 @@
property public final float preferredLineHeight;
property public final androidx.ui.engine.geometry.Size size;
property public final androidx.ui.painting.AnnotatedString? text;
- property public final androidx.ui.painting.TextStyle? textStyle;
property public final float width;
}
public final class TextPainterKt {
ctor public TextPainterKt();
- method public static float applyFloatingPointHack(float layoutValue);
}
public final class TextSpanKt {
@@ -444,16 +382,16 @@
}
public final class TextStyle {
- ctor public TextStyle(androidx.ui.graphics.Color? color, Float? fontSize, Float? fontSizeScale, androidx.ui.engine.text.FontWeight? fontWeight, androidx.ui.engine.text.FontStyle? fontStyle, androidx.ui.engine.text.FontSynthesis? fontSynthesis, String? fontFeatureSettings, Float? letterSpacing, Float? wordSpacing, androidx.ui.engine.text.BaselineShift? baselineShift, androidx.ui.engine.text.TextGeometricTransform? textGeometricTransform, androidx.ui.engine.window.Locale? locale, androidx.ui.graphics.Color? background, androidx.ui.engine.text.TextDecoration? decoration, androidx.ui.engine.text.font.FontFamily? fontFamily, androidx.ui.painting.Shadow? shadow, String? debugLabel);
+ ctor public TextStyle(androidx.ui.graphics.Color? color, Float? fontSize, Float? fontSizeScale, androidx.ui.engine.text.FontWeight? fontWeight, androidx.ui.engine.text.FontStyle? fontStyle, androidx.ui.engine.text.FontSynthesis? fontSynthesis, androidx.ui.engine.text.font.FontFamily? fontFamily, String? fontFeatureSettings, Float? letterSpacing, Float? wordSpacing, androidx.ui.engine.text.BaselineShift? baselineShift, androidx.ui.engine.text.TextGeometricTransform? textGeometricTransform, androidx.ui.engine.window.Locale? locale, androidx.ui.graphics.Color? background, androidx.ui.engine.text.TextDecoration? decoration, androidx.ui.painting.Shadow? shadow, String? debugLabel);
ctor public TextStyle();
method public androidx.ui.painting.basictypes.RenderComparison compareTo(androidx.ui.painting.TextStyle other);
method public androidx.ui.graphics.Color? component1();
- method public androidx.ui.engine.text.BaselineShift? component10();
- method public androidx.ui.engine.text.TextGeometricTransform? component11();
- method public androidx.ui.engine.window.Locale? component12();
- method public androidx.ui.graphics.Color? component13();
- method public androidx.ui.engine.text.TextDecoration? component14();
- method public androidx.ui.engine.text.font.FontFamily? component15();
+ method public Float? component10();
+ method public androidx.ui.engine.text.BaselineShift? component11();
+ method public androidx.ui.engine.text.TextGeometricTransform? component12();
+ method public androidx.ui.engine.window.Locale? component13();
+ method public androidx.ui.graphics.Color? component14();
+ method public androidx.ui.engine.text.TextDecoration? component15();
method public androidx.ui.painting.Shadow? component16();
method public String? component17();
method public Float? component2();
@@ -461,10 +399,10 @@
method public androidx.ui.engine.text.FontWeight? component4();
method public androidx.ui.engine.text.FontStyle? component5();
method public androidx.ui.engine.text.FontSynthesis? component6();
- method public String? component7();
- method public Float? component8();
+ method public androidx.ui.engine.text.font.FontFamily? component7();
+ method public String? component8();
method public Float? component9();
- method public androidx.ui.painting.TextStyle copy(androidx.ui.graphics.Color? color, Float? fontSize, Float? fontSizeScale, androidx.ui.engine.text.FontWeight? fontWeight, androidx.ui.engine.text.FontStyle? fontStyle, androidx.ui.engine.text.FontSynthesis? fontSynthesis, String? fontFeatureSettings, Float? letterSpacing, Float? wordSpacing, androidx.ui.engine.text.BaselineShift? baselineShift, androidx.ui.engine.text.TextGeometricTransform? textGeometricTransform, androidx.ui.engine.window.Locale? locale, androidx.ui.graphics.Color? background, androidx.ui.engine.text.TextDecoration? decoration, androidx.ui.engine.text.font.FontFamily? fontFamily, androidx.ui.painting.Shadow? shadow, String? debugLabel);
+ method public androidx.ui.painting.TextStyle copy(androidx.ui.graphics.Color? color, Float? fontSize, Float? fontSizeScale, androidx.ui.engine.text.FontWeight? fontWeight, androidx.ui.engine.text.FontStyle? fontStyle, androidx.ui.engine.text.FontSynthesis? fontSynthesis, androidx.ui.engine.text.font.FontFamily? fontFamily, String? fontFeatureSettings, Float? letterSpacing, Float? wordSpacing, androidx.ui.engine.text.BaselineShift? baselineShift, androidx.ui.engine.text.TextGeometricTransform? textGeometricTransform, androidx.ui.engine.window.Locale? locale, androidx.ui.graphics.Color? background, androidx.ui.engine.text.TextDecoration? decoration, androidx.ui.painting.Shadow? shadow, String? debugLabel);
method public androidx.ui.graphics.Color? getBackground();
method public androidx.ui.engine.text.BaselineShift? getBaselineShift();
method public androidx.ui.graphics.Color? getColor();
@@ -479,10 +417,8 @@
method public androidx.ui.engine.text.FontWeight? getFontWeight();
method public Float? getLetterSpacing();
method public androidx.ui.engine.window.Locale? getLocale();
- method public androidx.ui.engine.text.ParagraphStyle getParagraphStyle(androidx.ui.engine.text.TextAlign? textAlign = null, androidx.ui.engine.text.TextDirection? textDirection = null, androidx.ui.engine.text.TextIndent? textIndent = null, Float? lineHeight = null, float textScaleFactor = 1.0f, Boolean? ellipsis = null, Integer? maxLines = null, androidx.ui.engine.window.Locale? locale = null);
method public androidx.ui.painting.Shadow? getShadow();
method public androidx.ui.engine.text.TextGeometricTransform? getTextGeometricTransform();
- method public androidx.ui.engine.text.TextStyle getTextStyle(float textScaleFactor = 1.0f);
method public Float? getWordSpacing();
method public androidx.ui.painting.TextStyle merge(androidx.ui.painting.TextStyle? other = null);
method public void setFontFamily(androidx.ui.engine.text.font.FontFamily? p);
diff --git a/ui/text/api/restricted_1.0.0-alpha01.txt b/ui/text/api/restricted_1.0.0-alpha01.txt
index e2be170..438384f 100644
--- a/ui/text/api/restricted_1.0.0-alpha01.txt
+++ b/ui/text/api/restricted_1.0.0-alpha01.txt
@@ -18,7 +18,7 @@
}
public final class TextSpanKt {
- method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static androidx.ui.painting.AnnotatedString toAnnotatedString(androidx.ui.painting.TextSpan, boolean includeRootStyle = true);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static androidx.ui.painting.AnnotatedString toAnnotatedString(androidx.ui.painting.TextSpan);
}
}
diff --git a/ui/text/api/restricted_current.txt b/ui/text/api/restricted_current.txt
index e2be170..438384f 100644
--- a/ui/text/api/restricted_current.txt
+++ b/ui/text/api/restricted_current.txt
@@ -18,7 +18,7 @@
}
public final class TextSpanKt {
- method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static androidx.ui.painting.AnnotatedString toAnnotatedString(androidx.ui.painting.TextSpan, boolean includeRootStyle = true);
+ method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP) public static androidx.ui.painting.AnnotatedString toAnnotatedString(androidx.ui.painting.TextSpan);
}
}
diff --git a/ui/text/src/androidTest/java/androidx/ui/engine/text/ParagraphIntegrationTest.kt b/ui/text/src/androidTest/java/androidx/ui/engine/text/ParagraphIntegrationTest.kt
index 226cac8..66d5701 100644
--- a/ui/text/src/androidTest/java/androidx/ui/engine/text/ParagraphIntegrationTest.kt
+++ b/ui/text/src/androidTest/java/androidx/ui/engine/text/ParagraphIntegrationTest.kt
@@ -28,7 +28,6 @@
import androidx.ui.engine.text.FontTestData.Companion.FONT_200_REGULAR
import androidx.ui.engine.text.font.FontFamily
import androidx.ui.engine.text.font.asFontFamily
-import androidx.ui.engine.text.platform.bitmap
import androidx.ui.engine.window.Locale
import androidx.ui.graphics.Color
import androidx.ui.matchers.equalToBitmap
@@ -377,11 +376,11 @@
val paragraph = Paragraph(
text = text,
textStyles = listOf(),
- paragraphStyle = ParagraphStyle(
+ style = TextStyle(
fontSize = fontSize,
locale = locale
),
- defaultTextStyle = TextStyle()
+ paragraphStyle = ParagraphStyle()
)
// just have 10x font size to have a bitmap
@@ -895,10 +894,10 @@
Paragraph(
text = "",
textStyles = listOf(),
+ style = TextStyle(),
paragraphStyle = ParagraphStyle(
lineHeight = -1.0f
- ),
- defaultTextStyle = TextStyle()
+ )
)
}
@@ -1433,7 +1432,7 @@
val paragraphWithColor = simpleParagraph(
text = text,
- defaultTextStyle = textStyle,
+ textStyle = textStyle,
fontSize = fontSize
)
paragraphWithColor.layout(ParagraphConstraints(paragraphWidth))
@@ -1454,7 +1453,7 @@
val paragraph = simpleParagraph(
text = text,
- defaultTextStyle = textStyle,
+ textStyle = textStyle,
fontSize = fontSize
)
paragraph.layout(ParagraphConstraints(Float.MAX_VALUE))
@@ -1802,23 +1801,24 @@
lineHeight: Float? = null,
textStyles: List<AnnotatedString.Item<TextStyle>> = listOf(),
fontFamily: FontFamily = fontFamilyMeasureFont,
- defaultTextStyle: TextStyle = TextStyle(),
- locale: Locale? = null
+ locale: Locale? = null,
+ textStyle: TextStyle? = null
): Paragraph {
return Paragraph(
text = text,
textStyles = textStyles,
+ style = TextStyle(
+ fontFamily = fontFamily,
+ fontSize = fontSize,
+ locale = locale
+ ).merge(textStyle),
paragraphStyle = ParagraphStyle(
textIndent = textIndent,
textAlign = textAlign,
textDirection = textDirection,
maxLines = maxLines,
- fontFamily = fontFamily,
- fontSize = fontSize,
- lineHeight = lineHeight,
- locale = locale
- ),
- defaultTextStyle = defaultTextStyle
+ lineHeight = lineHeight
+ )
)
}
}
diff --git a/ui/text/src/androidTest/java/androidx/ui/engine/text/platform/ParagraphAndroidTest.kt b/ui/text/src/androidTest/java/androidx/ui/engine/text/platform/ParagraphAndroidTest.kt
index 54c27fb..bf4658e 100644
--- a/ui/text/src/androidTest/java/androidx/ui/engine/text/platform/ParagraphAndroidTest.kt
+++ b/ui/text/src/androidTest/java/androidx/ui/engine/text/platform/ParagraphAndroidTest.kt
@@ -37,12 +37,12 @@
import androidx.ui.engine.text.font.FontFamily
import androidx.ui.engine.text.font.asFontFamily
import androidx.ui.engine.window.Locale
+import androidx.ui.graphics.Color
import androidx.ui.matchers.equalToBitmap
import androidx.ui.matchers.hasSpan
import androidx.ui.matchers.hasSpanOnTop
-import androidx.ui.graphics.Color
-import androidx.ui.painting.Shadow
import androidx.ui.painting.AnnotatedString
+import androidx.ui.painting.Shadow
import androidx.ui.painting.TextStyle
import com.nhaarman.mockitokotlin2.any
import com.nhaarman.mockitokotlin2.eq
@@ -84,8 +84,10 @@
for (text in arrayOf("abc\ndef", "\u05D0\u05D1\u05D2\n\u05D3\u05D4\u05D5")) {
val paragraphAndroid = simpleParagraph(
text = text,
- fontSize = fontSize,
- fontFamily = fontFamily
+ textStyle = TextStyle(
+ fontSize = fontSize,
+ fontFamily = fontFamily
+ )
)
// 2 chars width
@@ -924,8 +926,10 @@
val paragraph = simpleParagraph(
text = "abc",
- fontFamily = null,
- fontWeight = FontWeight.bold,
+ textStyle = TextStyle(
+ fontFamily = null,
+ fontWeight = FontWeight.bold
+ ),
typefaceAdapter = typefaceAdapter
)
paragraph.layout(Float.MAX_VALUE)
@@ -948,8 +952,10 @@
val typefaceAdapter = spy(TypefaceAdapter())
val paragraph = simpleParagraph(
text = "abc",
- fontFamily = null,
- fontStyle = FontStyle.Italic,
+ textStyle = TextStyle(
+ fontFamily = null,
+ fontStyle = FontStyle.Italic
+ ),
typefaceAdapter = typefaceAdapter
)
paragraph.layout(Float.MAX_VALUE)
@@ -974,7 +980,9 @@
val paragraph = simpleParagraph(
text = "abc",
- fontFamily = fontFamily,
+ textStyle = TextStyle(
+ fontFamily = fontFamily
+ ),
typefaceAdapter = typefaceAdapter
)
paragraph.layout(Float.MAX_VALUE)
@@ -997,7 +1005,9 @@
val typefaceAdapter = spy(TypefaceAdapter())
val paragraph = simpleParagraph(
text = "abc",
- fontFamily = fontFamily,
+ textStyle = TextStyle(
+ fontFamily = fontFamily
+ ),
typefaceAdapter = typefaceAdapter
)
paragraph.layout(Float.MAX_VALUE)
@@ -1020,8 +1030,10 @@
val paragraphWidth = (text.length - 1) * fontSize
val paragraph = simpleParagraph(
text = text,
- fontFamily = fontFamily,
- fontSize = fontSize,
+ textStyle = TextStyle(
+ fontFamily = fontFamily,
+ fontSize = fontSize
+ ),
ellipsis = true
)
paragraph.layout(paragraphWidth)
@@ -1038,10 +1050,12 @@
val paragraphWidth = (text.length - 1.5f) * fontSize
val paragraph = simpleParagraph(
text = text,
- fontFamily = fontFamily,
- fontSize = fontSize,
ellipsis = true,
- maxLines = 1
+ maxLines = 1,
+ textStyle = TextStyle(
+ fontFamily = fontFamily,
+ fontSize = fontSize
+ )
)
paragraph.layout(paragraphWidth)
@@ -1056,10 +1070,12 @@
val maxLines = ceil(text.length * fontSize / paragraphWidth).toInt()
val paragraph = simpleParagraph(
text = text,
- fontFamily = fontFamily,
- fontSize = fontSize,
ellipsis = true,
- maxLines = maxLines
+ maxLines = maxLines,
+ textStyle = TextStyle(
+ fontFamily = fontFamily,
+ fontSize = fontSize
+ )
)
paragraph.layout(paragraphWidth)
@@ -1068,34 +1084,211 @@
}
}
+ @Test
+ fun testTextStyle_fontSize_appliedOnTextPaint() {
+ val fontSize = 100f
+ val paragraph = simpleParagraph(
+ text = "",
+ textStyle = TextStyle(fontSize = fontSize)
+ )
+ paragraph.layout(0f)
+
+ assertThat(paragraph.textPaint.textSize, equalTo(fontSize))
+ }
+
+ @Test
+ fun testTextStyle_fontSizeScale_appliedOnTextPaint() {
+ val fontSize = 100f
+ val fontSizeScale = 2f
+ val paragraph = simpleParagraph(
+ text = "",
+ textStyle = TextStyle(
+ fontSize = fontSize,
+ fontSizeScale = fontSizeScale
+ )
+ )
+ paragraph.layout(0f)
+
+ assertThat(paragraph.textPaint.textSize, equalTo(fontSize * fontSizeScale))
+ }
+
+ @Test
+ fun testTextStyle_locale_appliedOnTextPaint() {
+ val systemLocale = java.util.Locale.JAPANESE
+ val locale = Locale(systemLocale.language, systemLocale.country)
+
+ val paragraph = simpleParagraph(
+ text = "",
+ textStyle = TextStyle(locale = locale)
+ )
+ paragraph.layout(0f)
+
+ assertThat(paragraph.textPaint.textLocale, equalTo(systemLocale))
+ }
+
+ @Test
+ fun testTextStyle_color_appliedOnTextPaint() {
+ val color = Color(0x12345678)
+ val paragraph = simpleParagraph(
+ text = "",
+ textStyle = TextStyle(color = color)
+ )
+ paragraph.layout(0f)
+
+ assertThat(paragraph.textPaint.color, equalTo(color.toArgb()))
+ }
+
+ @Test
+ fun testTextStyle_letterSpacing_appliedOnTextPaint() {
+ val letterSpacing = 2.0f
+ val paragraph = simpleParagraph(
+ text = "",
+ textStyle = TextStyle(letterSpacing = letterSpacing)
+ )
+ paragraph.layout(0f)
+
+ assertThat(paragraph.textPaint.letterSpacing, equalTo(letterSpacing))
+ }
+
+ @Test
+ fun testTextStyle_fontFeatureSettings_appliedOnTextPaint() {
+ val fontFeatureSettings = "\"kern\" 0"
+ val paragraph = simpleParagraph(
+ text = "",
+ textStyle = TextStyle(fontFeatureSettings = fontFeatureSettings)
+ )
+ paragraph.layout(0f)
+
+ assertThat(paragraph.textPaint.fontFeatureSettings, equalTo(fontFeatureSettings))
+ }
+
+ @SdkSuppress(minSdkVersion = 29)
+ @Test
+ fun testTextStyle_wordSpacing_appliedOnTextPaint() {
+ val wordSpacing = 1.23f
+ val paragraph = simpleParagraph(
+ text = "",
+ textStyle = TextStyle(wordSpacing = wordSpacing)
+ )
+ paragraph.layout(0f)
+
+ assertThat(paragraph.textPaint.wordSpacing, equalTo(wordSpacing))
+ }
+
+ @Test
+ fun testTextStyle_scaleX_appliedOnTextPaint() {
+ val scaleX = 0.5f
+ val paragraph = simpleParagraph(
+ text = "",
+ textStyle = TextStyle(
+ textGeometricTransform = TextGeometricTransform(
+ scaleX = scaleX
+ )
+ )
+ )
+ paragraph.layout(0f)
+
+ assertThat(paragraph.textPaint.textScaleX, equalTo(scaleX))
+ }
+
+ @Test
+ fun testTextStyle_skewX_appliedOnTextPaint() {
+ val skewX = 0.5f
+ val paragraph = simpleParagraph(
+ text = "",
+ textStyle = TextStyle(
+ textGeometricTransform = TextGeometricTransform(
+ skewX = skewX
+ )
+ )
+ )
+ paragraph.layout(0f)
+
+ assertThat(paragraph.textPaint.textSkewX, equalTo(skewX))
+ }
+
+ @Test
+ fun testTextStyle_decoration_underline_appliedOnTextPaint() {
+ val paragraph = simpleParagraph(
+ text = "",
+ textStyle = TextStyle(decoration = TextDecoration.Underline)
+ )
+ paragraph.layout(0f)
+
+ assertThat(paragraph.textPaint.isUnderlineText, equalTo(true))
+ }
+
+ @Test
+ fun testTextStyle_decoration_lineThrough_appliedOnTextPaint() {
+ val paragraph = simpleParagraph(
+ text = "",
+ textStyle = TextStyle(decoration = TextDecoration.LineThrough)
+ )
+ paragraph.layout(0f)
+
+ assertThat(paragraph.textPaint.isStrikeThruText, equalTo(true))
+ }
+
+ @Test
+ fun testTextStyle_background_appliedAsSpan() {
+ // bgColor is reset in the Android Layout constructor.
+ // therefore we cannot apply them on paint, have to use spans.
+ val text = "abc"
+ val color = Color(0x12345678)
+ val paragraph = simpleParagraph(
+ text = text,
+ textStyle = TextStyle(background = color)
+ )
+ paragraph.layout(0f)
+
+ assertThat(paragraph.underlyingText,
+ hasSpan(BackgroundColorSpan::class, 0, text.length) { span ->
+ span.backgroundColor == color.toArgb()
+ }
+ )
+ }
+
+ @Test
+ fun testTextStyle_baselineShift_appliedAsSpan() {
+ // baselineShift is reset in the Android Layout constructor.
+ // therefore we cannot apply them on paint, have to use spans.
+ val text = "abc"
+ val baselineShift = BaselineShift.Subscript
+ val paragraph = simpleParagraph(
+ text = text,
+ textStyle = TextStyle(baselineShift = baselineShift)
+ )
+ paragraph.layout(0f)
+
+ assertThat(
+ paragraph.underlyingText,
+ hasSpan(BaselineShiftSpan::class, 0, text.length) { span ->
+ span.multiplier == BaselineShift.Subscript.multiplier
+ }
+ )
+ }
+
private fun simpleParagraph(
text: String = "",
textStyles: List<AnnotatedString.Item<TextStyle>> = listOf(),
textIndent: TextIndent? = null,
textAlign: TextAlign? = null,
- fontSize: Float? = null,
ellipsis: Boolean? = null,
maxLines: Int? = null,
- fontFamily: FontFamily? = null,
- fontWeight: FontWeight? = null,
- fontStyle: FontStyle? = null,
+ textStyle: TextStyle? = null,
typefaceAdapter: TypefaceAdapter = TypefaceAdapter()
): ParagraphAndroid {
return ParagraphAndroid(
text = text,
textStyles = textStyles,
typefaceAdapter = typefaceAdapter,
+ style = TextStyle().merge(textStyle),
paragraphStyle = ParagraphStyle(
textAlign = textAlign,
textIndent = textIndent,
ellipsis = ellipsis,
- maxLines = maxLines,
- fontFamily = fontFamily,
- fontSize = fontSize,
- fontWeight = fontWeight,
- fontStyle = fontStyle
- ),
- defaultTextStyle = TextStyle()
+ maxLines = maxLines
+ )
)
}
}
\ No newline at end of file
diff --git a/ui/text/src/main/java/androidx/ui/engine/text/Paragraph.kt b/ui/text/src/main/java/androidx/ui/engine/text/Paragraph.kt
index 82a8921..5bccfdd 100644
--- a/ui/text/src/main/java/androidx/ui/engine/text/Paragraph.kt
+++ b/ui/text/src/main/java/androidx/ui/engine/text/Paragraph.kt
@@ -33,10 +33,10 @@
* Paragraphs can be displayed on a [Canvas] using the [paint] method.
*/
class Paragraph internal constructor(
- val text: String,
- val defaultTextStyle: TextStyle,
- val paragraphStyle: ParagraphStyle,
- val textStyles: List<AnnotatedString.Item<TextStyle>>
+ private val text: String,
+ style: TextStyle,
+ paragraphStyle: ParagraphStyle,
+ textStyles: List<AnnotatedString.Item<TextStyle>>
) {
private var needsLayout = true
/** increased visibility for testing **/
@@ -98,19 +98,14 @@
if (paragraphStyle.lineHeight != null && paragraphStyle.lineHeight < 0.0f) {
throw IllegalArgumentException("lineHeight can't be negative")
}
- paragraphImpl = ParagraphAndroid(text, defaultTextStyle, paragraphStyle, textStyles)
+ paragraphImpl = ParagraphAndroid(
+ text = text,
+ style = style,
+ paragraphStyle = paragraphStyle,
+ textStyles = textStyles
+ )
}
- // void Paragraph::SetFontCollection(
- // std::shared_ptr<FontCollection> font_collection) {
- // font_collection_ = std::move(font_collection);
- // }
-
- // void Paragraph::SetParagraphStyle(const ParagraphStyle& style) {
- // needs_layout_ = true;
- // paragraph_style_ = style;
- // }
-
/**
* Computes the size and position of each glyph in the paragraph.
*
diff --git a/ui/text/src/main/java/androidx/ui/engine/text/ParagraphStyle.kt b/ui/text/src/main/java/androidx/ui/engine/text/ParagraphStyle.kt
index e657fa0..b7f42bc 100644
--- a/ui/text/src/main/java/androidx/ui/engine/text/ParagraphStyle.kt
+++ b/ui/text/src/main/java/androidx/ui/engine/text/ParagraphStyle.kt
@@ -15,9 +15,6 @@
*/
package androidx.ui.engine.text
-import androidx.ui.engine.text.font.FontFamily
-import androidx.ui.engine.window.Locale
-
/**
* An opaque object that determines the configuration used by
* [ParagraphBuilder] to position lines within a [Paragraph] of text.
@@ -40,58 +37,21 @@
* @param lineHeight The minimum height of the line boxes, as a multiple of the
* font size.
*
- * @param fontWeight The typeface thickness to use when painting the text
- * (e.g., bold).
- *
- * @param fontStyle The typeface variant to use when drawing the letters (e.g.,
- * italics).
- *
* @param maxLines The maximum number of lines painted. Lines beyond this
* number are silently dropped. For example, if `maxLines` is 1, then only
* one line is rendered.
*
- * @param fontFamily The name of the font to use when painting the text (e.g.,
- * Roboto).
- *
- * @param fontSize The size of glyphs (in logical pixels) to use when painting
- * the text.
- *
- * @param fontSynthesis Whether to synthesize font weight and/or style when the requested weight or
- * style cannot be found in the provided custom font family.
- *
* @param ellipsis Whether to ellipsize overflowing text. If `maxLines` is
* not null, ellipsis is applied to the last rendered line, if that line
* overflows the width constraints. If `maxLines` is null, it will never
* be applied. If ellipsis is null, the system default will be adopted.
- *
- * @param locale The locale used to select region-specific glyphs.
- *
*/
data class ParagraphStyle constructor(
val textAlign: TextAlign? = null,
val textDirection: TextDirection? = null,
val textIndent: TextIndent? = null,
val lineHeight: Float? = null,
- val fontWeight: FontWeight? = null,
- val fontStyle: FontStyle? = null,
val maxLines: Int? = null,
- val fontFamily: FontFamily? = null,
- val fontSize: Float? = null,
- val fontSynthesis: FontSynthesis? = null,
- val ellipsis: Boolean? = null,
- val locale: Locale? = null
-)
-
-/**
- * Returns true if this [ParagraphStyle] contains any font style attributes set.
- */
-internal fun ParagraphStyle.hasFontAttributes(): Boolean {
- return fontFamily != null || fontStyle != null || fontWeight != null
-}
-
-/**
- * Returns true if this [TextStyle] contains any font style attributes set.
- */
-internal fun TextStyle.hasFontAttributes(): Boolean {
- return fontFamily != null || fontStyle != null || fontWeight != null
-}
+ val ellipsis: Boolean? = null
+ // TODO(siyamed) add fontScaleFactor and use it in Paragraph
+)
\ No newline at end of file
diff --git a/ui/text/src/main/java/androidx/ui/engine/text/TextStyle.kt b/ui/text/src/main/java/androidx/ui/engine/text/TextStyle.kt
index 177f40c..3c2d58d 100644
--- a/ui/text/src/main/java/androidx/ui/engine/text/TextStyle.kt
+++ b/ui/text/src/main/java/androidx/ui/engine/text/TextStyle.kt
@@ -50,7 +50,7 @@
* style cannot be found in the provided custom font family.
* @param shadow The shadow effect applied on the text.
*/
-data class TextStyle constructor(
+internal data class TextStyle constructor(
val color: Color? = null,
val decoration: TextDecoration? = null,
val fontWeight: FontWeight? = null,
diff --git a/ui/text/src/main/java/androidx/ui/engine/text/platform/ParagraphAndroid.kt b/ui/text/src/main/java/androidx/ui/engine/text/platform/ParagraphAndroid.kt
index 4dd4e11..ab15be30 100644
--- a/ui/text/src/main/java/androidx/ui/engine/text/platform/ParagraphAndroid.kt
+++ b/ui/text/src/main/java/androidx/ui/engine/text/platform/ParagraphAndroid.kt
@@ -15,6 +15,7 @@
*/
package androidx.ui.engine.text.platform
+import android.graphics.Typeface
import android.os.Build
import android.text.SpannableString
import android.text.Spanned
@@ -63,7 +64,6 @@
import androidx.ui.engine.text.TextDecoration
import androidx.ui.engine.text.TextDirection
import androidx.ui.engine.text.TextIndent
-import androidx.ui.engine.text.hasFontAttributes
import androidx.ui.painting.AnnotatedString
import androidx.ui.painting.Canvas
import androidx.ui.painting.Path
@@ -72,16 +72,13 @@
import kotlin.math.floor
import kotlin.math.roundToInt
-const val LINE_FEED = '\n'
-
internal class ParagraphAndroid constructor(
val text: String,
- val defaultTextStyle: TextStyle,
+ val style: TextStyle,
val paragraphStyle: ParagraphStyle,
val textStyles: List<AnnotatedString.Item<TextStyle>>,
val typefaceAdapter: TypefaceAdapter = TypefaceAdapter()
) {
-
@VisibleForTesting
internal val textPaint = TextPaint(android.graphics.Paint.ANTI_ALIAS_FLAG)
private var layout: TextLayout? = null
@@ -141,35 +138,16 @@
fun layout(width: Float) {
val floorWidth = floor(width)
- defaultTextStyle.applyTextStyle(textPaint)
+ val newStyle = style.applyTextStyle(textPaint, typefaceAdapter)
- // TODO(haoyuchang) remove this engine.ParagraphStyle
- paragraphStyle.fontSize?.let {
- textPaint.textSize = it
- }
-
- // TODO(siyamed): This default values are problem here. If the user just gives a single font
- // in the family, and does not provide any fontWeight, TypefaceAdapter will still get the
- // call as FontWeight.normal (which is the default value)
- if (paragraphStyle.hasFontAttributes()) {
- textPaint.typeface = typefaceAdapter.create(
- fontFamily = paragraphStyle.fontFamily,
- fontWeight = paragraphStyle.fontWeight ?: FontWeight.normal,
- fontStyle = paragraphStyle.fontStyle ?: FontStyle.Normal,
- fontSynthesis = paragraphStyle.fontSynthesis ?: FontSynthesis.All
- )
- }
-
- paragraphStyle.locale?.let {
- textPaint.textLocale = Locale(
- it.languageCode,
- it.countryCode ?: ""
- )
- }
-
- val charSequence = applyTextStyle(text, paragraphStyle.textIndent, textStyles)
+ val charSequence = applyTextStyle(
+ text,
+ paragraphStyle.textIndent,
+ listOf(AnnotatedString.Item<TextStyle>(newStyle, 0, text.length)) + textStyles
+ )
val alignment = toLayoutAlign(paragraphStyle.textAlign)
+
// TODO(Migration/haoyuchang): Layout has more settings that flutter,
// we may add them in future.
val textDirectionHeuristic = when (paragraphStyle.textDirection) {
@@ -279,6 +257,15 @@
canvas.translate(-x, -y)
}
+ private fun createTypeface(style: androidx.ui.engine.text.TextStyle): Typeface {
+ return typefaceAdapter.create(
+ fontFamily = style.fontFamily,
+ fontWeight = style.fontWeight ?: FontWeight.normal,
+ fontStyle = style.fontStyle ?: FontStyle.Normal,
+ fontSynthesis = style.fontSynthesis ?: FontSynthesis.All
+ )
+ }
+
private fun applyTextStyle(
text: String,
textIndent: TextIndent?,
@@ -375,14 +362,8 @@
}
if (style.hasFontAttributes()) {
- val typeface = typefaceAdapter.create(
- fontFamily = style.fontFamily,
- fontWeight = style.fontWeight ?: FontWeight.normal,
- fontStyle = style.fontStyle ?: FontStyle.Normal,
- fontSynthesis = style.fontSynthesis ?: FontSynthesis.All
- )
spannableString.setSpan(
- TypefaceSpan(typeface),
+ TypefaceSpan(createTypeface(style)),
start,
end,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
@@ -432,7 +413,7 @@
style.locale?.let {
spannableString.setSpan(
// TODO(Migration/haoyuchang): support locale fallback in the framework
- LocaleSpan(Locale(it.languageCode, it.countryCode!!)),
+ LocaleSpan(Locale(it.languageCode, it.countryCode ?: "")),
start,
end,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
@@ -461,7 +442,38 @@
}
}
-internal fun TextStyle.applyTextStyle(textPaint: TextPaint) {
+private fun TextStyle.applyTextStyle(
+ textPaint: TextPaint,
+ typefaceAdapter: TypefaceAdapter
+): TextStyle {
+ // TODO(haoyuchang) remove this engine.ParagraphStyle
+ fontSize?.let {
+ textPaint.textSize = it
+ }
+
+ fontSizeScale?.let {
+ textPaint.textSize *= fontSizeScale
+ }
+
+ // TODO(siyamed): This default values are problem here. If the user just gives a single font
+ // in the family, and does not provide any fontWeight, TypefaceAdapter will still get the
+ // call as FontWeight.normal (which is the default value)
+ if (getTextStyle().hasFontAttributes()) {
+ textPaint.typeface = typefaceAdapter.create(
+ fontFamily = fontFamily,
+ fontWeight = fontWeight ?: FontWeight.normal,
+ fontStyle = fontStyle ?: FontStyle.Normal,
+ fontSynthesis = fontSynthesis ?: FontSynthesis.All
+ )
+ }
+
+ locale?.let {
+ textPaint.textLocale = Locale(
+ it.languageCode,
+ it.countryCode ?: ""
+ )
+ }
+
color?.let {
textPaint.color = it.toArgb()
}
@@ -470,10 +482,58 @@
textPaint.letterSpacing = it
}
- // TODO(haoyuchang) apply other styles when engine.ParagraphStyle get removed.
+ fontFeatureSettings?.let {
+ textPaint.fontFeatureSettings = it
+ }
+
+ if (Build.VERSION.SDK_INT >= 29) {
+ wordSpacing?.let {
+ textPaint.wordSpacing = it
+ }
+ }
+
+ textGeometricTransform?.scaleX?.let {
+ textPaint.textScaleX *= it
+ }
+
+ textGeometricTransform?.skewX?.let {
+ textPaint.textSkewX += it
+ }
+
+ shadow?.let {
+ textPaint.setShadowLayer(
+ it.blurRadius.value,
+ it.offset.dx,
+ it.offset.dy,
+ it.color.toArgb()
+ )
+ }
+
+ decoration?.let {
+ if (it.contains(TextDecoration.Underline)) {
+ textPaint.isUnderlineText = true
+ }
+ if (it.contains(TextDecoration.LineThrough)) {
+ textPaint.isStrikeThruText = true
+ }
+ }
+
+ // baselineShift and bgColor is reset in the Android Layout constructor.
+ // therefore we cannot apply them on paint, have to use spans.
+ return TextStyle(
+ background = background,
+ baselineShift = baselineShift
+ )
}
-internal fun toLayoutAlign(align: TextAlign?): Int = when (align) {
+/**
+ * Returns true if this [TextStyle] contains any font style attributes set.
+ */
+private fun androidx.ui.engine.text.TextStyle.hasFontAttributes(): Boolean {
+ return fontFamily != null || fontStyle != null || fontWeight != null
+}
+
+private fun toLayoutAlign(align: TextAlign?): Int = when (align) {
TextAlign.Left -> ALIGN_LEFT
TextAlign.Right -> ALIGN_RIGHT
TextAlign.Center -> ALIGN_CENTER
diff --git a/ui/text/src/main/java/androidx/ui/painting/TextPainter.kt b/ui/text/src/main/java/androidx/ui/painting/TextPainter.kt
index 560cee7..73cdec8 100644
--- a/ui/text/src/main/java/androidx/ui/painting/TextPainter.kt
+++ b/ui/text/src/main/java/androidx/ui/painting/TextPainter.kt
@@ -17,6 +17,7 @@
package androidx.ui.painting
import androidx.annotation.RestrictTo
+import androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP
import androidx.annotation.VisibleForTesting
import androidx.ui.core.Constraints
import androidx.ui.core.IntPxSize
@@ -39,6 +40,8 @@
private val DefaultTextAlign: TextAlign = TextAlign.Start
private val DefaultTextDirection: TextDirection = TextDirection.Ltr
+/** The default font size if none is specified. */
+private const val DefaultFontSize: Float = 14.0f
/**
* Unfortunately, using full precision floating point here causes bad layouts because floating
@@ -48,7 +51,7 @@
* fractional pixel values up to the nearest whole pixel value. The right long-term fix is to do
* layout using fixed precision arithmetic.
*/
-fun applyFloatingPointHack(layoutValue: Float): Float {
+internal fun applyFloatingPointHack(layoutValue: Float): Float {
return ceil(layoutValue)
}
@@ -99,7 +102,7 @@
*/
class TextPainter(
text: AnnotatedString? = null,
- style: TextStyle? = null,
+ val style: TextStyle? = null,
val paragraphStyle: androidx.ui.painting.ParagraphStyle? = null,
textScaleFactor: Float = 1.0f,
maxLines: Int? = null,
@@ -132,6 +135,8 @@
private var lastMinWidth: Float = 0.0f
private var lastMaxWidth: Float = 0.0f
+ // TODO(siyamed) make arguments below immutable
+ @RestrictTo(LIBRARY_GROUP)
var text: AnnotatedString? = text
set(value) {
if (field == value) return
@@ -140,14 +145,8 @@
needsLayout = true
}
- var textStyle: TextStyle? = style
- set(value) {
- if (field == value) return
- layoutTemplate = null
- field = value
- paragraph = null
- needsLayout = true
- }
+ internal val textStyle: TextStyle
+ get() = style ?: TextStyle()
internal var textAlign: TextAlign =
if (paragraphStyle?.textAlign != null) paragraphStyle.textAlign else DefaultTextAlign
@@ -210,23 +209,18 @@
needsLayout = true
}
+ private fun createTextStyle(): TextStyle {
+ return textStyle.copy(fontSize = (textStyle.fontSize ?: DefaultFontSize) * textScaleFactor)
+ }
+
internal fun createParagraphStyle(): ParagraphStyle {
- return textStyle?.getParagraphStyle(
+ return ParagraphStyle(
textAlign = textAlign,
textDirection = textDirection,
- textScaleFactor = textScaleFactor,
- lineHeight = paragraphStyle?.lineHeight,
textIndent = paragraphStyle?.textIndent,
+ lineHeight = paragraphStyle?.lineHeight,
maxLines = maxLines,
- ellipsis = overflow == TextOverflow.Ellipsis,
- locale = locale
- ) ?: ParagraphStyle(
- textAlign = textAlign,
- textDirection = textDirection,
- maxLines = maxLines,
- ellipsis = overflow == TextOverflow.Ellipsis,
- locale = locale
- )
+ ellipsis = overflow == TextOverflow.Ellipsis)
}
/**
@@ -246,7 +240,7 @@
// TODO(Migration/qqd): The textDirection below used to be RTL.
layoutTemplate = Paragraph(
text = " ",
- defaultTextStyle = textStyle ?: TextStyle(),
+ style = createTextStyle(),
// direction doesn't matter, text is just a space
paragraphStyle = createParagraphStyle(),
textStyles = listOf()
@@ -363,10 +357,9 @@
if (paragraph == null) {
paragraph = Paragraph(
text = text!!.text,
- defaultTextStyle = textStyle ?: TextStyle(),
+ style = createTextStyle(),
paragraphStyle = createParagraphStyle(),
- textStyles = text!!.textStyles
- )
+ textStyles = text!!.textStyles)
}
lastMinWidth = minWidth
lastMaxWidth = finalMaxWidth
diff --git a/ui/text/src/main/java/androidx/ui/painting/TextSpan.kt b/ui/text/src/main/java/androidx/ui/painting/TextSpan.kt
index 82b79e0..d5356c1 100644
--- a/ui/text/src/main/java/androidx/ui/painting/TextSpan.kt
+++ b/ui/text/src/main/java/androidx/ui/painting/TextSpan.kt
@@ -33,7 +33,6 @@
* @param children Additional spans to include as children. If both [text] and [children] are
* non-null, the text will precede the children. The list must not contain any nulls.
*
- * @param recognizer A gesture recognizer that will receive events that hit this text span.
* @hide
*/
// TODO(haoyuchang) Make TextSpan immutable.
@@ -41,8 +40,7 @@
class TextSpan(
var style: TextStyle? = null,
var text: String? = null,
- val children: MutableList<TextSpan> = mutableListOf()/*,
- val recognizer: GestureRecognizer? = null*/
+ val children: MutableList<TextSpan> = mutableListOf()
) {
/**
@@ -153,21 +151,18 @@
)
private fun TextSpan.annotatedStringVisitor(
- includeRootStyle: Boolean,
stringBuilder: java.lang.StringBuilder,
styles: MutableList<RecordInternal>
) {
- val styleSpan = if (includeRootStyle) {
- style?.let {
- val span = RecordInternal(it, stringBuilder.length, -1)
- styles.add(span)
- span
- }
- } else null
+ val styleSpan = style?.let {
+ val span = RecordInternal(it, stringBuilder.length, -1)
+ styles.add(span)
+ span
+ }
text?.let { stringBuilder.append(text) }
for (child in children) {
- child.annotatedStringVisitor(true, stringBuilder, styles)
+ child.annotatedStringVisitor(stringBuilder, styles)
}
if (styleSpan != null) {
@@ -177,15 +172,13 @@
/**
* Convert a [TextSpan] into an [AnnotatedString].
- * @param includeRootStyle whether to attach the text style in the root [TextSpan] to the output
- * [AnnotatedString]. It's useful when the top level [TextStyle] is used as global text style setting.
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-fun TextSpan.toAnnotatedString(includeRootStyle: Boolean = true): AnnotatedString {
+fun TextSpan.toAnnotatedString(): AnnotatedString {
val stringBuilder = java.lang.StringBuilder()
val tempRecords = mutableListOf<RecordInternal>()
- annotatedStringVisitor(includeRootStyle, stringBuilder, tempRecords)
+ annotatedStringVisitor(stringBuilder, tempRecords)
val records = tempRecords.map { AnnotatedString.Item(it.style, it.start, it.end) }
return AnnotatedString(stringBuilder.toString(), records)
}
\ No newline at end of file
diff --git a/ui/text/src/main/java/androidx/ui/painting/TextStyle.kt b/ui/text/src/main/java/androidx/ui/painting/TextStyle.kt
index f3ef0d7..1c3ff2a 100644
--- a/ui/text/src/main/java/androidx/ui/painting/TextStyle.kt
+++ b/ui/text/src/main/java/androidx/ui/painting/TextStyle.kt
@@ -20,12 +20,8 @@
import androidx.ui.engine.text.FontStyle
import androidx.ui.engine.text.FontSynthesis
import androidx.ui.engine.text.FontWeight
-import androidx.ui.engine.text.ParagraphStyle
-import androidx.ui.engine.text.TextAlign
import androidx.ui.engine.text.TextDecoration
-import androidx.ui.engine.text.TextDirection
import androidx.ui.engine.text.TextGeometricTransform
-import androidx.ui.engine.text.TextIndent
import androidx.ui.engine.text.font.FontFamily
import androidx.ui.engine.text.lerp
import androidx.ui.engine.window.Locale
@@ -37,9 +33,6 @@
private const val _kDefaultDebugLabel: String = "unknown"
-/** The default font size if none is specified. */
-internal const val _defaultFontSize: Float = 14.0f
-
/**
* Configuration object to define the text style.
*
@@ -72,6 +65,7 @@
val fontWeight: FontWeight? = null,
val fontStyle: FontStyle? = null,
val fontSynthesis: FontSynthesis? = null,
+ var fontFamily: FontFamily? = null,
val fontFeatureSettings: String? = null,
val letterSpacing: Float? = null,
val wordSpacing: Float? = null,
@@ -80,7 +74,6 @@
val locale: Locale? = null,
val background: Color? = null,
val decoration: TextDecoration? = null,
- var fontFamily: FontFamily? = null,
val shadow: Shadow? = null,
val debugLabel: String? = null
) {
@@ -238,7 +231,7 @@
}
/** The style information for text runs, encoded for use by ui. */
- fun getTextStyle(textScaleFactor: Float = 1.0f): androidx.ui.engine.text.TextStyle {
+ internal fun getTextStyle(textScaleFactor: Float = 1.0f): androidx.ui.engine.text.TextStyle {
return androidx.ui.engine.text.TextStyle(
color = color,
decoration = decoration,
@@ -260,42 +253,6 @@
}
/**
- * The style information for paragraphs, encoded for use by `ui`.
- * The `textScaleFactor` argument must not be null. If omitted, it defaults
- * to 1.0. The other arguments may be null. The `maxLines` argument, if
- * specified and non-null, must be greater than zero.
- *
- * If the font size on this style isn't set, it will default to 14 logical
- * pixels.
- */
- fun getParagraphStyle(
- textAlign: TextAlign? = null,
- textDirection: TextDirection? = null,
- textIndent: TextIndent? = null,
- lineHeight: Float? = null,
- textScaleFactor: Float = 1.0f,
- ellipsis: Boolean? = null,
- maxLines: Int? = null,
- locale: Locale? = null
- ): ParagraphStyle {
- assert(maxLines == null || maxLines > 0)
- return ParagraphStyle(
- textAlign = textAlign,
- textDirection = textDirection,
- textIndent = textIndent,
- lineHeight = lineHeight,
- fontWeight = fontWeight,
- fontStyle = fontStyle,
- maxLines = maxLines,
- fontFamily = fontFamily,
- fontSize = (fontSize ?: _defaultFontSize) * textScaleFactor,
- ellipsis = ellipsis,
- locale = locale,
- fontSynthesis = fontSynthesis
- )
- }
-
- /**
* Describe the difference between this style and another, in terms of how
* much damage it will make to the rendering.
*
diff --git a/ui/text/src/test/java/androidx/ui/engine/text/ParagraphStyleTest.kt b/ui/text/src/test/java/androidx/ui/engine/text/ParagraphStyleTest.kt
deleted file mode 100644
index b103e60..0000000
--- a/ui/text/src/test/java/androidx/ui/engine/text/ParagraphStyleTest.kt
+++ /dev/null
@@ -1,50 +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.ui.engine.text
-
-import androidx.ui.engine.text.font.FontFamily
-import org.hamcrest.CoreMatchers.equalTo
-import org.junit.Assert.assertThat
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-
-@RunWith(JUnit4::class)
-class ParagraphStyleTest {
- @Test
- fun `hasFontAttributes default`() {
- val paragraphStyle = ParagraphStyle()
- assertThat(paragraphStyle.hasFontAttributes(), equalTo(false))
- }
-
- @Test
- fun `hasFontAttributes with fontFamily returns true`() {
- val paragraphStyle = ParagraphStyle(fontFamily = FontFamily("sans"))
- assertThat(paragraphStyle.hasFontAttributes(), equalTo(true))
- }
-
- @Test
- fun `hasFontAttributes with fontStyle returns true`() {
- val paragraphStyle = ParagraphStyle(fontStyle = FontStyle.Italic)
- assertThat(paragraphStyle.hasFontAttributes(), equalTo(true))
- }
-
- @Test
- fun `hasFontAttributes with fontWeight returns true`() {
- val paragraphStyle = ParagraphStyle(fontWeight = FontWeight.normal)
- assertThat(paragraphStyle.hasFontAttributes(), equalTo(true))
- }
-}
\ No newline at end of file
diff --git a/ui/text/src/test/java/androidx/ui/engine/text/ParagraphTest.kt b/ui/text/src/test/java/androidx/ui/engine/text/ParagraphTest.kt
index 551357e..f527043 100644
--- a/ui/text/src/test/java/androidx/ui/engine/text/ParagraphTest.kt
+++ b/ui/text/src/test/java/androidx/ui/engine/text/ParagraphTest.kt
@@ -16,7 +16,6 @@
package androidx.ui.engine.text
import androidx.ui.engine.geometry.Offset
-import androidx.ui.engine.window.Locale
import androidx.ui.painting.TextStyle
import com.nhaarman.mockitokotlin2.mock
import org.hamcrest.CoreMatchers.equalTo
@@ -31,56 +30,64 @@
@Test
fun `width default value`() {
val paragraphStyle = createParagraphStyle()
- val paragraph = Paragraph("", TextStyle(), paragraphStyle, listOf())
+ val paragraph = createParagraph(paragraphStyle)
+
assertThat(paragraph.width, equalTo(-1.0f))
}
@Test
fun `height default value`() {
val paragraphStyle = createParagraphStyle()
- val paragraph = Paragraph("", TextStyle(), paragraphStyle, listOf())
+ val paragraph = createParagraph(paragraphStyle)
+
assertThat(paragraph.height, equalTo(0.0f))
}
@Test
fun `minIntrinsicWidth default value`() {
val paragraphStyle = createParagraphStyle()
- val paragraph = Paragraph("", TextStyle(), paragraphStyle, listOf())
+ val paragraph = createParagraph(paragraphStyle)
+
assertThat(paragraph.minIntrinsicWidth, equalTo(0.0f))
}
@Test
fun `maxIntrinsicWidth default value`() {
val paragraphStyle = createParagraphStyle()
- val paragraph = Paragraph("", TextStyle(), paragraphStyle, listOf())
+ val paragraph = createParagraph(paragraphStyle)
+
assertThat(paragraph.maxIntrinsicWidth, equalTo(0.0f))
}
@Test
fun `alphabeticBaseline default value`() {
val paragraphStyle = createParagraphStyle()
- val paragraph = Paragraph("", TextStyle(), paragraphStyle, listOf())
+ val paragraph = createParagraph(paragraphStyle)
+
assertThat(paragraph.baseline, equalTo(Float.MAX_VALUE))
}
@Test
fun `didExceedMaxLines default value`() {
val paragraphStyle = createParagraphStyle()
- val paragraph = Paragraph("", TextStyle(), paragraphStyle, listOf())
+ val paragraph = createParagraph(paragraphStyle)
+
assertThat(paragraph.didExceedMaxLines, equalTo(false))
}
@Test(expected = IllegalStateException::class)
fun `paint throws exception if layout is not called`() {
val paragraphStyle = createParagraphStyle()
- val paragraph = Paragraph("", TextStyle(), paragraphStyle, listOf())
+ val paragraph = createParagraph(paragraphStyle)
+
paragraph.paint(mock(), 0.0f, 0.0f)
}
@Test(expected = IllegalStateException::class)
fun `getPositionForOffset throws exception if layout is not called`() {
val paragraphStyle = createParagraphStyle()
- val paragraph = Paragraph("", TextStyle(), paragraphStyle, listOf())
+ val paragraph = createParagraph(paragraphStyle)
+
paragraph.getPositionForOffset(Offset(0.0f, 0.0f))
}
@@ -90,7 +97,7 @@
val textStart = 0
val textEnd = text.length
val paragraphStyle = createParagraphStyle()
- val paragraph = Paragraph(text, TextStyle(), paragraphStyle, listOf())
+ val paragraph = createParagraph(paragraphStyle)
paragraph.getPathForRange(textEnd, textStart)
}
@@ -101,7 +108,7 @@
val textStart = 0
val textEnd = text.length
val paragraphStyle = createParagraphStyle()
- val paragraph = Paragraph(text, TextStyle(), paragraphStyle, listOf())
+ val paragraph = createParagraph(paragraphStyle)
paragraph.getPathForRange(textStart - 2, textEnd - 1)
}
@@ -112,32 +119,31 @@
val textStart = 0
val textEnd = text.length
val paragraphStyle = createParagraphStyle()
- val paragraph = Paragraph(text, TextStyle(), paragraphStyle, listOf())
+ val paragraph = createParagraph(paragraphStyle)
paragraph.getPathForRange(textStart, textEnd + 1)
}
+ private fun createParagraph(paragraphStyle: ParagraphStyle): Paragraph {
+ return Paragraph(text = "",
+ style = TextStyle(),
+ paragraphStyle = paragraphStyle,
+ textStyles = listOf())
+ }
+
private fun createParagraphStyle(): ParagraphStyle {
val textAlign = TextAlign.End
val textDirection = TextDirection.Rtl
- val fontWeight = FontWeight.bold
- val fontStyle = FontStyle.Italic
val maxLines = 2
- val fontSize = 1.0f
val lineHeight = 2.0f
val ellipsis = false
- val locale = Locale("en")
return ParagraphStyle(
textAlign = textAlign,
textDirection = textDirection,
- fontWeight = fontWeight,
- fontStyle = fontStyle,
maxLines = maxLines,
- fontSize = fontSize,
lineHeight = lineHeight,
- ellipsis = ellipsis,
- locale = locale
+ ellipsis = ellipsis
)
}
}
\ No newline at end of file
diff --git a/ui/text/src/test/java/androidx/ui/painting/TextPainterTest.kt b/ui/text/src/test/java/androidx/ui/painting/TextPainterTest.kt
index 39bffe5..0042fac 100644
--- a/ui/text/src/test/java/androidx/ui/painting/TextPainterTest.kt
+++ b/ui/text/src/test/java/androidx/ui/painting/TextPainterTest.kt
@@ -208,12 +208,10 @@
val paragraphStyle = textPainter.createParagraphStyle()
- assertThat(paragraphStyle.fontSize).isNull()
assertThat(paragraphStyle.textAlign).isEqualTo(TextAlign.Center)
assertThat(paragraphStyle.textDirection).isEqualTo(TextDirection.Rtl)
assertThat(paragraphStyle.maxLines).isEqualTo(maxLines)
assertThat(paragraphStyle.ellipsis).isEqualTo(true)
- assertThat(paragraphStyle.locale).isEqualTo(locale)
}
@Test
@@ -240,12 +238,10 @@
val paragraphStyle = textPainter.createParagraphStyle()
- assertThat(paragraphStyle.fontSize).isEqualTo(fontSize * scaleFactor)
assertThat(paragraphStyle.textAlign).isEqualTo(TextAlign.Center)
assertThat(paragraphStyle.textDirection).isEqualTo(TextDirection.Rtl)
assertThat(paragraphStyle.maxLines).isEqualTo(maxLines)
assertThat(paragraphStyle.ellipsis).isEqualTo(true)
- assertThat(paragraphStyle.locale).isEqualTo(locale)
}
@Test
diff --git a/ui/text/src/test/java/androidx/ui/painting/TextSpanTest.kt b/ui/text/src/test/java/androidx/ui/painting/TextSpanTest.kt
index 87fb9d7..ca20d6e 100644
--- a/ui/text/src/test/java/androidx/ui/painting/TextSpanTest.kt
+++ b/ui/text/src/test/java/androidx/ui/painting/TextSpanTest.kt
@@ -304,16 +304,6 @@
}
@Test
- fun `toAnnotatedString with includeRootStyle false`() {
- val textStyle = TextStyle(fontSize = 10f)
- val text = "Hello"
- val textSpan = TextSpan(style = textStyle, text = text)
- val annotatedString = textSpan.toAnnotatedString(false)
-
- assertThat(annotatedString.textStyles.size).isEqualTo(0)
- }
-
- @Test
fun `toAnnotatedString with nested TextSpan plain text`() {
val text1 = "Hello"
val text2 = "World"
diff --git a/ui/text/src/test/java/androidx/ui/painting/TextStyleTest.kt b/ui/text/src/test/java/androidx/ui/painting/TextStyleTest.kt
index f5dfaf3d..a1ab8b45 100644
--- a/ui/text/src/test/java/androidx/ui/painting/TextStyleTest.kt
+++ b/ui/text/src/test/java/androidx/ui/painting/TextStyleTest.kt
@@ -22,12 +22,8 @@
import androidx.ui.engine.text.FontStyle
import androidx.ui.engine.text.FontSynthesis
import androidx.ui.engine.text.FontWeight
-import androidx.ui.engine.text.ParagraphStyle
-import androidx.ui.engine.text.TextAlign
import androidx.ui.engine.text.TextDecoration
-import androidx.ui.engine.text.TextDirection
import androidx.ui.engine.text.TextGeometricTransform
-import androidx.ui.engine.text.TextIndent
import androidx.ui.engine.text.font.FontFamily
import androidx.ui.engine.text.lerp
import androidx.ui.engine.window.Locale
@@ -1562,87 +1558,6 @@
}
@Test
- fun `getParagraphStyle with text align`() {
- val fontSize = 10.0f
- val color = Color(0xFF00FF00.toInt())
- val fontSynthesis = FontSynthesis.Style
- val textStyle = TextStyle(
- fontSize = fontSize,
- fontWeight = FontWeight.w800,
- color = color,
- fontSynthesis = fontSynthesis
- )
-
- assertThat(textStyle.fontFamily).isNull()
- assertThat(textStyle.fontSize).isEqualTo(fontSize)
- assertThat(textStyle.fontWeight).isEqualTo(FontWeight.w800)
- assertThat(textStyle.color).isEqualTo(color)
-
- val paragraphStyle = textStyle.getParagraphStyle(textAlign = TextAlign.Center)
-
- assertThat(paragraphStyle).isEqualTo(
- ParagraphStyle(
- textAlign = TextAlign.Center,
- fontWeight = FontWeight.w800,
- fontSize = fontSize,
- fontSynthesis = fontSynthesis
- )
- )
- }
-
- @Test
- fun `getParagraphStyle with LTR text direction`() {
- val paragraphStyle = TextStyle().getParagraphStyle(textDirection = TextDirection.Ltr)
-
- assertThat(paragraphStyle).isEqualTo(
- ParagraphStyle(
- textDirection = TextDirection.Ltr,
- fontSize = _defaultFontSize
- )
- )
- }
-
- @Test
- fun `getParagraphStyle with line height`() {
- val lineHeight = 1.2f
-
- val paragraphStyle = TextStyle().getParagraphStyle(lineHeight = lineHeight)
-
- assertThat(paragraphStyle).isEqualTo(
- ParagraphStyle(
- lineHeight = lineHeight,
- fontSize = _defaultFontSize
- )
- )
- }
-
- @Test
- fun `getParagraphStyle with text indent`() {
- val textIndent = TextIndent(firstLine = 10.px, restLine = 11.px)
-
- val paragraphStyle = TextStyle().getParagraphStyle(textIndent = textIndent)
-
- assertThat(paragraphStyle).isEqualTo(
- ParagraphStyle(
- textIndent = textIndent,
- fontSize = _defaultFontSize
- )
- )
- }
-
- @Test
- fun `getParagraphStyle with RTL text direction`() {
- val paragraphStyle = TextStyle().getParagraphStyle(textDirection = TextDirection.Rtl)
-
- assertThat(paragraphStyle).isEqualTo(
- ParagraphStyle(
- textDirection = TextDirection.Rtl,
- fontSize = _defaultFontSize
- )
- )
- }
-
- @Test
fun `debugLabel with constructor default values`() {
val unknown = TextStyle()
diff --git a/viewpager/api/api_lint.ignore b/viewpager/api/api_lint.ignore
new file mode 100644
index 0000000..9a70682
--- /dev/null
+++ b/viewpager/api/api_lint.ignore
@@ -0,0 +1,21 @@
+// Baseline format: 1.0
+ConcreteCollection: androidx.viewpager.widget.ViewPager#addFocusables(java.util.ArrayList<android.view.View>, int, int) parameter #0:
+ Parameter type is concrete collection (`java.util.ArrayList`); must be higher-level interface
+ConcreteCollection: androidx.viewpager.widget.ViewPager#addTouchables(java.util.ArrayList<android.view.View>) parameter #0:
+ Parameter type is concrete collection (`java.util.ArrayList`); must be higher-level interface
+
+
+ListenerInterface: androidx.viewpager.widget.ViewPager.SimpleOnPageChangeListener:
+ Listeners should be an interface, or otherwise renamed Callback: SimpleOnPageChangeListener
+
+
+ParcelCreator: androidx.viewpager.widget.ViewPager.SavedState:
+ Parcelable requires `public int describeContents()`; missing in androidx.viewpager.widget.ViewPager.SavedState
+
+
+ParcelNotFinal: androidx.viewpager.widget.ViewPager.SavedState:
+ Parcelable classes must be final: androidx.viewpager.widget.ViewPager.SavedState is not final
+
+
+VisiblySynchronized: androidx.viewpager.widget.PagerAdapter#notifyDataSetChanged():
+ Internal locks must not be exposed (synchronizing on this or class is still externally observable): method androidx.viewpager.widget.PagerAdapter.notifyDataSetChanged()
diff --git a/wear/api/api_lint.ignore b/wear/api/api_lint.ignore
new file mode 100644
index 0000000..6af38c4
--- /dev/null
+++ b/wear/api/api_lint.ignore
@@ -0,0 +1,33 @@
+// Baseline format: 1.0
+AcronymName: androidx.wear.widget.CurvingLayoutCallback#adjustAnchorOffsetXY(android.view.View, float[]):
+ Acronyms should not be capitalized in method names: was `adjustAnchorOffsetXY`, should this be `adjustAnchorOffsetXy`?
+
+
+ActionValue: androidx.wear.ambient.AmbientMode#EXTRA_BURN_IN_PROTECTION:
+ Inconsistent extra value; expected `androidx.wear.ambient.extra.BURN_IN_PROTECTION`, was `com.google.android.wearable.compat.extra.BURN_IN_PROTECTION`
+ActionValue: androidx.wear.ambient.AmbientMode#EXTRA_LOWBIT_AMBIENT:
+ Inconsistent extra value; expected `androidx.wear.ambient.extra.LOWBIT_AMBIENT`, was `com.google.android.wearable.compat.extra.LOWBIT_AMBIENT`
+ActionValue: androidx.wear.ambient.AmbientModeSupport#EXTRA_BURN_IN_PROTECTION:
+ Inconsistent extra value; expected `androidx.wear.ambient.extra.BURN_IN_PROTECTION`, was `com.google.android.wearable.compat.extra.BURN_IN_PROTECTION`
+ActionValue: androidx.wear.ambient.AmbientModeSupport#EXTRA_LOWBIT_AMBIENT:
+ Inconsistent extra value; expected `androidx.wear.ambient.extra.LOWBIT_AMBIENT`, was `com.google.android.wearable.compat.extra.LOWBIT_AMBIENT`
+
+
+CallbackMethodName: androidx.wear.widget.CurvingLayoutCallback:
+ Callback method names must follow the on<Something> style: adjustAnchorOffsetXY
+
+
+ForbiddenSuperClass: androidx.wear.activity.ConfirmationActivity:
+ ConfirmationActivity should not extend `Activity`. Activity subclasses are impossible to compose. Expose a composable API instead.
+
+
+NoClone: androidx.wear.ambient.AmbientMode#dump(String, java.io.FileDescriptor, java.io.PrintWriter, String[]) parameter #1:
+ Must use ParcelFileDescriptor instead of FileDescriptor in parameter fd in androidx.wear.ambient.AmbientMode.dump(String prefix, java.io.FileDescriptor fd, java.io.PrintWriter writer, String[] args)
+NoClone: androidx.wear.ambient.AmbientModeSupport#dump(String, java.io.FileDescriptor, java.io.PrintWriter, String[]) parameter #1:
+ Must use ParcelFileDescriptor instead of FileDescriptor in parameter fd in androidx.wear.ambient.AmbientModeSupport.dump(String prefix, java.io.FileDescriptor fd, java.io.PrintWriter writer, String[] args)
+
+
+RegistrationName: androidx.wear.widget.SwipeDismissFrameLayout#addCallback(androidx.wear.widget.SwipeDismissFrameLayout.Callback):
+ Callback methods should be named register/unregister; was addCallback
+RegistrationName: androidx.wear.widget.SwipeDismissFrameLayout#removeCallback(androidx.wear.widget.SwipeDismissFrameLayout.Callback):
+ Callback methods should be named register/unregister; was removeCallback
diff --git a/webkit/api/api_lint.ignore b/webkit/api/api_lint.ignore
new file mode 100644
index 0000000..992ae35
--- /dev/null
+++ b/webkit/api/api_lint.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+IntentName: androidx.webkit.WebViewFeature#DISABLED_ACTION_MODE_MENU_ITEMS:
+ Intent action constant name must be ACTION_FOO: DISABLED_ACTION_MODE_MENU_ITEMS
diff --git a/work/workmanager-gcm/build.gradle b/work/workmanager-gcm/build.gradle
index a1dfc7e..8f763e1 100644
--- a/work/workmanager-gcm/build.gradle
+++ b/work/workmanager-gcm/build.gradle
@@ -29,6 +29,10 @@
}
android {
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_1_7
+ targetCompatibility = JavaVersion.VERSION_1_7
+ }
buildTypes {
debug {
// Breaks Kotlin compiler
diff --git a/work/workmanager-ktx/build.gradle b/work/workmanager-ktx/build.gradle
index d60bfed..93c1a0e 100644
--- a/work/workmanager-ktx/build.gradle
+++ b/work/workmanager-ktx/build.gradle
@@ -29,6 +29,10 @@
}
android {
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_1_7
+ targetCompatibility = JavaVersion.VERSION_1_7
+ }
buildTypes {
debug {
// Breaks Kotlin compiler
diff --git a/work/workmanager-rxjava2/build.gradle b/work/workmanager-rxjava2/build.gradle
index ccac16c..95e03f5 100644
--- a/work/workmanager-rxjava2/build.gradle
+++ b/work/workmanager-rxjava2/build.gradle
@@ -26,6 +26,13 @@
id("kotlin-android")
}
+android {
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_1_7
+ targetCompatibility = JavaVersion.VERSION_1_7
+ }
+}
+
dependencies {
api(project(":work:work-runtime"))
api(RX_JAVA)
diff --git a/work/workmanager-testing/build.gradle b/work/workmanager-testing/build.gradle
index d3813f8..87d1182 100644
--- a/work/workmanager-testing/build.gradle
+++ b/work/workmanager-testing/build.gradle
@@ -28,6 +28,13 @@
id("kotlin-android")
}
+android {
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_1_7
+ targetCompatibility = JavaVersion.VERSION_1_7
+ }
+}
+
dependencies {
implementation(project(':work:work-runtime-ktx'))
implementation(ARCH_LIFECYCLE_LIVEDATA_CORE)
diff --git a/work/workmanager/api/api_lint.ignore b/work/workmanager/api/api_lint.ignore
new file mode 100644
index 0000000..cc73c5c
--- /dev/null
+++ b/work/workmanager/api/api_lint.ignore
@@ -0,0 +1,7 @@
+// Baseline format: 1.0
+AcronymName: androidx.work.Operation.State.FAILURE:
+ Acronyms should not be capitalized in class names: was `FAILURE`, should this be `Failure`?
+AcronymName: androidx.work.Operation.State.IN_PROGRESS:
+ Acronyms should not be capitalized in class names: was `IN_PROGRESS`, should this be `InProgress`?
+AcronymName: androidx.work.Operation.State.SUCCESS:
+ Acronyms should not be capitalized in class names: was `SUCCESS`, should this be `Success`?
diff --git a/work/workmanager/build.gradle b/work/workmanager/build.gradle
index 27aa9fc..9c4f719 100644
--- a/work/workmanager/build.gradle
+++ b/work/workmanager/build.gradle
@@ -29,6 +29,10 @@
}
android {
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_1_7
+ targetCompatibility = JavaVersion.VERSION_1_7
+ }
buildTypes.all {
consumerProguardFiles 'proguard-rules.pro'
}