Merge "Expose a list of default values for XAnnotation" into androidx-main
diff --git a/appactions/interaction/interaction-capabilities-core/lint-baseline.xml b/appactions/interaction/interaction-capabilities-core/lint-baseline.xml
deleted file mode 100644
index 845b3c4..0000000
--- a/appactions/interaction/interaction-capabilities-core/lint-baseline.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues name="AGP (8.1.0-beta02)" by="lint 8.1.0-beta02" client="gradle" dependencies="false"
- format="6" type="baseline" variant="all" version="8.1.0-beta02">
-
- <!-- Need to use reflection to dynamically access serializers based on type-name. These
- serializers may be coming from a downstream artifact and are not available at compile-time.
- This also aids in a binary-bloat optimization where all serializers start off a dead code and
- then we instruct proguard to only retain the serializers for types that are explicitly
- referenced and prune others.
- -->
- <issue
- errorLine1=" .map { it.invoke(null) as BuiltInTypeSerializer<*> }"
- errorLine2=" ~~~~~~~~~~~~~~~"
- id="BanUncheckedReflection"
- message="Calling `Method.invoke` without an SDK check">
- <location
- file="src/main/java/androidx/appactions/interaction/capabilities/serializers/types/BuiltInTypeSerializerRegistry.kt" />
- </issue>
-
-</issues>
diff --git a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/serializers/types/BuiltInTypeSerializerRegistry.kt b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/serializers/types/BuiltInTypeSerializerRegistry.kt
index 0d0d278..f80c2f3 100644
--- a/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/serializers/types/BuiltInTypeSerializerRegistry.kt
+++ b/appactions/interaction/interaction-capabilities-core/src/main/java/androidx/appactions/interaction/capabilities/serializers/types/BuiltInTypeSerializerRegistry.kt
@@ -52,6 +52,7 @@
* [BuiltInTypeSerializer].
* @param getClassOrNull Functor that returns a class ref given its canonical name, or null.
*/
+@Suppress("BanUncheckedReflection")
class BuiltInTypeSerializerRegistry(
serializerRegistryClassNames: List<String>,
getClassOrNull: (canonicalName: String) -> Class<*>?
@@ -61,6 +62,11 @@
Collections.synchronizedMap(mutableMapOf())
init {
+ // Need to use reflection to dynamically access serializers based on type-name. These
+ // serializers may be coming from a downstream artifact and are not available at
+ // compile-time. This also aids in a binary-bloat optimization where all serializers start
+ // off a dead code and then we instruct proguard to only retain the serializers for types
+ // that are explicitly referenced and prune others.
val serializers =
serializerRegistryClassNames
// object AllProductivitySerializers
diff --git a/appsearch/appsearch/api/current.txt b/appsearch/appsearch/api/current.txt
index 6724a679..96049d0 100644
--- a/appsearch/appsearch/api/current.txt
+++ b/appsearch/appsearch/api/current.txt
@@ -31,12 +31,6 @@
@java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD}) public static @interface Document.DoubleProperty {
method public abstract String name() default "";
method public abstract boolean required() default false;
- method public abstract Class<?> serializer() default androidx.appsearch.annotation.Document.DoubleProperty.DefaultSerializer.class;
- }
-
- public static final class Document.DoubleProperty.DefaultSerializer {
- method public static double deserialize(double);
- method public static double serialize(double);
}
@java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD}) public static @interface Document.Id {
@@ -46,12 +40,13 @@
method public abstract int indexingType() default androidx.appsearch.app.AppSearchSchema.LongPropertyConfig.INDEXING_TYPE_NONE;
method public abstract String name() default "";
method public abstract boolean required() default false;
- method public abstract Class<?> serializer() default androidx.appsearch.annotation.Document.LongProperty.DefaultSerializer.class;
+ method public abstract Class<? extends androidx.appsearch.app.LongSerializer<?>> serializer() default androidx.appsearch.annotation.Document.LongProperty.DefaultSerializer.class;
}
- public static final class Document.LongProperty.DefaultSerializer {
- method public static long deserialize(long);
- method public static long serialize(long);
+ public static final class Document.LongProperty.DefaultSerializer implements androidx.appsearch.app.LongSerializer<java.lang.Long> {
+ ctor public Document.LongProperty.DefaultSerializer();
+ method public Long deserialize(long);
+ method public long serialize(Long);
}
@java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD}) public static @interface Document.Namespace {
@@ -65,13 +60,14 @@
method public abstract int joinableValueType() default androidx.appsearch.app.AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE;
method public abstract String name() default "";
method public abstract boolean required() default false;
- method public abstract Class<?> serializer() default androidx.appsearch.annotation.Document.StringProperty.DefaultSerializer.class;
+ method public abstract Class<? extends androidx.appsearch.app.StringSerializer<?>> serializer() default androidx.appsearch.annotation.Document.StringProperty.DefaultSerializer.class;
method public abstract int tokenizerType() default androidx.appsearch.app.AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN;
}
- public static final class Document.StringProperty.DefaultSerializer {
- method public static String deserialize(String);
- method public static String serialize(String);
+ public static final class Document.StringProperty.DefaultSerializer implements androidx.appsearch.app.StringSerializer<java.lang.String> {
+ ctor public Document.StringProperty.DefaultSerializer();
+ method public String deserialize(String);
+ method public String serialize(String);
}
@java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD}) public static @interface Document.TtlMillis {
@@ -380,6 +376,11 @@
method public androidx.appsearch.app.JoinSpec.Builder setNestedSearch(String, androidx.appsearch.app.SearchSpec);
}
+ public interface LongSerializer<T> {
+ method public T? deserialize(long);
+ method public long serialize(T);
+ }
+
public abstract class Migrator {
ctor public Migrator();
method @WorkerThread public abstract androidx.appsearch.app.GenericDocument onDowngrade(int, int, androidx.appsearch.app.GenericDocument);
@@ -701,6 +702,11 @@
method public androidx.appsearch.app.StorageInfo.Builder setSizeBytes(long);
}
+ public interface StringSerializer<T> {
+ method public T? deserialize(String);
+ method public String serialize(T);
+ }
+
}
package androidx.appsearch.exceptions {
diff --git a/appsearch/appsearch/api/restricted_current.txt b/appsearch/appsearch/api/restricted_current.txt
index 6724a679..96049d0 100644
--- a/appsearch/appsearch/api/restricted_current.txt
+++ b/appsearch/appsearch/api/restricted_current.txt
@@ -31,12 +31,6 @@
@java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD}) public static @interface Document.DoubleProperty {
method public abstract String name() default "";
method public abstract boolean required() default false;
- method public abstract Class<?> serializer() default androidx.appsearch.annotation.Document.DoubleProperty.DefaultSerializer.class;
- }
-
- public static final class Document.DoubleProperty.DefaultSerializer {
- method public static double deserialize(double);
- method public static double serialize(double);
}
@java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD}) public static @interface Document.Id {
@@ -46,12 +40,13 @@
method public abstract int indexingType() default androidx.appsearch.app.AppSearchSchema.LongPropertyConfig.INDEXING_TYPE_NONE;
method public abstract String name() default "";
method public abstract boolean required() default false;
- method public abstract Class<?> serializer() default androidx.appsearch.annotation.Document.LongProperty.DefaultSerializer.class;
+ method public abstract Class<? extends androidx.appsearch.app.LongSerializer<?>> serializer() default androidx.appsearch.annotation.Document.LongProperty.DefaultSerializer.class;
}
- public static final class Document.LongProperty.DefaultSerializer {
- method public static long deserialize(long);
- method public static long serialize(long);
+ public static final class Document.LongProperty.DefaultSerializer implements androidx.appsearch.app.LongSerializer<java.lang.Long> {
+ ctor public Document.LongProperty.DefaultSerializer();
+ method public Long deserialize(long);
+ method public long serialize(Long);
}
@java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD}) public static @interface Document.Namespace {
@@ -65,13 +60,14 @@
method public abstract int joinableValueType() default androidx.appsearch.app.AppSearchSchema.StringPropertyConfig.JOINABLE_VALUE_TYPE_NONE;
method public abstract String name() default "";
method public abstract boolean required() default false;
- method public abstract Class<?> serializer() default androidx.appsearch.annotation.Document.StringProperty.DefaultSerializer.class;
+ method public abstract Class<? extends androidx.appsearch.app.StringSerializer<?>> serializer() default androidx.appsearch.annotation.Document.StringProperty.DefaultSerializer.class;
method public abstract int tokenizerType() default androidx.appsearch.app.AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN;
}
- public static final class Document.StringProperty.DefaultSerializer {
- method public static String deserialize(String);
- method public static String serialize(String);
+ public static final class Document.StringProperty.DefaultSerializer implements androidx.appsearch.app.StringSerializer<java.lang.String> {
+ ctor public Document.StringProperty.DefaultSerializer();
+ method public String deserialize(String);
+ method public String serialize(String);
}
@java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD}) public static @interface Document.TtlMillis {
@@ -380,6 +376,11 @@
method public androidx.appsearch.app.JoinSpec.Builder setNestedSearch(String, androidx.appsearch.app.SearchSpec);
}
+ public interface LongSerializer<T> {
+ method public T? deserialize(long);
+ method public long serialize(T);
+ }
+
public abstract class Migrator {
ctor public Migrator();
method @WorkerThread public abstract androidx.appsearch.app.GenericDocument onDowngrade(int, int, androidx.appsearch.app.GenericDocument);
@@ -701,6 +702,11 @@
method public androidx.appsearch.app.StorageInfo.Builder setSizeBytes(long);
}
+ public interface StringSerializer<T> {
+ method public T? deserialize(String);
+ method public String serialize(T);
+ }
+
}
package androidx.appsearch.exceptions {
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/annotation/Document.java b/appsearch/appsearch/src/main/java/androidx/appsearch/annotation/Document.java
index e1a3fcb..6b09339 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/annotation/Document.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/annotation/Document.java
@@ -18,6 +18,8 @@
import androidx.annotation.NonNull;
import androidx.appsearch.app.AppSearchSchema;
+import androidx.appsearch.app.LongSerializer;
+import androidx.appsearch.app.StringSerializer;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
@@ -259,18 +261,7 @@
* <p>Useful for representing properties using rich types that boil down to simple string
* values in the database.
*
- * <p>The referenced class must satisfy the following:
- *
- * <ol>
- * <li>
- * Have a static method called {@code serialize} that converts the property's Java
- * type to a {@link String}.
- * </li>
- * <li>
- * Have a static method called {@code deserialize} that converts a {@link String} to
- * the property's Java type or returns null if deserialization failed.
- * </li>
- * </ol>
+ * <p>The referenced class must have a public zero params constructor.
*
* <p>For example:
*
@@ -282,17 +273,21 @@
* @Document.StringProperty(serializer = SomeRichTypeSerializer.class)
* public SomeRichType getMyProperty();
*
- * public final class SomeRichTypeSerializer {
- * public static String serialize(SomeRichType instance) {...}
+ * public final class SomeRichTypeSerializer implements StringSerializer<SomeRichType> {
*
+ * @Override
+ * @NonNull
+ * public String serialize(@NonNull SomeRichType instance) {...}
+ *
+ * @Override
* @Nullable
- * public static SomeRichType deserialize(String string) {...}
+ * public SomeRichType deserialize(@NonNull String string) {...}
* }
* }
* }
* </pre>
*/
- Class<?> serializer() default DefaultSerializer.class;
+ Class<? extends StringSerializer<?>> serializer() default DefaultSerializer.class;
/**
* Configures whether this property must be specified for the document to be valid.
@@ -305,16 +300,16 @@
*/
boolean required() default false;
- final class DefaultSerializer {
- private DefaultSerializer() {}
-
+ final class DefaultSerializer implements StringSerializer<String> {
+ @Override
@NonNull
- public static String serialize(@NonNull String value) {
- return value;
+ public String serialize(@NonNull String instance) {
+ return instance;
}
+ @Override
@NonNull
- public static String deserialize(@NonNull String string) {
+ public String deserialize(@NonNull String string) {
return string;
}
}
@@ -387,22 +382,11 @@
* <p>Useful for representing properties using rich types that boil down to simple 64-bit
* integer values in the database.
*
- * <p>The referenced class must satisfy the following:
- *
- * <ol>
- * <li>
- * Have a static method called {@code serialize} that converts the property's Java
- * type to a {@link Long}.
- * </li>
- * <li>
- * Have a static method called {@code deserialize} that converts a {@link Long} to
- * the property's Java type or returns null if deserialization failed.
- * </li>
- * </ol>
+ * <p>The referenced class must have a public zero params constructor.
*
* <p>See {@link StringProperty#serializer()} for an example of a serializer.
*/
- Class<?> serializer() default DefaultSerializer.class;
+ Class<? extends LongSerializer<?>> serializer() default DefaultSerializer.class;
/**
* Configures whether this property must be specified for the document to be valid.
@@ -415,15 +399,17 @@
*/
boolean required() default false;
- final class DefaultSerializer {
- private DefaultSerializer() {}
-
- public static long serialize(long value) {
+ final class DefaultSerializer implements LongSerializer<Long> {
+ @Override
+ public long serialize(@NonNull @SuppressWarnings("AutoBoxing") Long value) {
return value;
}
- public static long deserialize(long l) {
- return l;
+ @Override
+ @NonNull
+ @SuppressWarnings("AutoBoxing")
+ public Long deserialize(long value) {
+ return value;
}
}
}
@@ -444,29 +430,6 @@
String name() default "";
/**
- * Configures how a property should be converted to and from a {@link Double}.
- *
- * <p>Useful for representing properties using rich types that boil down to simple
- * double-precision decimal values in the database.
- *
- * <p>The referenced class must satisfy the following:
- *
- * <ol>
- * <li>
- * Have a static method called {@code serialize} that converts the property's Java
- * type to a {@link Double}.
- * </li>
- * <li>
- * Have a static method called {@code deserialize} that converts a {@link Double} to
- * the property's Java type or returns null if deserialization failed.
- * </li>
- * </ol>
- *
- * <p>See {@link StringProperty#serializer()} for an example of a serializer.
- */
- Class<?> serializer() default DefaultSerializer.class;
-
- /**
* Configures whether this property must be specified for the document to be valid.
*
* <p>This attribute does not apply to properties of a repeated type (e.g. a list).
@@ -476,18 +439,6 @@
* this attribute to {@code true}.
*/
boolean required() default false;
-
- final class DefaultSerializer {
- private DefaultSerializer() {}
-
- public static double serialize(double value) {
- return value;
- }
-
- public static double deserialize(double d) {
- return d;
- }
- }
}
/** Configures a boolean member field of a class as a property known to AppSearch. */
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/LongSerializer.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/LongSerializer.java
new file mode 100644
index 0000000..7c597bc
--- /dev/null
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/LongSerializer.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.app;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * Serializes some {@link T} to and from a long.
+ *
+ * @param <T> The custom type that can be serialized to a long.
+ */
+public interface LongSerializer<T> {
+ /**
+ * Serializes a {@link T} to a long.
+ */
+ long serialize(@NonNull T instance);
+
+ /**
+ * Deserializes a {@link T} from a long. Returns null if deserialization failed.
+ */
+ @Nullable
+ T deserialize(long value);
+}
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/StringSerializer.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/StringSerializer.java
new file mode 100644
index 0000000..bdfa575
--- /dev/null
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/StringSerializer.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.app;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * Serializes some {@link T} to and from a String.
+ *
+ * @param <T> The custom type that can be serialized to a String.
+ */
+public interface StringSerializer<T> {
+ /**
+ * Serializes a {@link T} to a String.
+ */
+ @NonNull
+ String serialize(@NonNull T instance);
+
+ /**
+ * Deserializes a {@link T} from a String. Returns null if deserialization failed.
+ */
+ @Nullable
+ T deserialize(@NonNull String string);
+}
diff --git a/appsearch/compiler/build.gradle b/appsearch/compiler/build.gradle
index cb9caf4..c37b877 100644
--- a/appsearch/compiler/build.gradle
+++ b/appsearch/compiler/build.gradle
@@ -31,6 +31,8 @@
implementation(libs.autoValueAnnotations)
implementation(libs.javapoet)
+ annotationProcessor(libs.autoValue)
+
// For testing, add in the compiled classes from appsearch to get access to annotations.
testImplementationAarAsJar(project(":appsearch:appsearch"))
testImplementation(libs.googleCompileTesting)
diff --git a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/IntrospectionHelper.java b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/IntrospectionHelper.java
index ad2e92e..bc30992 100644
--- a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/IntrospectionHelper.java
+++ b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/IntrospectionHelper.java
@@ -20,7 +20,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
-import androidx.annotation.VisibleForTesting;
import com.google.auto.value.AutoValue;
import com.squareup.javapoet.ClassName;
@@ -57,18 +56,18 @@
* @exportToFramework:hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-class IntrospectionHelper {
- @VisibleForTesting
+public class IntrospectionHelper {
static final String GEN_CLASS_PREFIX = "$$__AppSearch__";
static final String APPSEARCH_PKG = "androidx.appsearch.app";
static final String APPSEARCH_EXCEPTION_PKG = "androidx.appsearch.exceptions";
static final String APPSEARCH_EXCEPTION_SIMPLE_NAME = "AppSearchException";
- static final String DOCUMENT_ANNOTATION_CLASS = "androidx.appsearch.annotation.Document";
+ public static final String DOCUMENT_ANNOTATION_CLASS = "androidx.appsearch.annotation.Document";
static final String ID_CLASS = "androidx.appsearch.annotation.Document.Id";
static final String NAMESPACE_CLASS = "androidx.appsearch.annotation.Document.Namespace";
static final String CREATION_TIMESTAMP_MILLIS_CLASS =
"androidx.appsearch.annotation.Document.CreationTimestampMillis";
- static final String TTL_MILLIS_CLASS = "androidx.appsearch.annotation.Document.TtlMillis";
+ static final String TTL_MILLIS_CLASS = "androidx.appsearch.annotation.Document"
+ + ".TtlMillis";
static final String SCORE_CLASS = "androidx.appsearch.annotation.Document.Score";
static final String BUILDER_PRODUCER_CLASS =
"androidx.appsearch.annotation.Document.BuilderProducer";
diff --git a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/BooleanPropertyAnnotation.java b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/BooleanPropertyAnnotation.java
new file mode 100644
index 0000000..0d84e17
--- /dev/null
+++ b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/BooleanPropertyAnnotation.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.compiler.annotationwrapper;
+
+import static androidx.appsearch.compiler.IntrospectionHelper.DOCUMENT_ANNOTATION_CLASS;
+
+import androidx.annotation.NonNull;
+
+import com.google.auto.value.AutoValue;
+
+import java.util.Map;
+
+/**
+ * An instance of the {@code @Document.BooleanProperty} annotation.
+ */
+@AutoValue
+public abstract class BooleanPropertyAnnotation extends DataPropertyAnnotation {
+ public static final String SIMPLE_CLASS_NAME = "BooleanProperty";
+ public static final String CLASS_NAME = DOCUMENT_ANNOTATION_CLASS + "." + SIMPLE_CLASS_NAME;
+
+ public BooleanPropertyAnnotation() {
+ super(SIMPLE_CLASS_NAME);
+ }
+
+ /**
+ * @param defaultName The name to use for the annotated property in case the annotation
+ * params do not mention an explicit name.
+ */
+ @NonNull
+ static BooleanPropertyAnnotation parse(
+ @NonNull Map<String, Object> annotationParams, @NonNull String defaultName) {
+ String name = (String) annotationParams.get("name");
+ return new AutoValue_BooleanPropertyAnnotation(
+ name.isEmpty() ? defaultName : name, (boolean) annotationParams.get("required"));
+ }
+}
diff --git a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/BytesPropertyAnnotation.java b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/BytesPropertyAnnotation.java
new file mode 100644
index 0000000..35cc428
--- /dev/null
+++ b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/BytesPropertyAnnotation.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.compiler.annotationwrapper;
+
+import static androidx.appsearch.compiler.IntrospectionHelper.DOCUMENT_ANNOTATION_CLASS;
+
+import androidx.annotation.NonNull;
+
+import com.google.auto.value.AutoValue;
+
+import java.util.Map;
+
+/**
+ * An instance of the {@code @Document.BytesProperty} annotation.
+ */
+@AutoValue
+public abstract class BytesPropertyAnnotation extends DataPropertyAnnotation {
+ public static final String SIMPLE_CLASS_NAME = "BytesProperty";
+ public static final String CLASS_NAME = DOCUMENT_ANNOTATION_CLASS + "." + SIMPLE_CLASS_NAME;
+
+ public BytesPropertyAnnotation() {
+ super(SIMPLE_CLASS_NAME);
+ }
+
+ /**
+ * @param defaultName The name to use for the annotated property in case the annotation
+ * params do not mention an explicit name.
+ */
+ @NonNull
+ static BytesPropertyAnnotation parse(
+ @NonNull Map<String, Object> annotationParams, @NonNull String defaultName) {
+ String name = (String) annotationParams.get("name");
+ return new AutoValue_BytesPropertyAnnotation(
+ name.isEmpty() ? defaultName : name, (boolean) annotationParams.get("required"));
+ }
+}
diff --git a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/DataPropertyAnnotation.java b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/DataPropertyAnnotation.java
new file mode 100644
index 0000000..245072a
--- /dev/null
+++ b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/DataPropertyAnnotation.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.compiler.annotationwrapper;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appsearch.compiler.IntrospectionHelper;
+
+import java.util.Map;
+
+import javax.lang.model.element.AnnotationMirror;
+
+/**
+ * An instance of an annotation for a data property e.g. {@code @Document.StringProperty}.
+ *
+ * <p>Is one of:
+ * <ul>
+ * <li>{@link StringPropertyAnnotation}</li>
+ * <li>{@link DocumentPropertyAnnotation}</li>
+ * <li>{@link LongPropertyAnnotation}</li>
+ * <li>{@link DoublePropertyAnnotation}</li>
+ * <li>{@link BooleanPropertyAnnotation}</li>
+ * <li>{@link BytesPropertyAnnotation}</li>
+ * </ul>
+ */
+public abstract class DataPropertyAnnotation implements PropertyAnnotation {
+ @NonNull
+ private final String mSimpleClassName;
+
+ DataPropertyAnnotation(@NonNull String simpleClassName) {
+ mSimpleClassName = simpleClassName;
+ }
+
+ /**
+ * Attempts to parse an {@link AnnotationMirror} into a {@link DataPropertyAnnotation}, or null.
+ *
+ * @param defaultName The name to use for the annotated property in case the annotation
+ * params do not mention an explicit name.
+ */
+ @Nullable
+ public static DataPropertyAnnotation tryParse(
+ @NonNull AnnotationMirror annotation,
+ @NonNull String defaultName,
+ @NonNull IntrospectionHelper helper) {
+ Map<String, Object> annotationParams = helper.getAnnotationParams(annotation);
+ String qualifiedClassName = annotation.getAnnotationType().toString();
+ switch (qualifiedClassName) {
+ case BooleanPropertyAnnotation.CLASS_NAME:
+ return BooleanPropertyAnnotation.parse(annotationParams, defaultName);
+ case BytesPropertyAnnotation.CLASS_NAME:
+ return BytesPropertyAnnotation.parse(annotationParams, defaultName);
+ case DocumentPropertyAnnotation.CLASS_NAME:
+ return DocumentPropertyAnnotation.parse(annotationParams, defaultName);
+ case DoublePropertyAnnotation.CLASS_NAME:
+ return DoublePropertyAnnotation.parse(annotationParams, defaultName);
+ case LongPropertyAnnotation.CLASS_NAME:
+ return LongPropertyAnnotation.parse(annotationParams, defaultName);
+ case StringPropertyAnnotation.CLASS_NAME:
+ return StringPropertyAnnotation.parse(annotationParams, defaultName);
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * The serialized name for the property in the database.
+ */
+ @NonNull
+ public abstract String getName();
+
+ /**
+ * Denotes whether this property must be specified for the document to be valid.
+ */
+ public abstract boolean isRequired();
+
+ @NonNull
+ @Override
+ public final String getSimpleClassName() {
+ return mSimpleClassName;
+ }
+}
diff --git a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/DocumentPropertyAnnotation.java b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/DocumentPropertyAnnotation.java
new file mode 100644
index 0000000..53c2523
--- /dev/null
+++ b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/DocumentPropertyAnnotation.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.compiler.annotationwrapper;
+
+import static androidx.appsearch.compiler.IntrospectionHelper.DOCUMENT_ANNOTATION_CLASS;
+
+import androidx.annotation.NonNull;
+
+import com.google.auto.value.AutoValue;
+
+import java.util.Map;
+
+/**
+ * An instance of the {@code @Document.DocumentProperty} annotation.
+ */
+@AutoValue
+public abstract class DocumentPropertyAnnotation extends DataPropertyAnnotation {
+ public static final String SIMPLE_CLASS_NAME = "DocumentProperty";
+ public static final String CLASS_NAME = DOCUMENT_ANNOTATION_CLASS + "." + SIMPLE_CLASS_NAME;
+
+ public DocumentPropertyAnnotation() {
+ super(SIMPLE_CLASS_NAME);
+ }
+
+ /**
+ * @param defaultName The name to use for the annotated property in case the annotation
+ * params do not mention an explicit name.
+ */
+ @NonNull
+ static DocumentPropertyAnnotation parse(
+ @NonNull Map<String, Object> annotationParams, @NonNull String defaultName) {
+ String name = (String) annotationParams.get("name");
+ return new AutoValue_DocumentPropertyAnnotation(
+ name.isEmpty() ? defaultName : name,
+ (boolean) annotationParams.get("required"),
+ (boolean) annotationParams.get("indexNestedProperties"));
+ }
+
+ /**
+ * Specifies whether fields in the nested document should be indexed.
+ */
+ public abstract boolean shouldIndexNestedProperties();
+}
diff --git a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/DoublePropertyAnnotation.java b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/DoublePropertyAnnotation.java
new file mode 100644
index 0000000..1a85e16
--- /dev/null
+++ b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/DoublePropertyAnnotation.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.compiler.annotationwrapper;
+
+import static androidx.appsearch.compiler.IntrospectionHelper.DOCUMENT_ANNOTATION_CLASS;
+
+import androidx.annotation.NonNull;
+
+import com.google.auto.value.AutoValue;
+
+import java.util.Map;
+
+/**
+ * An instance of the {@code @Document.DoubleProperty} annotation.
+ */
+@AutoValue
+public abstract class DoublePropertyAnnotation extends DataPropertyAnnotation {
+ public static final String SIMPLE_CLASS_NAME = "DoubleProperty";
+ public static final String CLASS_NAME = DOCUMENT_ANNOTATION_CLASS + "." + SIMPLE_CLASS_NAME;
+
+ public DoublePropertyAnnotation() {
+ super(SIMPLE_CLASS_NAME);
+ }
+
+ /**
+ * @param defaultName The name to use for the annotated property in case the annotation
+ * params do not mention an explicit name.
+ */
+ @NonNull
+ static DoublePropertyAnnotation parse(
+ @NonNull Map<String, Object> annotationParams, @NonNull String defaultName) {
+ String name = (String) annotationParams.get("name");
+ return new AutoValue_DoublePropertyAnnotation(
+ name.isEmpty() ? defaultName : name, (boolean) annotationParams.get("required"));
+ }
+}
diff --git a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/LongPropertyAnnotation.java b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/LongPropertyAnnotation.java
new file mode 100644
index 0000000..2f66b04
--- /dev/null
+++ b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/LongPropertyAnnotation.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.compiler.annotationwrapper;
+
+import static androidx.appsearch.compiler.IntrospectionHelper.DOCUMENT_ANNOTATION_CLASS;
+
+import androidx.annotation.NonNull;
+
+import com.google.auto.value.AutoValue;
+
+import java.util.Map;
+
+/**
+ * An instance of the {@code @Document.LongProperty} annotation.
+ */
+@AutoValue
+public abstract class LongPropertyAnnotation extends DataPropertyAnnotation {
+ public static final String SIMPLE_CLASS_NAME = "LongProperty";
+ public static final String CLASS_NAME = DOCUMENT_ANNOTATION_CLASS + "." + SIMPLE_CLASS_NAME;
+
+ public LongPropertyAnnotation() {
+ super(SIMPLE_CLASS_NAME);
+ }
+
+ /**
+ * @param defaultName The name to use for the annotated property in case the annotation
+ * params do not mention an explicit name.
+ */
+ @NonNull
+ static LongPropertyAnnotation parse(
+ @NonNull Map<String, Object> annotationParams, @NonNull String defaultName) {
+ String name = (String) annotationParams.get("name");
+ return new AutoValue_LongPropertyAnnotation(
+ name.isEmpty() ? defaultName : name,
+ (boolean) annotationParams.get("required"),
+ (int) annotationParams.get("indexingType"));
+ }
+
+ /**
+ * Specifies how a property should be indexed.
+ */
+ public abstract int getIndexingType();
+}
diff --git a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/MetadataPropertyAnnotation.java b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/MetadataPropertyAnnotation.java
new file mode 100644
index 0000000..9253048
--- /dev/null
+++ b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/MetadataPropertyAnnotation.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.compiler.annotationwrapper;
+
+import static java.util.Objects.requireNonNull;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.Arrays;
+
+import javax.lang.model.element.AnnotationMirror;
+
+/**
+ * An annotation for a metadata property e.g. {@code @Document.Id}.
+ */
+public enum MetadataPropertyAnnotation implements PropertyAnnotation {
+ ID(/* simpleClassName= */"Id"),
+ NAMESPACE(/* simpleClassName= */"Namespace"),
+ CREATION_TIMESTAMP_MILLIS(/* simpleClassName= */"CreationTimestampMillis"),
+ TTL_MILLIS(/* simpleClassName= */"TtlMillis"),
+ SCORE(/* simpleClassName= */"Score");
+
+ /**
+ * Attempts to parse an {@link AnnotationMirror} into a {@link MetadataPropertyAnnotation},
+ * or null.
+ */
+ @Nullable
+ public static MetadataPropertyAnnotation tryParse(@NonNull AnnotationMirror annotation) {
+ String qualifiedClassName = annotation.getAnnotationType().toString();
+ return Arrays.stream(values())
+ .filter(val -> val.getQualifiedClassName().equals(qualifiedClassName))
+ .findFirst()
+ .orElse(null);
+ }
+
+ @NonNull
+ private final String mSimpleClassName;
+
+ MetadataPropertyAnnotation(@NonNull String simpleClassName) {
+ mSimpleClassName = requireNonNull(simpleClassName);
+ }
+
+ @Override
+ @NonNull
+ public String getSimpleClassName() {
+ return mSimpleClassName;
+ }
+}
+
diff --git a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/PropertyAnnotation.java b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/PropertyAnnotation.java
new file mode 100644
index 0000000..41cf474
--- /dev/null
+++ b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/PropertyAnnotation.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.compiler.annotationwrapper;
+
+import static androidx.appsearch.compiler.IntrospectionHelper.DOCUMENT_ANNOTATION_CLASS;
+
+import androidx.annotation.NonNull;
+
+/**
+ * An instance of an AppSearch property annotation.
+ *
+ * <p>Is one of:
+ * <ul>
+ * <li>{@link MetadataPropertyAnnotation} e.g. {@code @Document.Id}</li>
+ * <li>{@link DataPropertyAnnotation} e.g. {@code @Document.StringProperty}</li>
+ * </ul>
+ */
+public interface PropertyAnnotation {
+ /**
+ * The annotation class' simple name.
+ *
+ * <p>For example, {@code StringProperty} for a {@link StringPropertyAnnotation}.
+ */
+ @NonNull
+ String getSimpleClassName();
+
+ /**
+ * The annotation class' qualified name
+ *
+ * <p>{@code androidx.appsearch.annotation.Document.StringProperty} for a
+ * {@link StringPropertyAnnotation}.
+ */
+ @NonNull
+ default String getQualifiedClassName() {
+ return DOCUMENT_ANNOTATION_CLASS + "." + getSimpleClassName();
+ }
+}
diff --git a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/StringPropertyAnnotation.java b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/StringPropertyAnnotation.java
new file mode 100644
index 0000000..ddcdf8e
--- /dev/null
+++ b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/annotationwrapper/StringPropertyAnnotation.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.appsearch.compiler.annotationwrapper;
+
+import static androidx.appsearch.compiler.IntrospectionHelper.DOCUMENT_ANNOTATION_CLASS;
+
+import androidx.annotation.NonNull;
+
+import com.google.auto.value.AutoValue;
+
+import java.util.Map;
+
+/**
+ * An instance of the {@code @Document.StringProperty} annotation.
+ */
+@AutoValue
+public abstract class StringPropertyAnnotation extends DataPropertyAnnotation {
+ public static final String SIMPLE_CLASS_NAME = "StringProperty";
+ public static final String CLASS_NAME = DOCUMENT_ANNOTATION_CLASS + "." + SIMPLE_CLASS_NAME;
+
+ public StringPropertyAnnotation() {
+ super(SIMPLE_CLASS_NAME);
+ }
+
+ /**
+ * @param defaultName The name to use for the annotated property in case the annotation
+ * params do not mention an explicit name.
+ */
+ @NonNull
+ static StringPropertyAnnotation parse(
+ @NonNull Map<String, Object> annotationParams, @NonNull String defaultName) {
+ String name = (String) annotationParams.get("name");
+ return new AutoValue_StringPropertyAnnotation(
+ name.isEmpty() ? defaultName : name,
+ (boolean) annotationParams.get("required"),
+ (int) annotationParams.get("tokenizerType"),
+ (int) annotationParams.get("indexingType"),
+ (int) annotationParams.get("joinableValueType"));
+ }
+
+ /**
+ * Specifies how tokens should be extracted from this property.
+ */
+ public abstract int getTokenizerType();
+
+ /**
+ * Specifies how a property should be indexed.
+ */
+ public abstract int getIndexingType();
+
+ /**
+ * Specifies how a property should be processed so that the document can be joined.
+ */
+ public abstract int getJoinableValueType();
+}
diff --git a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/UserspaceTracingTest.kt b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/InMemoryTracingTest.kt
similarity index 84%
rename from benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/UserspaceTracingTest.kt
rename to benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/InMemoryTracingTest.kt
index 20f264f..ad72a77 100644
--- a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/UserspaceTracingTest.kt
+++ b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/InMemoryTracingTest.kt
@@ -16,6 +16,7 @@
package androidx.benchmark
+import android.os.Process
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
@@ -23,35 +24,29 @@
import kotlin.test.assertNotNull
import org.junit.After
import org.junit.Assert.assertEquals
-import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import perfetto.protos.ThreadDescriptor
import perfetto.protos.TracePacket
import perfetto.protos.TrackDescriptor
import perfetto.protos.TrackEvent
@RunWith(AndroidJUnit4::class)
@SmallTest
-class UserspaceTracingTest {
+class InMemoryTracingTest {
@Before
@After
fun setup() {
- UserspaceTracing.commitToTrace() // reset
+ InMemoryTracing.clearEvents() // reset
}
@Test
fun emptyTrace() {
- val beforeTime = System.nanoTime()
- UserspaceTracing.commitToTrace() // reset, and trigger first event in next trace
- val afterTime = System.nanoTime()
-
- val trace = UserspaceTracing.commitToTrace() // capture trace
+ val trace = InMemoryTracing.commitToTrace("testLabel") // capture trace
assertEquals(1, trace.packet.size)
val packet = trace.packet.first()
-
- assertTrue(packet.timestamp in beforeTime..afterTime)
assertEquals(
packet,
TracePacket(
@@ -60,7 +55,9 @@
incremental_state_cleared = true,
track_descriptor = TrackDescriptor(
uuid = packet.track_descriptor?.uuid,
- name = "Macrobenchmark"
+ name = "testLabel",
+ thread = ThreadDescriptor(pid = Process.myPid(), tid = Process.myTid()),
+ disallow_merging_with_system_tracks = true
)
)
)
@@ -69,16 +66,19 @@
@Test
fun minimalTrace() {
val beforeTime = System.nanoTime()
- userspaceTrace("test trace section") {}
+ inMemoryTrace("test trace section") {}
val afterTime = System.nanoTime()
- val trace = UserspaceTracing.commitToTrace()
+ val trace = InMemoryTracing.commitToTrace("testLabel")
assertEquals(3, trace.packet.size)
+ // verify track
val descriptor = trace.packet.first().track_descriptor
- assertNotNull(descriptor) // verify track
+ assertNotNull(descriptor)
+ assertEquals("testLabel", descriptor.name)
+ // verify events
trace.packet[1].apply {
assert(timestamp in beforeTime..afterTime)
assertEquals(
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/BenchmarkState.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/BenchmarkState.kt
index b6ab1fc..6df6bcd 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/BenchmarkState.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/BenchmarkState.kt
@@ -250,7 +250,7 @@
if (phaseIndex >= 0) {
currentPhase.profiler?.stop()
- UserspaceTracing.endSection()
+ InMemoryTracing.endSection()
thermalThrottleSleepSeconds += currentPhase.thermalThrottleSleepSeconds
if (currentPhase.loopMode.warmupManager == null && currentPhase.profiler == null) {
// Always save metrics, except during warmup / profiling
@@ -282,7 +282,7 @@
iterationsPerRepeat = iterationsPerRepeat.coerceAtLeast(currentLoopsPerMeasurement)
- UserspaceTracing.beginSection(currentPhase.label)
+ InMemoryTracing.beginSection(currentPhase.label)
val phaseProfilerResult = currentPhase.profiler?.start(traceUniqueName)
if (phaseProfilerResult != null) {
require(profilerResult == null) {
@@ -385,7 +385,7 @@
if (!value) {
ThreadPriority.resetBumpedThread()
if (phaseIndex >= 0 && phaseIndex <= phases.size) {
- UserspaceTracing.endSection() // current phase cancelled, complete trace event
+ InMemoryTracing.endSection() // current phase cancelled, complete trace event
}
throw IllegalStateException(lazyMessage())
}
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/UserspaceTracing.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/InMemoryTracing.kt
similarity index 67%
rename from benchmark/benchmark-common/src/main/java/androidx/benchmark/UserspaceTracing.kt
rename to benchmark/benchmark-common/src/main/java/androidx/benchmark/InMemoryTracing.kt
index 5972a77..4b171eb 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/UserspaceTracing.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/InMemoryTracing.kt
@@ -16,25 +16,32 @@
package androidx.benchmark
+import android.os.Process
import androidx.annotation.RestrictTo
+import androidx.benchmark.InMemoryTracing.commitToTrace
+import perfetto.protos.ThreadDescriptor
import perfetto.protos.Trace
import perfetto.protos.TracePacket
import perfetto.protos.TrackDescriptor
import perfetto.protos.TrackEvent
/**
- * Userspace-buffer-based tracing api, that provides implementation for [userspaceTrace].
+ * Tracing api that writes events directly into memory.
*
- * This records while atrace isn't capturing by storing trace events manually in a list of
- * in-userspace-memory perfetto protos.
+ * This has a few advantages over typical atrace:
+ * - can record while atrace isn't captured either due to platform limitations (old platforms may
+ * only allow one process to be traced at a time), or when debugging benchmark performance, it can
+ * capture when atrace isn't active (e.g. tracing the start/stop of perfetto)
+ * - can create events asynchronously, deferring record cost (e.g. micro creating events after all
+ * measurements, using existing timestamps)
+ * - can customize presentation of events in trace
*
* After trace processing, the extra events (before _and_ after the measureBlock section of a
* benchmark) can be added to the trace by calling [commitToTrace], and appending that to the
* trace on-disk.
- *
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-object UserspaceTracing {
+object InMemoryTracing {
/**
* All events emitted by the benchmark annotation should have the same value.
* the value needs to not conflict with any sequence id emitted in the trace.
@@ -58,39 +65,44 @@
private const val CLOCK_ID = 3
/**
- * Name of track in for userspace tracing events
- */
- private const val TRACK_DESCRIPTOR_NAME = "Macrobenchmark"
-
- /**
* Tag to enable post-filtering of events in the trace.
*/
private val TRACK_EVENT_CATEGORIES = listOf("benchmark")
- private fun createInitialTracePacket() = TracePacket(
- timestamp = System.nanoTime(),
- timestamp_clock_id = CLOCK_ID,
- incremental_state_cleared = true,
- track_descriptor = TrackDescriptor(
- uuid = UUID,
- name = TRACK_DESCRIPTOR_NAME
- )
- )
-
/**
* For perf/simplicity, this isn't protected by a lock - it should only every be
* accessed by the test thread, and dumped/reset between tests.
*/
- val events = mutableListOf(createInitialTracePacket())
+ val events = mutableListOf<TracePacket>()
+
+ fun clearEvents() {
+ events.clear()
+ }
/**
* Capture trace state, and return as a Trace(), which can be appended to a trace file.
*/
- fun commitToTrace(): Trace {
+ fun commitToTrace(
+ label: String
+ ): Trace {
val capturedEvents = events.toList()
- events.clear()
- events.add(createInitialTracePacket())
- return Trace(capturedEvents)
+ clearEvents()
+ return Trace(
+ listOf(
+ TracePacket(
+ timestamp_clock_id = CLOCK_ID,
+ incremental_state_cleared = true,
+ track_descriptor = TrackDescriptor(
+ uuid = UUID,
+ name = label,
+ thread = ThreadDescriptor(pid = Process.myPid(), tid = Process.myTid()),
+ // currently separate for clarity, to allow InMemoryTrace events to have a visible
+ // track name, but not override the thread name
+ disallow_merging_with_system_tracks = true
+ )
+ )
+ ) + capturedEvents
+ )
}
fun beginSection(label: String, nanoTime: Long = System.nanoTime()) {
@@ -125,11 +137,11 @@
}
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-inline fun <T> userspaceTrace(label: String, block: () -> T): T {
- UserspaceTracing.beginSection(label)
+inline fun <T> inMemoryTrace(label: String, block: () -> T): T {
+ InMemoryTracing.beginSection(label)
return try {
block()
} finally {
- UserspaceTracing.endSection()
+ InMemoryTracing.endSection()
}
}
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/MetricsContainer.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/MetricsContainer.kt
index 50e30ad..d0ae847 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/MetricsContainer.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/MetricsContainer.kt
@@ -54,7 +54,7 @@
internal val data: List<LongArray> = List(repeatCount) { LongArray(names.size) }
/**
- * Array of start / stop time, per measurement, to be passed to [UserspaceTracing].
+ * Array of start / stop time, per measurement, to be passed to [InMemoryTracing].
*
* These values are used both in metric calculation and trace data, so tracing is extremely low
* overhead - just the cost of storing the timing data in an additional place in memory.
@@ -135,8 +135,8 @@
*/
fun captureFinished(maxIterations: Int): List<MetricResult> {
for (i in 0..repeatTiming.lastIndex step 2) {
- UserspaceTracing.beginSection("measurement ${i / 2}", nanoTime = repeatTiming[i])
- UserspaceTracing.endSection(nanoTime = repeatTiming[i + 1])
+ InMemoryTracing.beginSection("measurement ${i / 2}", nanoTime = repeatTiming[i])
+ InMemoryTracing.endSection(nanoTime = repeatTiming[i + 1])
}
return names.mapIndexed { index, name ->
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/MicrobenchmarkPhase.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/MicrobenchmarkPhase.kt
index e8cba451..a21e9c8 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/MicrobenchmarkPhase.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/MicrobenchmarkPhase.kt
@@ -53,7 +53,7 @@
"THERMAL THROTTLE DETECTED, SLEEPING FOR $THROTTLE_BACKOFF_S SECONDS"
)
val startTimeNs = System.nanoTime()
- userspaceTrace("Sleep due to Thermal Throttle") {
+ inMemoryTrace("Sleep due to Thermal Throttle") {
Thread.sleep(TimeUnit.SECONDS.toMillis(THROTTLE_BACKOFF_S))
}
val sleepTimeNs = System.nanoTime() - startTimeNs
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/RunListenerDelegate.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/RunListenerDelegate.kt
new file mode 100644
index 0000000..b9332af
--- /dev/null
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/RunListenerDelegate.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.benchmark
+
+import android.util.Log
+import androidx.annotation.RestrictTo
+
+/**
+ * Actions that need to be performed once per test suite are defined in this [RunListenerDelegate].
+ *
+ * This way, we minimize the costs of these suite wide actions, and benchmarks continue running
+ * fast (with minimal additional overheads).
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+class RunListenerDelegate(private val sideEffects: List<SideEffect>) {
+ /**
+ * Called before any tests have been run.
+ */
+ fun onTestRunStarted() {
+ sideEffects.forEach { sideEffect ->
+ Log.d(
+ BenchmarkState.TAG,
+ "Setting up side effect ${sideEffect.name()}"
+ )
+ sideEffect.setup()
+ }
+ }
+
+ /**
+ * Called after all tests have been completed.
+ */
+ fun onTestRunFinished() {
+ sideEffects.forEach { sideEffect ->
+ Log.d(
+ BenchmarkState.TAG,
+ "Tearing down side effect ${sideEffect.name()}"
+ )
+ sideEffect.tearDown()
+ }
+ }
+}
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/Shell.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/Shell.kt
index faabcf9..6811685 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/Shell.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/Shell.kt
@@ -588,7 +588,7 @@
if (runningProcesses.isEmpty()) {
return
}
- userspaceTrace("wait for $runningProcesses to die") {
+ inMemoryTrace("wait for $runningProcesses to die") {
SystemClock.sleep(waitPollPeriodMs)
}
Log.d(BenchmarkState.TAG, "Waiting $waitPollPeriodMs ms for $runningProcesses to die")
@@ -611,6 +611,34 @@
}
@RequiresApi(21)
+ fun disablePackages(appPackages: List<String>) {
+ val command = appPackages.joinToString(separator = "\n") { appPackage ->
+ "pm disable-user $appPackage"
+ }
+ executeScriptCaptureStdoutStderr(command)
+ }
+
+ @RequiresApi(21)
+ fun enablePackages(appPackages: List<String>) {
+ val command = appPackages.joinToString(separator = "\n") { appPackage ->
+ "pm enable $appPackage"
+ }
+ executeScriptCaptureStdoutStderr(command)
+ }
+
+ @RequiresApi(24)
+ fun disableBackgroundDexOpt() {
+ // Cancels the active job if any
+ ShellImpl.executeCommandUnsafe("cmd package bg-dexopt-job --cancel")
+ ShellImpl.executeCommandUnsafe("cmd package bg-dexopt-job --disable")
+ }
+
+ @RequiresApi(24)
+ fun enableBackgroundDexOpt() {
+ ShellImpl.executeCommandUnsafe("cmd package bg-dexopt-job --enable")
+ }
+
+ @RequiresApi(21)
fun isSELinuxEnforced(): Boolean {
return when (val value = executeScriptCaptureStdout("getenforce").trim()) {
"Permissive" -> false
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/SideEffect.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/SideEffect.kt
new file mode 100644
index 0000000..512851d
--- /dev/null
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/SideEffect.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.benchmark
+
+import androidx.annotation.RestrictTo
+
+/**
+* Represents actions that effect changes to the state of the app / device outside of the scope
+* of the benchmark. Typically used to help reduce the amount of interference during a benchmark.
+*
+* [SideEffect]s must define a [setup], that is executed when the benchmark starts. The [tearDown]
+* method is called during the end of the benchmark to reverse actions so subsequent invocations of
+* the benchmark are hermetic.
+*/
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+interface SideEffect {
+
+ /**
+ * Returns the canonical name of the [SideEffect].
+ */
+ fun name(): String
+
+ /**
+ * This method is executed when the benchmark starts.
+ */
+ fun setup()
+
+ /**
+ * This method is executed when the benchmark is complete. A [SideEffect] should undo
+ * the changes to the state of the device app, to ensure hermetic benchmarks.
+ */
+ fun tearDown()
+}
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/SideEffects.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/SideEffects.kt
new file mode 100644
index 0000000..c520311
--- /dev/null
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/SideEffects.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.benchmark
+
+import android.os.Build
+import android.util.Log
+import androidx.annotation.RestrictTo
+
+/**
+ * Disables the [packages] during the course of a benchmark thereby reducing the amount of noise.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+class DisablePackages(
+ private val packages: List<String> = DEFAULT_PACKAGES_TO_DISABLE
+) : SideEffect {
+ override fun name(): String {
+ return "DisablePackages"
+ }
+
+ override fun setup() {
+ if (Build.VERSION.SDK_INT >= 21) {
+ Log.d(BenchmarkState.TAG, "Disabling packages $packages")
+ Shell.disablePackages(packages)
+ }
+ }
+
+ override fun tearDown() {
+ if (Build.VERSION.SDK_INT >= 21) {
+ Log.d(BenchmarkState.TAG, "Re-enabling packages $packages")
+ Shell.enablePackages(packages)
+ }
+ }
+
+ companion object {
+ // A list of packages to disable + "com.google.android.gms"
+ // https://source.corp.google.com/piper///depot/google3/java/com/google/android/libraries/swpower/fixture/DisableModule.java
+ internal val DEFAULT_PACKAGES_TO_DISABLE = listOf(
+ "com.android.chrome",
+ "com.android.phone",
+ "com.android.ramdump",
+ "com.android.vending",
+ "com.google.android.apps.docs",
+ "com.google.android.apps.gcs",
+ "com.google.android.apps.internal.betterbug",
+ "com.google.android.apps.maps",
+ "com.google.android.apps.messaging",
+ "com.google.android.apps.nbu.files",
+ "com.google.android.apps.photos",
+ "com.google.android.apps.scone",
+ "com.google.android.apps.tips",
+ "com.google.android.apps.turbo",
+ "com.google.android.apps.tycho",
+ "com.google.android.apps.work.clouddpc",
+ "com.google.android.apps.youtube.music",
+ "com.google.android.as",
+ "com.google.android.calculator",
+ "com.google.android.calendar",
+ "com.google.android.configupdater",
+ "com.google.android.contacts",
+ "com.google.android.deskclock",
+ "com.google.android.dialer",
+ "com.google.android.googlequicksearchbox",
+ "com.google.android.gm",
+ "com.google.android.gms",
+ "com.google.android.GoogleCamera",
+ "com.google.android.ims",
+ "com.google.android.inputmethod.latin",
+ "com.google.android.marvin.talkback",
+ "com.google.android.partnersetup",
+ "com.google.android.settings.intelligence",
+ "com.google.android.tts",
+ "com.google.android.videos",
+ "com.google.android.youtube",
+ "com.google.android.videos",
+ "com.google.android.volta"
+ )
+ }
+}
+
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+class DisableDexOpt : SideEffect {
+ override fun name(): String {
+ return "DisableDexOpt"
+ }
+
+ override fun setup() {
+ // PGO was enabled on Android N
+ if (Build.VERSION.SDK_INT >= 24) {
+ Shell.disableBackgroundDexOpt()
+ }
+ }
+
+ override fun tearDown() {
+ if (Build.VERSION.SDK_INT >= 24) {
+ Shell.enableBackgroundDexOpt()
+ }
+ }
+}
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCapture.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCapture.kt
index 8cd40a0..2f5a387 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCapture.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCapture.kt
@@ -22,9 +22,9 @@
import androidx.annotation.RestrictTo
import androidx.benchmark.Outputs
import androidx.benchmark.Shell
+import androidx.benchmark.inMemoryTrace
import androidx.benchmark.perfetto.PerfettoCapture.PerfettoSdkConfig.InitialProcessState
import androidx.benchmark.perfetto.PerfettoHelper.Companion.isAbiSupported
-import androidx.benchmark.userspaceTrace
import androidx.test.platform.app.InstrumentationRegistry
import androidx.tracing.perfetto.handshake.PerfettoSdkHandshake
import androidx.tracing.perfetto.handshake.protocol.ResponseResultCodes.RESULT_CODE_ALREADY_ENABLED
@@ -57,18 +57,18 @@
/**
* Start collecting perfetto trace.
*/
- fun start(config: PerfettoConfig) = userspaceTrace("start perfetto") {
+ fun start(config: PerfettoConfig) = inMemoryTrace("start perfetto") {
// Write config proto to dir that shell can read
// We use `.pb` even with textproto so we'll only ever have one file
val configProtoFile = File(Outputs.dirUsableByAppAndShell, "trace_config.pb")
try {
- userspaceTrace("write config") {
+ inMemoryTrace("write config") {
config.writeTo(configProtoFile)
if (Outputs.forceFilesForShellAccessible) {
configProtoFile.setReadable(true, /* ownerOnly = */ false)
}
}
- userspaceTrace("start perfetto process") {
+ inMemoryTrace("start perfetto process") {
helper.startCollecting(configProtoFile.absolutePath, config.isTextProto)
}
} finally {
@@ -82,7 +82,7 @@
* @param destinationPath Absolute path to write perfetto trace to. Must be shell-writable,
* such as result of `context.getExternalFilesDir(null)` or other similar `external` paths.
*/
- public fun stop(destinationPath: String) = userspaceTrace("stop perfetto") {
+ public fun stop(destinationPath: String) = inMemoryTrace("stop perfetto") {
helper.stopCollecting(destinationPath)
}
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCaptureWrapper.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCaptureWrapper.kt
index 278416e..c823e02 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCaptureWrapper.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoCaptureWrapper.kt
@@ -20,12 +20,14 @@
import android.util.Log
import androidx.annotation.RequiresApi
import androidx.annotation.RestrictTo
+import androidx.benchmark.InMemoryTracing
import androidx.benchmark.Outputs
import androidx.benchmark.Outputs.dateToFileName
import androidx.benchmark.PropOverride
import androidx.benchmark.Shell
import androidx.benchmark.perfetto.PerfettoHelper.Companion.LOG_TAG
import androidx.benchmark.perfetto.PerfettoHelper.Companion.isAbiSupported
+import java.io.File
/**
* Wrapper for [PerfettoCapture] which does nothing below API 23.
@@ -93,6 +95,7 @@
perfettoSdkConfig: PerfettoCapture.PerfettoSdkConfig?,
traceCallback: ((String) -> Unit)? = null,
enableTracing: Boolean = true,
+ inMemoryTracingLabel: String? = null,
block: () -> Unit
): String? {
// skip if Perfetto not supported, or if caller opts out
@@ -122,11 +125,20 @@
try {
propOverride?.forceValue()
start(config, perfettoSdkConfig)
+
+ // To avoid b/174007010, userspace tracing is cleared and saved *during* trace, so
+ // that events won't lie outside the bounds of the trace content.
+ InMemoryTracing.clearEvents()
try {
block()
} finally {
// finally here to ensure trace is fully recorded if block throws
path = stop(fileLabel)
+
+ if (inMemoryTracingLabel != null) {
+ val inMemoryTrace = InMemoryTracing.commitToTrace(inMemoryTracingLabel)
+ File(path).appendBytes(inMemoryTrace.encode())
+ }
traceCallback?.invoke(path)
}
return path
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoHelper.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoHelper.kt
index 2491c74..b4fdbc1 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoHelper.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/perfetto/PerfettoHelper.kt
@@ -23,8 +23,8 @@
import androidx.annotation.RestrictTo
import androidx.benchmark.DeviceInfo.deviceSummaryString
import androidx.benchmark.Shell
+import androidx.benchmark.inMemoryTrace
import androidx.benchmark.perfetto.PerfettoHelper.Companion.MIN_SDK_VERSION
-import androidx.benchmark.userspaceTrace
import androidx.test.platform.app.InstrumentationRegistry
import androidx.tracing.trace
import java.io.File
@@ -165,7 +165,7 @@
* This is a good indicator that tracing is actually enabled (including the app atrace tag), and
* that content will be captured in the trace buffer
*/
- private fun checkTracingOn(): Unit = userspaceTrace("poll tracing_on") {
+ private fun checkTracingOn(): Unit = inMemoryTrace("poll tracing_on") {
val path: String = when {
Shell.pathExists(TRACING_ON_PATH) -> {
TRACING_ON_PATH
@@ -187,7 +187,7 @@
repeat(pollTracingOnMaxCount) {
when (val output = Shell.executeScriptCaptureStdout("cat $path").trim()) {
"0" -> {
- userspaceTrace("wait for trace to start (tracing_on == 1)") {
+ inMemoryTrace("wait for trace to start (tracing_on == 1)") {
SystemClock.sleep(pollTracingOnMs)
}
}
@@ -232,12 +232,12 @@
// Stop the perfetto and copy the output file.
Log.i(LOG_TAG, "Stopping perfetto.")
- userspaceTrace("stop perfetto process") {
+ inMemoryTrace("stop perfetto process") {
stopPerfetto()
}
Log.i(LOG_TAG, "Writing to $destinationFile.")
- userspaceTrace("copy trace to output dir") {
+ inMemoryTrace("copy trace to output dir") {
copyFileOutput(destinationFile)
}
}
@@ -410,7 +410,7 @@
}
fun createExecutable(tool: String): String {
- userspaceTrace("create executable: $tool") {
+ inMemoryTrace("create executable: $tool") {
if (!isAbiSupported()) {
throw IllegalStateException(
"Unsupported ABI (${Build.SUPPORTED_ABIS.joinToString()})"
diff --git a/benchmark/benchmark-junit4/src/main/java/androidx/benchmark/junit4/BenchmarkRule.kt b/benchmark/benchmark-junit4/src/main/java/androidx/benchmark/junit4/BenchmarkRule.kt
index 5fcf6977..c39aaf8 100644
--- a/benchmark/benchmark-junit4/src/main/java/androidx/benchmark/junit4/BenchmarkRule.kt
+++ b/benchmark/benchmark-junit4/src/main/java/androidx/benchmark/junit4/BenchmarkRule.kt
@@ -25,7 +25,6 @@
import androidx.benchmark.DeviceInfo
import androidx.benchmark.ExperimentalBenchmarkConfigApi
import androidx.benchmark.MicrobenchmarkConfig
-import androidx.benchmark.UserspaceTracing
import androidx.benchmark.perfetto.PerfettoCapture
import androidx.benchmark.perfetto.PerfettoCaptureWrapper
import androidx.benchmark.perfetto.PerfettoConfig
@@ -217,8 +216,6 @@
val uniqueName = description.testClass.simpleName + "_" + invokeMethodName
internalState.traceUniqueName = uniqueName
- var userspaceTrace: perfetto.protos.Trace? = null
-
val tracePath = PerfettoCaptureWrapper().record(
fileLabel = uniqueName,
config = PerfettoConfig.Benchmark(
@@ -245,19 +242,13 @@
// Optimize throughput in dryRunMode, since trace isn't useful, and extremely
// expensive on some emulators. Could alternately use UserspaceTracing if desired
// Additionally, skip on misconfigured devices to still enable benchmarking.
- enableTracing = !Arguments.dryRunMode && !DeviceInfo.misconfiguredForTracing
+ enableTracing = !Arguments.dryRunMode && !DeviceInfo.misconfiguredForTracing,
+ inMemoryTracingLabel = "Microbenchmark"
) {
- UserspaceTracing.commitToTrace() // clear buffer
-
trace(description.displayName) { base.evaluate() }
-
- // To avoid b/174007010, userspace tracing is cleared and saved *during* trace, so
- // that events won't lie outside the bounds of the trace content.
- userspaceTrace = UserspaceTracing.commitToTrace()
}?.apply {
// trace completed, and copied into shell writeable dir
val file = File(this)
- file.appendBytes(userspaceTrace!!.encode())
file.appendUiState(
UiState(
timelineStart = null,
diff --git a/benchmark/benchmark-junit4/src/main/java/androidx/benchmark/junit4/SideEffectRunListener.kt b/benchmark/benchmark-junit4/src/main/java/androidx/benchmark/junit4/SideEffectRunListener.kt
new file mode 100644
index 0000000..fa14e63
--- /dev/null
+++ b/benchmark/benchmark-junit4/src/main/java/androidx/benchmark/junit4/SideEffectRunListener.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.benchmark.junit4
+
+import androidx.annotation.RestrictTo
+import androidx.benchmark.DisableDexOpt
+import androidx.benchmark.DisablePackages
+import androidx.benchmark.RunListenerDelegate
+import org.junit.runner.Description
+import org.junit.runner.Result
+import org.junit.runner.notification.RunListener
+
+/**
+ * Enables the use of side-effects that reduce the noise during a benchmark run.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+class SideEffectRunListener : RunListener() {
+ private val delegate: RunListenerDelegate = RunListenerDelegate(
+ sideEffects = listOf(
+ DisablePackages(),
+ DisableDexOpt(),
+ )
+ )
+
+ override fun testRunStarted(description: Description) {
+ super.testRunStarted(description)
+ delegate.onTestRunStarted()
+ }
+
+ override fun testRunFinished(result: Result) {
+ super.testRunFinished(result)
+ delegate.onTestRunFinished()
+ }
+}
diff --git a/benchmark/benchmark-macro/api/1.2.0-beta02.txt b/benchmark/benchmark-macro/api/1.2.0-beta02.txt
index 6b770f4..54c8a8c 100644
--- a/benchmark/benchmark-macro/api/1.2.0-beta02.txt
+++ b/benchmark/benchmark-macro/api/1.2.0-beta02.txt
@@ -208,7 +208,7 @@
}
@SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public final class TraceSectionMetric extends androidx.benchmark.macro.Metric {
- ctor public TraceSectionMetric(String sectionName, optional androidx.benchmark.macro.TraceSectionMetric.Mode mode);
+ ctor public TraceSectionMetric(String sectionName, optional androidx.benchmark.macro.TraceSectionMetric.Mode mode, optional boolean targetPackageOnly);
}
public enum TraceSectionMetric.Mode {
diff --git a/benchmark/benchmark-macro/api/current.txt b/benchmark/benchmark-macro/api/current.txt
index 6b770f4..54c8a8c 100644
--- a/benchmark/benchmark-macro/api/current.txt
+++ b/benchmark/benchmark-macro/api/current.txt
@@ -208,7 +208,7 @@
}
@SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public final class TraceSectionMetric extends androidx.benchmark.macro.Metric {
- ctor public TraceSectionMetric(String sectionName, optional androidx.benchmark.macro.TraceSectionMetric.Mode mode);
+ ctor public TraceSectionMetric(String sectionName, optional androidx.benchmark.macro.TraceSectionMetric.Mode mode, optional boolean targetPackageOnly);
}
public enum TraceSectionMetric.Mode {
diff --git a/benchmark/benchmark-macro/api/restricted_1.2.0-beta02.txt b/benchmark/benchmark-macro/api/restricted_1.2.0-beta02.txt
index 32628f3..9fb21fd 100644
--- a/benchmark/benchmark-macro/api/restricted_1.2.0-beta02.txt
+++ b/benchmark/benchmark-macro/api/restricted_1.2.0-beta02.txt
@@ -230,7 +230,7 @@
}
@SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public final class TraceSectionMetric extends androidx.benchmark.macro.Metric {
- ctor public TraceSectionMetric(String sectionName, optional androidx.benchmark.macro.TraceSectionMetric.Mode mode);
+ ctor public TraceSectionMetric(String sectionName, optional androidx.benchmark.macro.TraceSectionMetric.Mode mode, optional boolean targetPackageOnly);
}
public enum TraceSectionMetric.Mode {
diff --git a/benchmark/benchmark-macro/api/restricted_current.txt b/benchmark/benchmark-macro/api/restricted_current.txt
index 32628f3..9fb21fd 100644
--- a/benchmark/benchmark-macro/api/restricted_current.txt
+++ b/benchmark/benchmark-macro/api/restricted_current.txt
@@ -230,7 +230,7 @@
}
@SuppressCompatibility @androidx.benchmark.macro.ExperimentalMetricApi public final class TraceSectionMetric extends androidx.benchmark.macro.Metric {
- ctor public TraceSectionMetric(String sectionName, optional androidx.benchmark.macro.TraceSectionMetric.Mode mode);
+ ctor public TraceSectionMetric(String sectionName, optional androidx.benchmark.macro.TraceSectionMetric.Mode mode, optional boolean targetPackageOnly);
}
public enum TraceSectionMetric.Mode {
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/MacrobenchmarkTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/MacrobenchmarkTest.kt
index 03de19f..09a6c7d 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/MacrobenchmarkTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/MacrobenchmarkTest.kt
@@ -96,7 +96,8 @@
className = "MacrobenchmarkTest",
testName = "validateCallbackBehavior",
packageName = Packages.TARGET,
- metrics = listOf(TraceSectionMetric(TRACE_LABEL)),
+ // disable targetPackageOnly filter, since this process emits the event
+ metrics = listOf(TraceSectionMetric(TRACE_LABEL, targetPackageOnly = false)),
compilationMode = CompilationMode.DEFAULT,
iterations = 2,
startupMode = startupMode,
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/PerfettoTraceRuleTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/PerfettoTraceRuleTest.kt
index b8b2819..d5229ca 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/PerfettoTraceRuleTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/PerfettoTraceRuleTest.kt
@@ -58,7 +58,7 @@
val sliceNameInstances = PerfettoTraceProcessor.runSingleSessionServer(
trace!!.path
) {
- querySlices(UNIQUE_SLICE_NAME)
+ querySlices(UNIQUE_SLICE_NAME, packageName = null)
.map { slice -> slice.name }
}
assertEquals(listOf(UNIQUE_SLICE_NAME), sliceNameInstances)
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/TraceSectionMetricTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/TraceSectionMetricTest.kt
index 4eedd27..d69c472 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/TraceSectionMetricTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/TraceSectionMetricTest.kt
@@ -38,7 +38,7 @@
@Test
fun activityThreadMain() = verifyFirstSum(
tracePath = api24ColdStart,
- packageName = Packages.TEST,
+ packageName = Packages.TARGET,
sectionName = "ActivityThreadMain",
expectedFirstMs = 12.639
)
@@ -46,7 +46,7 @@
@Test
fun activityStart() = verifyFirstSum(
tracePath = api24ColdStart,
- packageName = Packages.TEST,
+ packageName = Packages.TARGET,
sectionName = "activityStart",
expectedFirstMs = 81.979
)
@@ -54,17 +54,18 @@
@Test
fun startActivityAndWait() = verifyFirstSum(
tracePath = api24ColdStart,
- packageName = Packages.TEST,
+ packageName = "androidx.benchmark.integration.macrobenchmark.test",
sectionName = "startActivityAndWait",
- expectedFirstMs = 1_110.689
+ expectedFirstMs = 1_110.689,
)
@Test
fun launching() = verifyFirstSum(
tracePath = api24ColdStart,
- packageName = Packages.TEST,
+ packageName = Packages.TARGET,
sectionName = "launching: androidx.benchmark.integration.macrobenchmark.target",
- expectedFirstMs = 269.947
+ expectedFirstMs = 269.947,
+ targetPackageOnly = false // slice from system_server
)
@Test
@@ -76,23 +77,28 @@
)
@Test
- fun multiSection() = verifyFirstSum(
+ fun multiSection_targetOnly() = verifyFirstSum(
+ tracePath = api24ColdStart,
+ packageName = Packages.TARGET,
+ sectionName = "inflate",
+ expectedFirstMs = 4.949, // first inflation
+ expectedSumMs = 19.779, // total inflation
+ expectedSumCount = 3,
+ targetPackageOnly = true,
+ )
+
+ @Test
+ fun multiSection_unfiltered() = verifyFirstSum(
tracePath = api24ColdStart,
packageName = Packages.TARGET,
sectionName = "inflate",
expectedFirstMs = 13.318, // first inflation
expectedSumMs = 43.128, // total inflation
expectedSumCount = 8,
+ targetPackageOnly = false,
)
companion object {
- private val captureInfo = Metric.CaptureInfo(
- targetPackageName = Packages.TEST,
- testPackageName = Packages.TEST,
- startupMode = StartupMode.COLD,
- apiLevel = 24
- )
-
private fun verifyMetric(
tracePath: String,
packageName: String,
@@ -100,15 +106,23 @@
mode: TraceSectionMetric.Mode,
expectedMs: Double,
expectedCount: Int,
+ targetPackageOnly: Boolean
) {
assumeTrue(PerfettoHelper.isAbiSupported())
- val metric = TraceSectionMetric(sectionName, mode)
+ val metric = TraceSectionMetric(sectionName, mode, targetPackageOnly)
metric.configure(packageName = packageName)
val result = PerfettoTraceProcessor.runSingleSessionServer(tracePath) {
metric.getResult(
- captureInfo = captureInfo,
+ // note that most args are incorrect here, but currently
+ // only targetPackageName matters in this context
+ captureInfo = Metric.CaptureInfo(
+ targetPackageName = packageName,
+ testPackageName = Packages.TEST,
+ startupMode = StartupMode.COLD,
+ apiLevel = 24
+ ),
traceSession = this
)
}
@@ -134,7 +148,8 @@
sectionName: String,
expectedFirstMs: Double,
expectedSumMs: Double = expectedFirstMs, // default implies only one matching section
- expectedSumCount: Int = 1
+ expectedSumCount: Int = 1,
+ targetPackageOnly: Boolean = true,
) {
verifyMetric(
tracePath = tracePath,
@@ -142,7 +157,8 @@
sectionName = sectionName,
mode = TraceSectionMetric.Mode.First,
expectedMs = expectedFirstMs,
- expectedCount = 1
+ expectedCount = 1,
+ targetPackageOnly = targetPackageOnly,
)
verifyMetric(
tracePath = tracePath,
@@ -151,6 +167,7 @@
mode = TraceSectionMetric.Mode.Sum,
expectedMs = expectedSumMs,
expectedCount = expectedSumCount,
+ targetPackageOnly = targetPackageOnly,
)
}
}
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/BatteryDischargeQueryTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/BatteryDischargeQueryTest.kt
index db3bea2..31de56f 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/BatteryDischargeQueryTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/BatteryDischargeQueryTest.kt
@@ -42,7 +42,8 @@
val traceFile = createTempFileFromAsset("api31_battery_discharge", ".perfetto-trace")
val actualMetrics = PerfettoTraceProcessor.runSingleSessionServer(traceFile.absolutePath) {
- val slice = querySlices(PowerMetric.MEASURE_BLOCK_SECTION_NAME).first()
+ val slice =
+ querySlices(PowerMetric.MEASURE_BLOCK_SECTION_NAME, packageName = null).first()
BatteryDischargeQuery.getBatteryDischargeMetrics(this, slice)
}
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoCaptureSweepTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoCaptureSweepTest.kt
index 117726f..41d9868 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoCaptureSweepTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoCaptureSweepTest.kt
@@ -129,7 +129,7 @@
perfettoCapture.stop(traceFilePath)
val matchingSlices = PerfettoTraceProcessor.runSingleSessionServer(traceFilePath) {
- querySlices("PerfettoCaptureTest_%")
+ querySlices("PerfettoCaptureTest_%", packageName = null)
}
// Note: this test avoids validating platform-triggered trace sections, to avoid flakes
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoSdkHandshakeTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoSdkHandshakeTest.kt
index f8cbebb..0bcb3b4 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoSdkHandshakeTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoSdkHandshakeTest.kt
@@ -43,7 +43,6 @@
import org.junit.After
import org.junit.Assume.assumeTrue
import org.junit.Before
-import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
@@ -216,14 +215,10 @@
}
}
- // TODO(283953019): enable alongside StartupTracingInitializer (pending performance testing)
- @Ignore
@Test
fun test_handshake_framework_cold_start_persistent() =
test_handshake_framework_cold_start(persistent = true)
- // TODO(283953019): enable alongside StartupTracingInitializer (pending performance testing)
- @Ignore
@Test
fun test_handshake_framework_cold_start_non_persistent() =
test_handshake_framework_cold_start(persistent = false)
@@ -277,8 +272,6 @@
}
}
- // TODO(283953019): enable alongside StartupTracingInitializer (pending performance testing)
- @Ignore
/**
* Tests [androidx.benchmark.perfetto.PerfettoCapture.enableAndroidxTracingPerfetto] as
* opposed to [androidx.tracing.perfetto.handshake.PerfettoSdkHandshake.enableTracingColdStart]
@@ -326,14 +319,10 @@
}
}
- // TODO(283953019): enable alongside StartupTracingInitializer (pending performance testing)
- @Ignore
@Test
fun test_handshake_framework_cold_start_disable_persistent() =
test_handshake_framework_cold_start_disable(persistent = true)
- // TODO(283953019): enable alongside StartupTracingInitializer (pending performance testing)
- @Ignore
@Test
fun test_handshake_framework_cold_start_disable_non_persistent() =
test_handshake_framework_cold_start_disable(persistent = true)
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoSdkTraceTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoSdkTraceTest.kt
index 07c1416..d171aaa 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoSdkTraceTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PerfettoSdkTraceTest.kt
@@ -58,7 +58,12 @@
if (enableUserspaceTracing) yield(StringSource.userspaceTraceStrings)
}.flatMap { it }.toList()
val actualSlices = PerfettoTraceProcessor.runSingleSessionServer(trace.path) {
- StringSource.allTraceStrings.flatMap { querySlices(it).map { s -> s.name } }
+ StringSource.allTraceStrings.flatMap {
+ querySlices(
+ it,
+ packageName = null
+ ).map { s -> s.name }
+ }
}
assertThat(actualSlices).containsExactlyElementsIn(expectedSlices)
}
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PowerQueryTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PowerQueryTest.kt
index c3e4847..4c5e491 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PowerQueryTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/perfetto/PowerQueryTest.kt
@@ -43,7 +43,7 @@
val actualMetrics = PerfettoTraceProcessor.runSingleSessionServer(traceFile.absolutePath) {
PowerQuery.getPowerMetrics(
this,
- querySlices(MEASURE_BLOCK_SECTION_NAME).first()
+ querySlices(MEASURE_BLOCK_SECTION_NAME, packageName = null).first()
)
}
@@ -182,7 +182,10 @@
val traceFile = createTempFileFromAsset("api31_odpm_rails_empty", ".perfetto-trace")
val actualMetrics = PerfettoTraceProcessor.runSingleSessionServer(traceFile.absolutePath) {
- PowerQuery.getPowerMetrics(this, querySlices(MEASURE_BLOCK_SECTION_NAME).first())
+ PowerQuery.getPowerMetrics(
+ this,
+ querySlices(MEASURE_BLOCK_SECTION_NAME, packageName = null).first()
+ )
}
assertEquals(emptyMap(), actualMetrics)
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/perfetto/PerfettoTraceProcessorTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/perfetto/PerfettoTraceProcessorTest.kt
index e772ff9..37769a6 100644
--- a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/perfetto/PerfettoTraceProcessorTest.kt
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/perfetto/PerfettoTraceProcessorTest.kt
@@ -21,7 +21,8 @@
import androidx.benchmark.macro.createTempFileFromAsset
import androidx.benchmark.perfetto.PerfettoHelper.Companion.isAbiSupported
import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
+import androidx.test.filters.LargeTest
+import androidx.test.filters.MediumTest
import androidx.test.platform.app.InstrumentationRegistry
import java.io.File
import java.net.ConnectException
@@ -37,7 +38,7 @@
import org.junit.Test
import org.junit.runner.RunWith
-@SmallTest
+@MediumTest
@RunWith(AndroidJUnit4::class)
class PerfettoTraceProcessorTest {
@Test
@@ -93,38 +94,66 @@
}
}
+ enum class QuerySlicesMode(val target: String?) {
+ ValidPackage("androidx.benchmark.integration.macrobenchmark.target"),
+ Unspecified(null),
+ InvalidPackage("not.a.real.package")
+ }
+
@Test
- fun querySlices() {
+ fun querySlices_validPackage() = validateQuerySlices(QuerySlicesMode.ValidPackage)
+
+ @Test
+ fun querySlices_invalidPackage() = validateQuerySlices(QuerySlicesMode.InvalidPackage)
+
+ @Test
+ fun querySlices_unspecified() = validateQuerySlices(QuerySlicesMode.Unspecified)
+
+ private fun validateQuerySlices(mode: QuerySlicesMode) {
// check known slice content is queryable
assumeTrue(isAbiSupported())
val traceFile = createTempFileFromAsset("api31_startup_cold", ".perfetto-trace")
PerfettoTraceProcessor.runSingleSessionServer(traceFile.absolutePath) {
assertEquals(
- expected = listOf(
- Slice(
- name = "activityStart",
- ts = 186975009436431,
- dur = 29580628
+ expected = when (mode) {
+ QuerySlicesMode.InvalidPackage -> emptyList()
+ else -> listOf(
+ Slice(
+ name = "activityStart",
+ ts = 186975009436431,
+ dur = 29580628
+ )
)
- ),
- actual = querySlices("activityStart")
+ },
+ actual = querySlices("activityStart", packageName = mode.target)
)
assertEquals(
- expected = listOf(
- Slice(
- name = "activityStart",
- ts = 186975009436431,
- dur = 29580628
- ),
- Slice(
- name = "activityResume",
- ts = 186975039764298,
- dur = 6570418
+ expected = when (mode) {
+ QuerySlicesMode.InvalidPackage -> emptyList()
+ else -> listOf(
+ Slice(
+ name = "activityStart",
+ ts = 186975009436431,
+ dur = 29580628
+ ),
+ Slice(
+ name = "activityResume",
+ ts = 186975039764298,
+ dur = 6570418
+ )
)
- ),
- actual = querySlices("activityStart", "activityResume")
+ },
+ actual = querySlices("activityStart", "activityResume", packageName = mode.target)
.sortedBy { it.ts }
)
+ assertEquals(
+ expected = when (mode) {
+ QuerySlicesMode.ValidPackage -> 7
+ QuerySlicesMode.Unspecified -> 127
+ QuerySlicesMode.InvalidPackage -> 0
+ },
+ actual = querySlices("Lock contention %", packageName = mode.target).size
+ )
}
}
@@ -253,6 +282,7 @@
assertTrue(!isRunning())
}
+ @LargeTest
@Test
fun parseLongTrace() {
val traceFile = File
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/BaselineProfiles.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/BaselineProfiles.kt
index cc75044..bb55014 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/BaselineProfiles.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/BaselineProfiles.kt
@@ -27,7 +27,7 @@
import androidx.benchmark.InstrumentationResults
import androidx.benchmark.Outputs
import androidx.benchmark.Shell
-import androidx.benchmark.userspaceTrace
+import androidx.benchmark.inMemoryTrace
import java.io.File
/**
@@ -59,7 +59,7 @@
val finalMaxIterations = if (Arguments.dryRunMode) 1 else maxIterations
while (iteration <= finalMaxIterations) {
- userspaceTrace("generate profile for $packageName ($iteration)") {
+ inMemoryTrace("generate profile for $packageName ($iteration)") {
val mode = CompilationMode.Partial(
baselineProfileMode = BaselineProfileMode.Disable,
warmupIterations = 1
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/CompilationMode.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/CompilationMode.kt
index 1b9da10..4aecd9b 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/CompilationMode.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/CompilationMode.kt
@@ -24,11 +24,11 @@
import androidx.benchmark.Arguments
import androidx.benchmark.DeviceInfo
import androidx.benchmark.Shell
+import androidx.benchmark.inMemoryTrace
import androidx.benchmark.macro.CompilationMode.Full
import androidx.benchmark.macro.CompilationMode.Ignore
import androidx.benchmark.macro.CompilationMode.None
import androidx.benchmark.macro.CompilationMode.Partial
-import androidx.benchmark.userspaceTrace
import androidx.profileinstaller.ProfileInstallReceiver
import org.junit.AssumptionViolatedException
@@ -123,7 +123,7 @@
* does work on older APIs without root
*/
private fun reinstallPackage(packageName: String) {
- userspaceTrace("reinstallPackage") {
+ inMemoryTrace("reinstallPackage") {
// Copy APKs to /data/local/temp
val apkPaths = Shell.pmPath(packageName)
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt
index c73d755..d1ad575 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Macrobenchmark.kt
@@ -30,9 +30,9 @@
import androidx.benchmark.Profiler
import androidx.benchmark.ResultWriter
import androidx.benchmark.Shell
-import androidx.benchmark.UserspaceTracing
import androidx.benchmark.checkAndGetSuppressionState
import androidx.benchmark.conditionalError
+import androidx.benchmark.inMemoryTrace
import androidx.benchmark.perfetto.PerfettoCapture.PerfettoSdkConfig
import androidx.benchmark.perfetto.PerfettoCapture.PerfettoSdkConfig.InitialProcessState
import androidx.benchmark.perfetto.PerfettoCaptureWrapper
@@ -41,7 +41,6 @@
import androidx.benchmark.perfetto.PerfettoTraceProcessor
import androidx.benchmark.perfetto.UiState
import androidx.benchmark.perfetto.appendUiState
-import androidx.benchmark.userspaceTrace
import androidx.test.platform.app.InstrumentationRegistry
import androidx.tracing.trace
import java.io.File
@@ -220,7 +219,7 @@
// Always kill the process at beginning of test
scope.killProcess()
- userspaceTrace("compile $packageName") {
+ inMemoryTrace("compile $packageName") {
compilationMode.resetAndCompile(packageName, killProcessBlock = scope::killProcess) {
setupBlock(scope)
measureBlock(scope)
@@ -243,12 +242,12 @@
val runIterations = if (Arguments.dryRunMode) 1 else iterations
List(runIterations) { iteration ->
// Wake the device to ensure it stays awake with large iteration count
- userspaceTrace("wake device") {
+ inMemoryTrace("wake device") {
scope.device.wakeUp()
}
scope.iteration = iteration
- userspaceTrace("setupBlock") {
+ inMemoryTrace("setupBlock") {
setupBlock(scope)
}
@@ -274,7 +273,8 @@
},
useStackSamplingConfig = true
),
- perfettoSdkConfig = perfettoSdkConfig
+ perfettoSdkConfig = perfettoSdkConfig,
+ inMemoryTracingLabel = "Macrobenchmark"
) {
try {
trace("start metrics") {
@@ -306,7 +306,7 @@
val measurementList = loadTrace(PerfettoTrace(tracePath)) {
// Extracts the metrics using the perfetto trace processor
- userspaceTrace("extract metrics") {
+ inMemoryTrace("extract metrics") {
metrics
// capture list of Measurements
.map {
@@ -330,10 +330,6 @@
highlightPackage = packageName
)
File(tracePath).apply {
- // Disabled currently, see b/194424816 and b/174007010
- // appendBytes(UserspaceTracing.commitToTrace().encode())
- UserspaceTracing.commitToTrace() // clear buffer
-
appendUiState(uiState)
}
Log.d(TAG, "Iteration $iteration captured $uiState")
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/MethodTracing.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/MethodTracing.kt
index 7a07561..f77efc40e 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/MethodTracing.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/MethodTracing.kt
@@ -5,7 +5,7 @@
import androidx.benchmark.Outputs
import androidx.benchmark.Shell
import androidx.benchmark.getFirstMountedMediaDir
-import androidx.benchmark.userspaceTrace
+import androidx.benchmark.inMemoryTrace
import androidx.test.platform.app.InstrumentationRegistry
import java.io.File
@@ -73,7 +73,7 @@
}
private fun broadcast(targetPackageName: String, extras: String) {
- userspaceTrace("methodTracingBroadcast") {
+ inMemoryTrace("methodTracingBroadcast") {
val action = "androidx.benchmark.experiments.ACTION_METHOD_TRACE"
val result =
Shell.amBroadcast("-a $action $extras $targetPackageName/$RECEIVER_NAME") ?: 0
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Metric.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Metric.kt
index e60b36a..4b3ba98 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Metric.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/Metric.kt
@@ -382,7 +382,8 @@
* Captures the time taken by named trace section - a named begin / end pair matching the provided
* [sectionName].
*
- * Select how matching sections are resolved into a duration metric with [mode].
+ * Select how matching sections are resolved into a duration metric with [mode], and configure if
+ * sections outside the target process are included with [targetPackageOnly].
*
* @see androidx.tracing.Trace.beginSection
* @see androidx.tracing.Trace.endSection
@@ -390,8 +391,22 @@
*/
@ExperimentalMetricApi
class TraceSectionMetric(
+ /**
+ * Section name or pattern to match.
+ *
+ * "%" can be used as a wildcard, as this is supported by the underlying
+ * [PerfettoTraceProcessor] query. For example `"JIT %"` will match a section named
+ * `"JIT compiling int com.package.MyClass.method(int)"` present in the trace.
+ */
private val sectionName: String,
- private val mode: Mode = Mode.First
+ /**
+ * How should the
+ */
+ private val mode: Mode = Mode.First,
+ /**
+ * Filter results to trace sections only from the target process, defaults to true.
+ */
+ private val targetPackageOnly: Boolean = true
) : Metric() {
enum class Mode {
/**
@@ -425,7 +440,10 @@
captureInfo: CaptureInfo,
traceSession: PerfettoTraceProcessor.Session
): List<Measurement> {
- val slices = traceSession.querySlices(sectionName)
+ val slices = traceSession.querySlices(
+ sectionName,
+ packageName = if (targetPackageOnly) captureInfo.targetPackageName else null
+ )
return when (mode) {
Mode.First -> {
@@ -583,7 +601,7 @@
traceSession: PerfettoTraceProcessor.Session
): List<Measurement> {
// collect metrics between trace point flags
- val slice = traceSession.querySlices(MEASURE_BLOCK_SECTION_NAME)
+ val slice = traceSession.querySlices(MEASURE_BLOCK_SECTION_NAME, packageName = null)
.firstOrNull()
?: return emptyList()
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/server/PerfettoHttpServer.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/server/PerfettoHttpServer.kt
index 2f74b4c..4fd7b50 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/server/PerfettoHttpServer.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/perfetto/server/PerfettoHttpServer.kt
@@ -24,8 +24,8 @@
import androidx.annotation.RequiresApi
import androidx.benchmark.Shell
import androidx.benchmark.ShellScript
+import androidx.benchmark.inMemoryTrace
import androidx.benchmark.perfetto.PerfettoTraceProcessor
-import androidx.benchmark.userspaceTrace
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
@@ -101,10 +101,10 @@
* @throws IllegalStateException if the server is not running by the end of the timeout.
*/
@SuppressLint("BanThreadSleep")
- fun startServer() = userspaceTrace("PerfettoHttpServer#startServer") {
+ fun startServer() = inMemoryTrace("PerfettoHttpServer#startServer") {
if (processId != null) {
Log.w(TAG, "Tried to start a trace shell processor that is already running.")
- return@userspaceTrace
+ return@inMemoryTrace
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N &&
@@ -168,10 +168,10 @@
/**
* Stops the server killing the associated process
*/
- fun stopServer() = userspaceTrace("PerfettoHttpServer#stopServer") {
+ fun stopServer() = inMemoryTrace("PerfettoHttpServer#stopServer") {
if (processId == null) {
Log.w(TAG, "Tried to stop trace shell processor http server without starting it.")
- return@userspaceTrace
+ return@inMemoryTrace
}
Shell.executeScriptSilent("kill -TERM $processId")
Log.i(TAG, "Perfetto trace processor shell server stopped (pid=$processId).")
@@ -180,10 +180,10 @@
/**
* Returns true whether the server is running, false otherwise.
*/
- fun isRunning(): Boolean = userspaceTrace("PerfettoHttpServer#isRunning") {
- return@userspaceTrace try {
+ fun isRunning(): Boolean = inMemoryTrace("PerfettoHttpServer#isRunning") {
+ return@inMemoryTrace try {
val statusResult = status()
- return@userspaceTrace statusResult.api_version != null && statusResult.api_version > 0
+ return@inMemoryTrace statusResult.api_version != null && statusResult.api_version > 0
} catch (e: ConnectException) {
false
}
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/perfetto/PerfettoTraceProcessor.kt b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/perfetto/PerfettoTraceProcessor.kt
index 9314bde..c5bc405 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/perfetto/PerfettoTraceProcessor.kt
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/perfetto/PerfettoTraceProcessor.kt
@@ -18,8 +18,8 @@
import androidx.annotation.RestrictTo
import androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP
+import androidx.benchmark.inMemoryTrace
import androidx.benchmark.macro.perfetto.server.PerfettoHttpServer
-import androidx.benchmark.userspaceTrace
import java.io.File
import java.io.FileInputStream
import java.io.InputStream
@@ -93,7 +93,7 @@
@JvmStatic
fun <T> runServer(
block: PerfettoTraceProcessor.() -> T
- ): T = userspaceTrace("PerfettoTraceProcessor#runServer") {
+ ): T = inMemoryTrace("PerfettoTraceProcessor#runServer") {
var perfettoTraceProcessor: PerfettoTraceProcessor? = null
try {
@@ -101,7 +101,7 @@
perfettoTraceProcessor = PerfettoTraceProcessor().startServer()
// Executes the query block
- return@userspaceTrace userspaceTrace("PerfettoTraceProcessor#runServer#block") {
+ return@inMemoryTrace inMemoryTrace("PerfettoTraceProcessor#runServer#block") {
block(perfettoTraceProcessor)
}
} finally {
@@ -145,7 +145,7 @@
*/
@RestrictTo(LIBRARY_GROUP) // avoids exposing Proto API
fun getTraceMetrics(metric: String): TraceMetrics {
- userspaceTrace("PerfettoTraceProcessor#getTraceMetrics $metric") {
+ inMemoryTrace("PerfettoTraceProcessor#getTraceMetrics $metric") {
require(!metric.contains(" ")) {
"Metric must not contain spaces: $metric"
}
@@ -186,7 +186,7 @@
* @see PerfettoTraceProcessor.Session
*/
fun query(@Language("sql") query: String): Sequence<Row> {
- userspaceTrace("PerfettoTraceProcessor#query $query".take(127)) {
+ inMemoryTrace("PerfettoTraceProcessor#query $query".take(127)) {
require(traceProcessor.perfettoHttpServer.isRunning()) {
"Perfetto trace_shell_process is not running."
}
@@ -224,7 +224,7 @@
* @see Session.query
*/
fun rawQuery(@Language("sql") query: String): ByteArray {
- userspaceTrace("PerfettoTraceProcessor#query $query".take(127)) {
+ inMemoryTrace("PerfettoTraceProcessor#query $query".take(127)) {
require(traceProcessor.perfettoHttpServer.isRunning()) {
"Perfetto trace_shell_process is not running."
}
@@ -238,21 +238,43 @@
* Note that sliceNames may include wildcard matches, such as `foo%`
*/
@RestrictTo(LIBRARY_GROUP) // Slice API not currently exposed, since it doesn't track table
- fun querySlices(vararg sliceNames: String): List<Slice> {
+ fun querySlices(
+ vararg sliceNames: String,
+ packageName: String?,
+ ): List<Slice> {
require(traceProcessor.perfettoHttpServer.isRunning()) {
"Perfetto trace_shell_process is not running."
}
val whereClause = sliceNames
- .joinToString(separator = " OR ") {
+ .joinToString(
+ separator = " OR ",
+ prefix = if (packageName == null) {
+ "("
+ } else {
+ processNameLikePkg(packageName) + " AND ("
+ },
+ postfix = ")"
+ ) {
"slice.name LIKE \"$it\""
}
+ val innerJoins = if (packageName != null) {
+ """
+ INNER JOIN thread_track on slice.track_id = thread_track.id
+ INNER JOIN thread USING(utid)
+ INNER JOIN process USING(upid)
+ """.trimMargin()
+ } else {
+ ""
+ }
return query(
query = """
SELECT slice.name,ts,dur
FROM slice
+ $innerJoins
WHERE $whereClause
+ ORDER BY ts
""".trimMargin()
).toSlices()
}
@@ -262,13 +284,13 @@
private var traceLoaded = false
private fun startServer(): PerfettoTraceProcessor =
- userspaceTrace("PerfettoTraceProcessor#startServer") {
+ inMemoryTrace("PerfettoTraceProcessor#startServer") {
println("startserver")
perfettoHttpServer.startServer()
- return@userspaceTrace this
+ return@inMemoryTrace this
}
- private fun stopServer() = userspaceTrace("PerfettoTraceProcessor#stopServer") {
+ private fun stopServer() = inMemoryTrace("PerfettoTraceProcessor#stopServer") {
println("stopserver")
perfettoHttpServer.stopServer()
}
@@ -278,7 +300,7 @@
* trace if existing.
*/
private fun loadTraceImpl(absoluteTracePath: String) {
- userspaceTrace("PerfettoTraceProcessor#loadTraceImpl") {
+ inMemoryTrace("PerfettoTraceProcessor#loadTraceImpl") {
require(!absoluteTracePath.contains(" ")) {
"Trace path must not contain spaces: $absoluteTracePath"
}
@@ -306,7 +328,7 @@
/**
* Clears the current loaded trace.
*/
- private fun clearTrace() = userspaceTrace("PerfettoTraceProcessor#clearTrace") {
+ private fun clearTrace() = inMemoryTrace("PerfettoTraceProcessor#clearTrace") {
perfettoHttpServer.restoreInitialTables()
traceLoaded = false
}
diff --git a/bluetooth/bluetooth-testing/src/test/kotlin/androidx/bluetooth/testing/RobolectricAdvertiseTest.kt b/bluetooth/bluetooth-testing/src/test/kotlin/androidx/bluetooth/testing/RobolectricAdvertiseTest.kt
index 8fb6552..34276ac 100644
--- a/bluetooth/bluetooth-testing/src/test/kotlin/androidx/bluetooth/testing/RobolectricAdvertiseTest.kt
+++ b/bluetooth/bluetooth-testing/src/test/kotlin/androidx/bluetooth/testing/RobolectricAdvertiseTest.kt
@@ -50,7 +50,7 @@
* the legacy advertise limit (31 bytes)
*/
@Test
- fun advertiseTooLargeDataO() = runTest {
+ fun advertiseTooLargeData() = runTest {
val parcelUuid = UUID.randomUUID()
val serviceData = "sampleAdvertiseDataTooLargeToAdvertise".toByteArray(Charsets.UTF_8)
diff --git a/bluetooth/bluetooth-testing/src/test/kotlin/androidx/bluetooth/testing/RobolectricGattClientTest.kt b/bluetooth/bluetooth-testing/src/test/kotlin/androidx/bluetooth/testing/RobolectricGattClientTest.kt
new file mode 100644
index 0000000..dddad2d
--- /dev/null
+++ b/bluetooth/bluetooth-testing/src/test/kotlin/androidx/bluetooth/testing/RobolectricGattClientTest.kt
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.bluetooth.testing
+
+import android.bluetooth.BluetoothAdapter
+import android.bluetooth.BluetoothDevice as FwkDevice
+import android.bluetooth.BluetoothGatt
+import android.bluetooth.BluetoothGattCallback
+import android.bluetooth.BluetoothGattCharacteristic
+import android.bluetooth.BluetoothGattDescriptor
+import android.bluetooth.BluetoothGattService
+import android.bluetooth.BluetoothManager
+import android.content.Context
+import androidx.bluetooth.BluetoothDevice
+import androidx.bluetooth.BluetoothLe
+import androidx.bluetooth.GattClient
+import java.util.UUID
+import kotlinx.coroutines.test.runTest
+import org.junit.Assert
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.RuntimeEnvironment
+import org.robolectric.Shadows.shadowOf
+import org.robolectric.shadows.ShadowBluetoothGatt
+
+@RunWith(RobolectricTestRunner::class)
+@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+class RobolectricGattClientTest {
+ private val context: Context = RuntimeEnvironment.getApplication()
+ private val bluetoothManager: BluetoothManager =
+ context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
+ private val bluetoothAdapter: BluetoothAdapter? = bluetoothManager.adapter
+ private lateinit var bluetoothLe: BluetoothLe
+ private lateinit var clientAdapter: StubClientFrameworkAdapter
+
+ private companion object {
+ private val serviceUuid1 = UUID.fromString("00001111-0000-1000-8000-00805F9B34FB")
+ private val serviceUuid2 = UUID.fromString("00001112-0000-1000-8000-00805F9B34FB")
+
+ private val service1 = BluetoothGattService(serviceUuid1,
+ BluetoothGattService.SERVICE_TYPE_PRIMARY)
+ private val service2 = BluetoothGattService(serviceUuid2,
+ BluetoothGattService.SERVICE_TYPE_PRIMARY)
+
+ private val sampleServices: List<BluetoothGattService> = listOf(service1, service2)
+ }
+
+ @Before
+ fun setUp() {
+ bluetoothLe = BluetoothLe(context)
+ clientAdapter = StubClientFrameworkAdapter(bluetoothLe.client.fwkAdapter)
+ bluetoothLe.client.fwkAdapter = clientAdapter
+ }
+
+ @Test
+ fun connectGatt() = runTest {
+ acceptConnect()
+ val device = createDevice("00:11:22:33:44:55")
+ Assert.assertEquals(true, bluetoothLe.connectGatt(device) {
+ Assert.assertEquals(sampleServices.size, getServices().size)
+ sampleServices.forEachIndexed { index, service ->
+ Assert.assertEquals(service.uuid, getServices()[index].uuid)
+ }
+ true
+ }.getOrNull())
+ }
+
+ @Test
+ fun connectFail() = runTest {
+ val device = createDevice("00:11:22:33:44:55")
+ rejectConnect()
+ Assert.assertEquals(true, bluetoothLe.connectGatt(device) { true }.isFailure)
+ }
+
+ private fun acceptConnect() {
+ clientAdapter.onConnectListener =
+ StubClientFrameworkAdapter.OnConnectListener { device, _ ->
+ shadowOf(device).simulateGattConnectionChange(
+ BluetoothGatt.GATT_SUCCESS, BluetoothGatt.STATE_CONNECTED
+ )
+ true
+ }
+
+ clientAdapter.onRequestMtuListener =
+ StubClientFrameworkAdapter.OnRequestMtuListener { mtu ->
+ clientAdapter.callback?.onMtuChanged(clientAdapter.bluetoothGatt, mtu,
+ BluetoothGatt.GATT_SUCCESS)
+ }
+
+ clientAdapter.onDiscoverServicesListener =
+ StubClientFrameworkAdapter.OnDiscoverServicesListener {
+ clientAdapter.gattServices = sampleServices
+ clientAdapter.callback?.onServicesDiscovered(clientAdapter.bluetoothGatt,
+ BluetoothGatt.GATT_SUCCESS)
+ }
+ }
+
+ private fun rejectConnect() {
+ clientAdapter.onConnectListener =
+ StubClientFrameworkAdapter.OnConnectListener { device, _ ->
+ shadowOf(device).simulateGattConnectionChange(
+ BluetoothGatt.GATT_FAILURE, BluetoothGatt.STATE_DISCONNECTED
+ )
+ false
+ }
+ }
+
+ private fun createDevice(address: String): BluetoothDevice {
+ return BluetoothDevice(bluetoothAdapter!!.getRemoteDevice(address))
+ }
+
+ class StubClientFrameworkAdapter(
+ private val baseAdapter: GattClient.FrameworkAdapter
+ ) : GattClient.FrameworkAdapter {
+ var gattServices: List<BluetoothGattService> = listOf()
+ var callback: BluetoothGattCallback? = null
+ override var bluetoothGatt: BluetoothGatt?
+ get() = baseAdapter.bluetoothGatt
+ set(value) { baseAdapter.bluetoothGatt = value }
+ val shadowBluetoothGatt: ShadowBluetoothGatt
+ get() = shadowOf(bluetoothGatt)
+
+ var onConnectListener: OnConnectListener? = null
+ var onRequestMtuListener: OnRequestMtuListener? = null
+ var onDiscoverServicesListener: OnDiscoverServicesListener? = null
+ var onReadCharacteristicListener: OnReadCharacteristicListener? = null
+ var onWriteCharacteristicListener: OnWriteCharacteristicListener? = null
+ var onWriteDescriptorListener: OnWriteDescriptorListener? = null
+ var onSetCharacteristicNotifiationListener: OnSetCharacteristicNotificationListener? = null
+
+ override fun connectGatt(
+ context: Context,
+ device: FwkDevice,
+ callback: BluetoothGattCallback
+ ): Boolean {
+ this.callback = callback
+ baseAdapter.connectGatt(context, device, callback)
+ return onConnectListener?.onConnect(device, callback) ?: false
+ }
+
+ override fun requestMtu(mtu: Int) {
+ baseAdapter.requestMtu(mtu)
+ onRequestMtuListener?.onRequestMtu(mtu)
+ }
+
+ override fun discoverServices() {
+ baseAdapter.discoverServices()
+ onDiscoverServicesListener?.onDiscoverServices()
+ }
+
+ override fun getServices(): List<BluetoothGattService> {
+ return gattServices
+ }
+
+ override fun getService(uuid: UUID): BluetoothGattService? {
+ return gattServices.find { it.uuid == uuid }
+ }
+
+ override fun readCharacteristic(characteristic: BluetoothGattCharacteristic) {
+ baseAdapter.readCharacteristic(characteristic)
+ onReadCharacteristicListener?.onReadCharacteristic(characteristic)
+ }
+
+ override fun writeCharacteristic(
+ characteristic: BluetoothGattCharacteristic,
+ value: ByteArray,
+ writeType: Int
+ ) {
+ baseAdapter.writeCharacteristic(characteristic, value, writeType)
+ onWriteCharacteristicListener?.onWriteCharacteristic(characteristic, value, writeType)
+ }
+
+ override fun writeDescriptor(descriptor: BluetoothGattDescriptor, value: ByteArray) {
+ baseAdapter.writeDescriptor(descriptor, value)
+ onWriteDescriptorListener?.onWriteDescriptor(descriptor, value)
+ }
+
+ override fun setCharacteristicNotification(
+ characteristic: BluetoothGattCharacteristic,
+ enable: Boolean
+ ) {
+ baseAdapter.setCharacteristicNotification(characteristic, enable)
+ onSetCharacteristicNotifiationListener
+ ?.onSetCharacteristicNotification(characteristic, enable)
+ }
+
+ fun interface OnConnectListener {
+ fun onConnect(device: FwkDevice, callback: BluetoothGattCallback): Boolean
+ }
+ fun interface OnRequestMtuListener {
+ fun onRequestMtu(mtu: Int)
+ }
+ fun interface OnDiscoverServicesListener {
+ fun onDiscoverServices()
+ }
+ fun interface OnReadCharacteristicListener {
+ fun onReadCharacteristic(characteristic: BluetoothGattCharacteristic)
+ }
+ fun interface OnWriteCharacteristicListener {
+ fun onWriteCharacteristic(
+ characteristic: BluetoothGattCharacteristic,
+ value: ByteArray,
+ writeType: Int
+ )
+ }
+ fun interface OnWriteDescriptorListener {
+ fun onWriteDescriptor(descriptor: BluetoothGattDescriptor, value: ByteArray)
+ }
+ fun interface OnSetCharacteristicNotificationListener {
+ fun onSetCharacteristicNotification(
+ characteristic: BluetoothGattCharacteristic,
+ enable: Boolean
+ )
+ }
+ }
+}
diff --git a/bluetooth/bluetooth-testing/src/test/kotlin/androidx/bluetooth/testing/RobolectricScanTest.kt b/bluetooth/bluetooth-testing/src/test/kotlin/androidx/bluetooth/testing/RobolectricScanTest.kt
index 283bc1d..1dd57c9 100644
--- a/bluetooth/bluetooth-testing/src/test/kotlin/androidx/bluetooth/testing/RobolectricScanTest.kt
+++ b/bluetooth/bluetooth-testing/src/test/kotlin/androidx/bluetooth/testing/RobolectricScanTest.kt
@@ -38,7 +38,7 @@
}
@Test
- fun scanTest() = runTest {
+ fun scan() = runTest {
try {
withTimeout(TIMEOUT_MS) {
bluetoothLe.scan(listOf(ScanFilter())).collect {
diff --git a/bluetooth/bluetooth/src/androidTest/java/androidx/bluetooth/BluetoothDeviceTest.kt b/bluetooth/bluetooth/src/androidTest/java/androidx/bluetooth/BluetoothDeviceTest.kt
index 98eeb61..9e8956d 100644
--- a/bluetooth/bluetooth/src/androidTest/java/androidx/bluetooth/BluetoothDeviceTest.kt
+++ b/bluetooth/bluetooth/src/androidTest/java/androidx/bluetooth/BluetoothDeviceTest.kt
@@ -55,7 +55,7 @@
Assume.assumeNotNull(bluetoothAdapter) // Bluetooth is not available if adapter is null
val fwkBluetoothDevice = bluetoothAdapter!!.getRemoteDevice("00:01:02:03:04:05")
- val bluetoothDevice = BluetoothDevice.of(fwkBluetoothDevice)
+ val bluetoothDevice = BluetoothDevice(fwkBluetoothDevice)
assertEquals(bluetoothDevice.bondState, fwkBluetoothDevice.bondState)
assertEquals(bluetoothDevice.name, fwkBluetoothDevice.name)
diff --git a/bluetooth/bluetooth/src/androidTest/java/androidx/bluetooth/ScanResultTest.kt b/bluetooth/bluetooth/src/androidTest/java/androidx/bluetooth/ScanResultTest.kt
index 9df2442..2e7b1fd 100644
--- a/bluetooth/bluetooth/src/androidTest/java/androidx/bluetooth/ScanResultTest.kt
+++ b/bluetooth/bluetooth/src/androidTest/java/androidx/bluetooth/ScanResultTest.kt
@@ -53,8 +53,8 @@
timeStampNanos)
val scanResult = ScanResult(fwkScanResult)
- assertEquals(scanResult.device.name, BluetoothDevice.of(fwkBluetoothDevice).name)
- assertEquals(scanResult.device.bondState, BluetoothDevice.of(fwkBluetoothDevice).bondState)
+ assertEquals(scanResult.device.name, BluetoothDevice(fwkBluetoothDevice).name)
+ assertEquals(scanResult.device.bondState, BluetoothDevice(fwkBluetoothDevice).bondState)
assertEquals(scanResult.deviceAddress.address, address)
assertEquals(scanResult.deviceAddress.addressType,
BluetoothAddress.ADDRESS_TYPE_RANDOM_STATIC)
diff --git a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/BluetoothDevice.kt b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/BluetoothDevice.kt
index 2cc4340..2c34db7 100644
--- a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/BluetoothDevice.kt
+++ b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/BluetoothDevice.kt
@@ -30,14 +30,9 @@
* @property bondState the bondState for this BluetoothDevice
*
*/
-class BluetoothDevice private constructor(
+class BluetoothDevice @RestrictTo(RestrictTo.Scope.LIBRARY) constructor(
internal val fwkDevice: FwkBluetoothDevice
) {
- internal companion object {
- fun of(device: FwkBluetoothDevice): BluetoothDevice {
- return BluetoothDevice(device)
- }
- }
val id: UUID = UUID.randomUUID()
@get:RequiresPermission(
diff --git a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/BluetoothLe.kt b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/BluetoothLe.kt
index 24b5d0d..cc95ef8 100644
--- a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/BluetoothLe.kt
+++ b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/BluetoothLe.kt
@@ -28,6 +28,7 @@
import android.util.Log
import androidx.annotation.RequiresPermission
import androidx.annotation.RestrictTo
+import androidx.annotation.VisibleForTesting
import java.util.UUID
import kotlinx.coroutines.cancel
import kotlinx.coroutines.channels.awaitClose
@@ -39,7 +40,7 @@
* operations such as scanning, advertising, and connection with a respective [BluetoothDevice].
*
*/
-class BluetoothLe(private val context: Context) {
+class BluetoothLe constructor(private val context: Context) {
private companion object {
private const val TAG = "BluetoothLe"
@@ -48,6 +49,10 @@
private val bluetoothManager =
context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager?
private val bluetoothAdapter = bluetoothManager?.adapter
+
+ @VisibleForTesting
+ @get:RestrictTo(RestrictTo.Scope.LIBRARY)
+ val client = GattClient(context)
private val server = GattServer(context)
/**
@@ -226,7 +231,7 @@
device: BluetoothDevice,
block: suspend GattClientScope.() -> R
): Result<R> {
- return GattClient().connect(context, device, block)
+ return client.connect(device, block)
}
/**
diff --git a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/GattClient.kt b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/GattClient.kt
index ab2b0df..411d947 100644
--- a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/GattClient.kt
+++ b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/GattClient.kt
@@ -27,6 +27,8 @@
import android.content.Context
import android.os.Build
import androidx.annotation.RequiresPermission
+import androidx.annotation.RestrictTo
+import androidx.annotation.VisibleForTesting
import java.util.UUID
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CompletableDeferred
@@ -48,13 +50,18 @@
/**
* A class for handling operations as a GATT client role.
*/
-internal class GattClient {
- private interface GattClientImpl {
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+class GattClient(private val context: Context) {
+ interface FrameworkAdapter {
+ var bluetoothGatt: BluetoothGatt?
fun connectGatt(
context: Context,
device: FwkDevice,
callback: BluetoothGattCallback
): Boolean
+ fun requestMtu(mtu: Int)
+
+ fun discoverServices()
fun getServices(): List<FwkService>
fun getService(uuid: UUID): FwkService?
@@ -70,7 +77,9 @@
fun setCharacteristicNotification(characteristic: FwkCharacteristic, enable: Boolean)
}
- private companion object {
+ @VisibleForTesting
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ companion object {
private const val TAG = "GattClient"
/**
@@ -81,9 +90,11 @@
}
@SuppressLint("ObsoleteSdkInt")
- private val impl: GattClientImpl =
- if (Build.VERSION.SDK_INT >= 33) GattClientImplApi33()
- else BaseGattClientImpl()
+ @VisibleForTesting
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
+ var fwkAdapter: FrameworkAdapter =
+ if (Build.VERSION.SDK_INT >= 33) FrameworkAdapterApi33()
+ else FrameworkAdapterBase()
private sealed interface CallbackResult {
class OnCharacteristicRead(
@@ -116,7 +127,6 @@
@SuppressLint("MissingPermission")
suspend fun <R> connect(
- context: Context,
device: BluetoothDevice,
block: suspend BluetoothLe.GattClientScope.() -> R
): Result<R> = coroutineScope {
@@ -130,7 +140,7 @@
val callback = object : BluetoothGattCallback() {
override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) {
if (newState == BluetoothGatt.STATE_CONNECTED) {
- gatt?.requestMtu(GATT_MAX_MTU)
+ fwkAdapter.requestMtu(GATT_MAX_MTU)
} else {
connectResult.cancel("connect failed")
}
@@ -138,16 +148,14 @@
override fun onMtuChanged(gatt: BluetoothGatt?, mtu: Int, status: Int) {
if (status == BluetoothGatt.GATT_SUCCESS) {
- gatt?.discoverServices()
+ fwkAdapter.discoverServices()
} else {
connectResult.cancel("mtu request failed")
}
}
override fun onServicesDiscovered(gatt: BluetoothGatt?, status: Int) {
- gatt?.let {
- attributeMap.updateWithFrameworkServices(it.services)
- }
+ attributeMap.updateWithFrameworkServices(fwkAdapter.getServices())
if (status == BluetoothGatt.GATT_SUCCESS) connectResult.complete(Unit)
else connectResult.cancel("service discover failed")
}
@@ -208,7 +216,7 @@
}
}
}
- if (!impl.connectGatt(context, device.fwkDevice, callback)) {
+ if (!fwkAdapter.connectGatt(context, device.fwkDevice, callback)) {
return@coroutineScope Result.failure(CancellationException("failed to connect"))
}
@@ -230,7 +238,7 @@
}
override fun getService(uuid: UUID): GattService? {
- return impl.getService(uuid)?.let { attributeMap.fromFwkService(it) }
+ return fwkAdapter.getService(uuid)?.let { attributeMap.fromFwkService(it) }
}
override suspend fun readCharacteristic(characteristic: GattCharacteristic):
@@ -239,7 +247,7 @@
return Result.failure(IllegalArgumentException("can't read the characteristic"))
}
return runTask {
- impl.readCharacteristic(characteristic.fwkCharacteristic)
+ fwkAdapter.readCharacteristic(characteristic.fwkCharacteristic)
val res = takeMatchingResult<CallbackResult.OnCharacteristicRead>(
callbackResultsFlow
) {
@@ -263,7 +271,8 @@
)
}
return runTask {
- impl.writeCharacteristic(characteristic.fwkCharacteristic, value, writeType)
+ fwkAdapter.writeCharacteristic(
+ characteristic.fwkCharacteristic, value, writeType)
val res = takeMatchingResult<CallbackResult.OnCharacteristicWrite>(
callbackResultsFlow
) {
@@ -298,11 +307,11 @@
}
runTask {
- impl.setCharacteristicNotification(
+ fwkAdapter.setCharacteristicNotification(
characteristic.fwkCharacteristic, /*enable=*/true
)
- impl.writeDescriptor(cccd, FwkDescriptor.ENABLE_NOTIFICATION_VALUE)
+ fwkAdapter.writeDescriptor(cccd, FwkDescriptor.ENABLE_NOTIFICATION_VALUE)
val res = takeMatchingResult<CallbackResult.OnDescriptorWrite>(
callbackResultsFlow
) {
@@ -317,11 +326,11 @@
launch {
unregisterSubscribeListener(characteristic.fwkCharacteristic)
}
- impl.setCharacteristicNotification(
+ fwkAdapter.setCharacteristicNotification(
characteristic.fwkCharacteristic, /*enable=*/false
)
- impl.writeDescriptor(cccd, FwkDescriptor.DISABLE_NOTIFICATION_VALUE)
+ fwkAdapter.writeDescriptor(cccd, FwkDescriptor.DISABLE_NOTIFICATION_VALUE)
}
}
}
@@ -374,8 +383,8 @@
return flow.filter { it is R && predicate(it) }.first() as R
}
- private open class BaseGattClientImpl : GattClientImpl {
- var bluetoothGatt: BluetoothGatt? = null
+ private open class FrameworkAdapterBase : FrameworkAdapter {
+ override var bluetoothGatt: BluetoothGatt? = null
@RequiresPermission(BLUETOOTH_CONNECT)
override fun connectGatt(
@@ -387,6 +396,16 @@
return bluetoothGatt != null
}
+ @RequiresPermission(BLUETOOTH_CONNECT)
+ override fun requestMtu(mtu: Int) {
+ bluetoothGatt?.requestMtu(mtu)
+ }
+
+ @RequiresPermission(BLUETOOTH_CONNECT)
+ override fun discoverServices() {
+ bluetoothGatt?.discoverServices()
+ }
+
override fun getServices(): List<FwkService> {
return bluetoothGatt?.services ?: listOf()
}
@@ -427,7 +446,7 @@
}
}
- private open class GattClientImplApi33 : BaseGattClientImpl() {
+ private open class FrameworkAdapterApi33 : FrameworkAdapterBase() {
@RequiresPermission(BLUETOOTH_CONNECT)
override fun writeCharacteristic(
characteristic: FwkCharacteristic,
diff --git a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/GattServer.kt b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/GattServer.kt
index 0c22a3b..785983d 100644
--- a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/GattServer.kt
+++ b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/GattServer.kt
@@ -99,7 +99,7 @@
BluetoothProfile.STATE_CONNECTED -> {
trySend(
BluetoothLe.GattServerConnectionRequest(
- BluetoothDevice.of(device),
+ BluetoothDevice(device),
this@GattServer,
addSession(device)
)
diff --git a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/ScanResult.kt b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/ScanResult.kt
index f98714b..045b3de 100644
--- a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/ScanResult.kt
+++ b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/ScanResult.kt
@@ -39,7 +39,7 @@
/** Remote Bluetooth device found. */
val device: BluetoothDevice
- get() = BluetoothDevice.of(fwkScanResult.device)
+ get() = BluetoothDevice(fwkScanResult.device)
// TODO(kihongs) Find a way to get address type from framework scan result
/** Bluetooth address for the remote device found. */
diff --git a/buildSrc-tests/src/test/java/androidx/build/testConfiguration/AndroidTestConfigBuilderTest.kt b/buildSrc-tests/src/test/java/androidx/build/testConfiguration/AndroidTestConfigBuilderTest.kt
index e4d7736..8d92531 100644
--- a/buildSrc-tests/src/test/java/androidx/build/testConfiguration/AndroidTestConfigBuilderTest.kt
+++ b/buildSrc-tests/src/test/java/androidx/build/testConfiguration/AndroidTestConfigBuilderTest.kt
@@ -350,6 +350,7 @@
<option name="wifi:disable" value="true" />
<option name="instrumentation-arg" key="notAnnotation" value="androidx.test.filters.FlakyTest" />
<option name="instrumentation-arg" key="listener" value="androidx.benchmark.junit4.InstrumentationResultsRunListener" />
+ <option name="instrumentation-arg" key="listener" value="androidx.benchmark.junit4.SideEffectRunListener" />
<include name="google/unbundled/common/setup" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
@@ -358,6 +359,7 @@
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="run-command" value="cmd package compile -f -m speed com.androidx.placeholder.Placeholder" />
+ <option name="run-command-timeout" value="240000" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest">
<option name="runner" value="com.example.Runner"/>
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/AndroidTestConfigBuilder.kt b/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/AndroidTestConfigBuilder.kt
index 657f702..90fd4df 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/AndroidTestConfigBuilder.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/testConfiguration/AndroidTestConfigBuilder.kt
@@ -242,6 +242,7 @@
"""
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="run-command" value="${benchmarkPostInstallCommand(packageName)}" />
+ <option name="run-command-timeout" value="240000" />
</target_preparer>
""".trimIndent()
@@ -323,6 +324,7 @@
private val MICROBENCHMARK_POSTSUBMIT_OPTIONS =
"""
<option name="instrumentation-arg" key="listener" value="androidx.benchmark.junit4.InstrumentationResultsRunListener" />
+ <option name="instrumentation-arg" key="listener" value="androidx.benchmark.junit4.SideEffectRunListener" />
"""
.trimIndent()
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraControl.java b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraControl.java
index 86ce8a9..4f64bb4 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraControl.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraControl.java
@@ -193,8 +193,8 @@
@NonNull
@Override
- public ListenableFuture<Integer> setExposureCompensationIndex(int exposure) {
- mExposureCompensation = exposure;
+ public ListenableFuture<Integer> setExposureCompensationIndex(int value) {
+ mExposureCompensation = value;
return Futures.immediateFuture(null);
}
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/mocks/MockConsumer.java b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/mocks/MockConsumer.java
index 904e34e..c3c8932 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/mocks/MockConsumer.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/mocks/MockConsumer.java
@@ -261,8 +261,8 @@
}
@Override
- public void accept(T event) {
- mEventList.add(event);
+ public void accept(T t) {
+ mEventList.add(t);
synchronized (mLock) {
if (mLatch != null && isVerified(mEventList)) {
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/audio/AudioSource.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/audio/AudioSource.java
index a4b0e4e4..2b78756 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/internal/audio/AudioSource.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/audio/AudioSource.java
@@ -469,7 +469,7 @@
NANOSECONDS.toMicros(packetInfo.getTimestampNs()));
inputBuffer.submit();
} else {
- Logger.w(TAG, "Unable to read data from AudioRecord.");
+ Logger.w(TAG, "Unable to read data from AudioStream.");
inputBuffer.cancel();
}
sendNextAudio();
diff --git a/camera/camera-video/src/main/java/androidx/camera/video/internal/audio/BufferedAudioStream.java b/camera/camera-video/src/main/java/androidx/camera/video/internal/audio/BufferedAudioStream.java
index 7c3d659..bbd013b 100644
--- a/camera/camera-video/src/main/java/androidx/camera/video/internal/audio/BufferedAudioStream.java
+++ b/camera/camera-video/src/main/java/androidx/camera/video/internal/audio/BufferedAudioStream.java
@@ -21,6 +21,8 @@
import static androidx.core.util.Preconditions.checkArgument;
import static androidx.core.util.Preconditions.checkState;
+import android.annotation.SuppressLint;
+
import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -55,6 +57,7 @@
private static final String TAG = "BufferedAudioStream";
private static final int DEFAULT_BUFFER_SIZE_IN_FRAME = 1024;
private static final int DEFAULT_QUEUE_SIZE = 500;
+ private static final int DATA_WAITING_TIME_MILLIS = 1;
private final AtomicBoolean mIsStarted = new AtomicBoolean(false);
private final AtomicBoolean mIsReleased = new AtomicBoolean(false);
@@ -151,6 +154,7 @@
});
}
+ @SuppressLint("BanThreadSleep")
@NonNull
@Override
public PacketInfo read(@NonNull ByteBuffer byteBuffer) {
@@ -160,24 +164,40 @@
// Match collection buffer size and read buffer size to improve read efficiency.
updateCollectionBufferSizeAsync(byteBuffer.remaining());
+ // Block the thread till the audio data is actually read.
+ boolean isWaitingForData;
PacketInfo packetInfo = PacketInfo.of(0, 0);
- synchronized (mLock) {
- AudioData audioData = mAudioDataNotFullyRead;
- mAudioDataNotFullyRead = null;
- if (audioData == null) {
- audioData = mAudioDataQueue.poll();
- }
-
- if (audioData != null) {
- packetInfo = audioData.read(byteBuffer);
-
- if (audioData.getRemainingBufferSizeInBytes() > 0) {
- mAudioDataNotFullyRead = audioData;
+ do {
+ synchronized (mLock) {
+ AudioData audioData = mAudioDataNotFullyRead;
+ mAudioDataNotFullyRead = null;
+ if (audioData == null) {
+ audioData = mAudioDataQueue.poll();
}
- } else {
- Logger.d(TAG, "No data to read.");
+
+ if (audioData != null) {
+ packetInfo = audioData.read(byteBuffer);
+
+ if (audioData.getRemainingBufferSizeInBytes() > 0) {
+ mAudioDataNotFullyRead = audioData;
+ }
+ }
}
- }
+
+ // Wait for data collection if no data to read and the audio stream is still running.
+ isWaitingForData =
+ packetInfo.getSizeInBytes() <= 0 && mIsStarted.get() && !mIsReleased.get();
+
+ // Sleep to prevent busy accessing to variables.
+ if (isWaitingForData) {
+ try {
+ Thread.sleep(DATA_WAITING_TIME_MILLIS);
+ } catch (InterruptedException e) {
+ Logger.w(TAG, "Interruption while waiting for audio data", e);
+ break;
+ }
+ }
+ } while (isWaitingForData);
return packetInfo;
}
diff --git a/car/app/app-automotive/api/1.4.0-beta01.txt b/car/app/app-automotive/api/1.4.0-beta01.txt
new file mode 100644
index 0000000..33c4502
--- /dev/null
+++ b/car/app/app-automotive/api/1.4.0-beta01.txt
@@ -0,0 +1,101 @@
+// Signature format: 4.0
+package androidx.car.app.activity {
+
+ public abstract class BaseCarAppActivity extends androidx.fragment.app.FragmentActivity implements androidx.lifecycle.LifecycleOwner {
+ ctor public BaseCarAppActivity();
+ method public void bindToViewModel(androidx.car.app.SessionInfo);
+ method public android.content.ComponentName? getServiceComponentName();
+ method @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public android.content.ComponentName? retrieveServiceComponentName();
+ }
+
+ public final class CarAppActivity extends androidx.car.app.activity.BaseCarAppActivity implements androidx.lifecycle.LifecycleOwner {
+ ctor public CarAppActivity();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class LauncherActivity extends androidx.fragment.app.FragmentActivity implements androidx.lifecycle.LifecycleOwner {
+ ctor public LauncherActivity();
+ }
+
+}
+
+package androidx.car.app.activity.renderer.surface {
+
+ @SuppressCompatibility public final class LegacySurfacePackage {
+ ctor public LegacySurfacePackage(androidx.car.app.activity.renderer.surface.SurfaceControlCallback);
+ }
+
+ public interface SurfaceControlCallback {
+ method public default void onError(String, Throwable);
+ method public void onKeyEvent(android.view.KeyEvent);
+ method public void onTouchEvent(android.view.MotionEvent);
+ method public void onWindowFocusChanged(boolean, boolean);
+ method public void setSurfaceWrapper(androidx.car.app.activity.renderer.surface.SurfaceWrapper);
+ }
+
+ @SuppressCompatibility public final class SurfaceWrapper {
+ ctor public SurfaceWrapper(android.os.IBinder?, @Dimension int, @Dimension int, int, int, android.view.Surface);
+ method public int getDensityDpi();
+ method public int getDisplayId();
+ method @Dimension public int getHeight();
+ method public android.os.IBinder? getHostToken();
+ method public android.view.Surface getSurface();
+ method @Dimension public int getWidth();
+ }
+
+}
+
+package androidx.car.app.hardware {
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class AutomotiveCarHardwareManager implements androidx.car.app.hardware.CarHardwareManager {
+ ctor public AutomotiveCarHardwareManager(android.content.Context);
+ }
+
+}
+
+package androidx.car.app.hardware.common {
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class CarZoneAreaIdConstants {
+ field public static final int AREA_ID_GLOBAL = 0; // 0x0
+ }
+
+ public static final class CarZoneAreaIdConstants.VehicleAreaSeat {
+ field public static final int COL_ALL = 1911; // 0x777
+ field public static final int COL_CENTER = 546; // 0x222
+ field public static final int COL_LEFT = 273; // 0x111
+ field public static final int COL_RIGHT = 1092; // 0x444
+ field public static final int ROW_1_CENTER = 2; // 0x2
+ field public static final int ROW_1_LEFT = 1; // 0x1
+ field public static final int ROW_1_RIGHT = 4; // 0x4
+ field public static final int ROW_2_CENTER = 32; // 0x20
+ field public static final int ROW_2_LEFT = 16; // 0x10
+ field public static final int ROW_2_RIGHT = 64; // 0x40
+ field public static final int ROW_3_CENTER = 512; // 0x200
+ field public static final int ROW_3_LEFT = 256; // 0x100
+ field public static final int ROW_3_RIGHT = 1024; // 0x400
+ field public static final int ROW_ALL = 1911; // 0x777
+ field public static final int ROW_FIRST = 7; // 0x7
+ field public static final int ROW_SECOND = 112; // 0x70
+ field public static final int ROW_THIRD = 1792; // 0x700
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public interface CarZoneAreaIdConverter {
+ method public com.google.common.collect.ImmutableSet<androidx.car.app.hardware.common.CarZone!> convertAreaIdToCarZones(int);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class CarZoneUtils {
+ method public static com.google.common.collect.ImmutableSet<androidx.car.app.hardware.common.CarZone!> convertAreaIdToCarZones(int, int);
+ method public static androidx.car.app.hardware.common.CarZoneAreaIdConverter getZoneAreaIdConverter(int);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public class GlobalCarZoneAreaIdConverter implements androidx.car.app.hardware.common.CarZoneAreaIdConverter {
+ ctor public GlobalCarZoneAreaIdConverter();
+ method public com.google.common.collect.ImmutableSet<androidx.car.app.hardware.common.CarZone!> convertAreaIdToCarZones(int);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public class SeatCarZoneAreaIdConverter implements androidx.car.app.hardware.common.CarZoneAreaIdConverter {
+ ctor public SeatCarZoneAreaIdConverter();
+ method public com.google.common.collect.ImmutableSet<androidx.car.app.hardware.common.CarZone!> convertAreaIdToCarZones(int);
+ }
+
+}
+
diff --git a/car/app/app-automotive/api/res-1.4.0-beta01.txt b/car/app/app-automotive/api/res-1.4.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/car/app/app-automotive/api/res-1.4.0-beta01.txt
diff --git a/car/app/app-automotive/api/restricted_1.4.0-beta01.txt b/car/app/app-automotive/api/restricted_1.4.0-beta01.txt
new file mode 100644
index 0000000..433cb93
--- /dev/null
+++ b/car/app/app-automotive/api/restricted_1.4.0-beta01.txt
@@ -0,0 +1,101 @@
+// Signature format: 4.0
+package androidx.car.app.activity {
+
+ public abstract class BaseCarAppActivity extends androidx.fragment.app.FragmentActivity {
+ ctor public BaseCarAppActivity();
+ method public void bindToViewModel(androidx.car.app.SessionInfo);
+ method public android.content.ComponentName? getServiceComponentName();
+ method @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public android.content.ComponentName? retrieveServiceComponentName();
+ }
+
+ public final class CarAppActivity extends androidx.car.app.activity.BaseCarAppActivity {
+ ctor public CarAppActivity();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class LauncherActivity extends androidx.fragment.app.FragmentActivity {
+ ctor public LauncherActivity();
+ }
+
+}
+
+package androidx.car.app.activity.renderer.surface {
+
+ @SuppressCompatibility public final class LegacySurfacePackage {
+ ctor public LegacySurfacePackage(androidx.car.app.activity.renderer.surface.SurfaceControlCallback);
+ }
+
+ public interface SurfaceControlCallback {
+ method public default void onError(String, Throwable);
+ method public void onKeyEvent(android.view.KeyEvent);
+ method public void onTouchEvent(android.view.MotionEvent);
+ method public void onWindowFocusChanged(boolean, boolean);
+ method public void setSurfaceWrapper(androidx.car.app.activity.renderer.surface.SurfaceWrapper);
+ }
+
+ @SuppressCompatibility public final class SurfaceWrapper {
+ ctor public SurfaceWrapper(android.os.IBinder?, @Dimension int, @Dimension int, int, int, android.view.Surface);
+ method public int getDensityDpi();
+ method public int getDisplayId();
+ method @Dimension public int getHeight();
+ method public android.os.IBinder? getHostToken();
+ method public android.view.Surface getSurface();
+ method @Dimension public int getWidth();
+ }
+
+}
+
+package androidx.car.app.hardware {
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class AutomotiveCarHardwareManager implements androidx.car.app.hardware.CarHardwareManager {
+ ctor public AutomotiveCarHardwareManager(android.content.Context);
+ }
+
+}
+
+package androidx.car.app.hardware.common {
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class CarZoneAreaIdConstants {
+ field public static final int AREA_ID_GLOBAL = 0; // 0x0
+ }
+
+ public static final class CarZoneAreaIdConstants.VehicleAreaSeat {
+ field public static final int COL_ALL = 1911; // 0x777
+ field public static final int COL_CENTER = 546; // 0x222
+ field public static final int COL_LEFT = 273; // 0x111
+ field public static final int COL_RIGHT = 1092; // 0x444
+ field public static final int ROW_1_CENTER = 2; // 0x2
+ field public static final int ROW_1_LEFT = 1; // 0x1
+ field public static final int ROW_1_RIGHT = 4; // 0x4
+ field public static final int ROW_2_CENTER = 32; // 0x20
+ field public static final int ROW_2_LEFT = 16; // 0x10
+ field public static final int ROW_2_RIGHT = 64; // 0x40
+ field public static final int ROW_3_CENTER = 512; // 0x200
+ field public static final int ROW_3_LEFT = 256; // 0x100
+ field public static final int ROW_3_RIGHT = 1024; // 0x400
+ field public static final int ROW_ALL = 1911; // 0x777
+ field public static final int ROW_FIRST = 7; // 0x7
+ field public static final int ROW_SECOND = 112; // 0x70
+ field public static final int ROW_THIRD = 1792; // 0x700
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public interface CarZoneAreaIdConverter {
+ method public com.google.common.collect.ImmutableSet<androidx.car.app.hardware.common.CarZone!> convertAreaIdToCarZones(int);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class CarZoneUtils {
+ method public static com.google.common.collect.ImmutableSet<androidx.car.app.hardware.common.CarZone!> convertAreaIdToCarZones(int, int);
+ method public static androidx.car.app.hardware.common.CarZoneAreaIdConverter getZoneAreaIdConverter(int);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public class GlobalCarZoneAreaIdConverter implements androidx.car.app.hardware.common.CarZoneAreaIdConverter {
+ ctor public GlobalCarZoneAreaIdConverter();
+ method public com.google.common.collect.ImmutableSet<androidx.car.app.hardware.common.CarZone!> convertAreaIdToCarZones(int);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public class SeatCarZoneAreaIdConverter implements androidx.car.app.hardware.common.CarZoneAreaIdConverter {
+ ctor public SeatCarZoneAreaIdConverter();
+ method public com.google.common.collect.ImmutableSet<androidx.car.app.hardware.common.CarZone!> convertAreaIdToCarZones(int);
+ }
+
+}
+
diff --git a/car/app/app-projected/api/1.4.0-beta01.txt b/car/app/app-projected/api/1.4.0-beta01.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/car/app/app-projected/api/1.4.0-beta01.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/car/app/app-projected/api/res-1.4.0-beta01.txt b/car/app/app-projected/api/res-1.4.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/car/app/app-projected/api/res-1.4.0-beta01.txt
diff --git a/car/app/app-projected/api/restricted_1.4.0-beta01.txt b/car/app/app-projected/api/restricted_1.4.0-beta01.txt
new file mode 100644
index 0000000..e6f50d0
--- /dev/null
+++ b/car/app/app-projected/api/restricted_1.4.0-beta01.txt
@@ -0,0 +1 @@
+// Signature format: 4.0
diff --git a/car/app/app-testing/api/1.4.0-beta01.txt b/car/app/app-testing/api/1.4.0-beta01.txt
new file mode 100644
index 0000000..57cf025
--- /dev/null
+++ b/car/app/app-testing/api/1.4.0-beta01.txt
@@ -0,0 +1,66 @@
+// Signature format: 4.0
+package androidx.car.app.testing {
+
+ public class FakeHost {
+ method public void performNotificationActionClick(android.app.PendingIntent);
+ method @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public void setMicrophoneInputData(java.io.InputStream);
+ }
+
+ public class ScreenController {
+ ctor public ScreenController(androidx.car.app.Screen);
+ method public androidx.car.app.Screen getScreen();
+ method public Object? getScreenResult();
+ method public java.util.List<androidx.car.app.model.Template!> getTemplatesReturned();
+ method public androidx.car.app.testing.ScreenController moveToState(androidx.lifecycle.Lifecycle.State);
+ method public void reset();
+ }
+
+ public class SessionController {
+ ctor public SessionController(androidx.car.app.Session, androidx.car.app.testing.TestCarContext, android.content.Intent);
+ method public androidx.car.app.Session getSession();
+ method public androidx.car.app.testing.SessionController moveToState(androidx.lifecycle.Lifecycle.State);
+ }
+
+ public class TestAppManager extends androidx.car.app.AppManager {
+ method public androidx.car.app.SurfaceCallback? getSurfaceCallback();
+ method public java.util.List<android.util.Pair<androidx.car.app.Screen!,androidx.car.app.model.Template!>!> getTemplatesReturned();
+ method public java.util.List<java.lang.CharSequence!> getToastsShown();
+ method public void reset();
+ }
+
+ public class TestCarContext extends androidx.car.app.CarContext {
+ method public static androidx.car.app.testing.TestCarContext createCarContext(android.content.Context);
+ method public androidx.car.app.testing.FakeHost getFakeHost();
+ method public androidx.car.app.testing.TestCarContext.PermissionRequestInfo? getLastPermissionRequestInfo();
+ method public java.util.List<android.content.Intent!> getStartCarAppIntents();
+ method public boolean hasCalledFinishCarApp();
+ method public void reset();
+ }
+
+ public static class TestCarContext.PermissionRequestInfo {
+ method public androidx.car.app.OnRequestPermissionsListener getListener();
+ method public java.util.List<java.lang.String!> getPermissionsRequested();
+ }
+
+ public class TestScreenManager extends androidx.car.app.ScreenManager {
+ method public java.util.List<androidx.car.app.Screen!> getScreensPushed();
+ method public java.util.List<androidx.car.app.Screen!> getScreensRemoved();
+ method public boolean hasScreens();
+ method public void reset();
+ }
+
+}
+
+package androidx.car.app.testing.navigation {
+
+ public class TestNavigationManager extends androidx.car.app.navigation.NavigationManager {
+ ctor public TestNavigationManager(androidx.car.app.testing.TestCarContext, androidx.car.app.HostDispatcher);
+ method public int getNavigationEndedCount();
+ method public androidx.car.app.navigation.NavigationManagerCallback? getNavigationManagerCallback();
+ method public int getNavigationStartedCount();
+ method public java.util.List<androidx.car.app.navigation.model.Trip!> getTripsSent();
+ method public void reset();
+ }
+
+}
+
diff --git a/car/app/app-testing/api/res-1.4.0-beta01.txt b/car/app/app-testing/api/res-1.4.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/car/app/app-testing/api/res-1.4.0-beta01.txt
diff --git a/car/app/app-testing/api/restricted_1.4.0-beta01.txt b/car/app/app-testing/api/restricted_1.4.0-beta01.txt
new file mode 100644
index 0000000..57cf025
--- /dev/null
+++ b/car/app/app-testing/api/restricted_1.4.0-beta01.txt
@@ -0,0 +1,66 @@
+// Signature format: 4.0
+package androidx.car.app.testing {
+
+ public class FakeHost {
+ method public void performNotificationActionClick(android.app.PendingIntent);
+ method @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public void setMicrophoneInputData(java.io.InputStream);
+ }
+
+ public class ScreenController {
+ ctor public ScreenController(androidx.car.app.Screen);
+ method public androidx.car.app.Screen getScreen();
+ method public Object? getScreenResult();
+ method public java.util.List<androidx.car.app.model.Template!> getTemplatesReturned();
+ method public androidx.car.app.testing.ScreenController moveToState(androidx.lifecycle.Lifecycle.State);
+ method public void reset();
+ }
+
+ public class SessionController {
+ ctor public SessionController(androidx.car.app.Session, androidx.car.app.testing.TestCarContext, android.content.Intent);
+ method public androidx.car.app.Session getSession();
+ method public androidx.car.app.testing.SessionController moveToState(androidx.lifecycle.Lifecycle.State);
+ }
+
+ public class TestAppManager extends androidx.car.app.AppManager {
+ method public androidx.car.app.SurfaceCallback? getSurfaceCallback();
+ method public java.util.List<android.util.Pair<androidx.car.app.Screen!,androidx.car.app.model.Template!>!> getTemplatesReturned();
+ method public java.util.List<java.lang.CharSequence!> getToastsShown();
+ method public void reset();
+ }
+
+ public class TestCarContext extends androidx.car.app.CarContext {
+ method public static androidx.car.app.testing.TestCarContext createCarContext(android.content.Context);
+ method public androidx.car.app.testing.FakeHost getFakeHost();
+ method public androidx.car.app.testing.TestCarContext.PermissionRequestInfo? getLastPermissionRequestInfo();
+ method public java.util.List<android.content.Intent!> getStartCarAppIntents();
+ method public boolean hasCalledFinishCarApp();
+ method public void reset();
+ }
+
+ public static class TestCarContext.PermissionRequestInfo {
+ method public androidx.car.app.OnRequestPermissionsListener getListener();
+ method public java.util.List<java.lang.String!> getPermissionsRequested();
+ }
+
+ public class TestScreenManager extends androidx.car.app.ScreenManager {
+ method public java.util.List<androidx.car.app.Screen!> getScreensPushed();
+ method public java.util.List<androidx.car.app.Screen!> getScreensRemoved();
+ method public boolean hasScreens();
+ method public void reset();
+ }
+
+}
+
+package androidx.car.app.testing.navigation {
+
+ public class TestNavigationManager extends androidx.car.app.navigation.NavigationManager {
+ ctor public TestNavigationManager(androidx.car.app.testing.TestCarContext, androidx.car.app.HostDispatcher);
+ method public int getNavigationEndedCount();
+ method public androidx.car.app.navigation.NavigationManagerCallback? getNavigationManagerCallback();
+ method public int getNavigationStartedCount();
+ method public java.util.List<androidx.car.app.navigation.model.Trip!> getTripsSent();
+ method public void reset();
+ }
+
+}
+
diff --git a/car/app/app/api/1.4.0-beta01.txt b/car/app/app/api/1.4.0-beta01.txt
new file mode 100644
index 0000000..73c12de
--- /dev/null
+++ b/car/app/app/api/1.4.0-beta01.txt
@@ -0,0 +1,2210 @@
+// Signature format: 4.0
+package androidx.car.app {
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class AppInfo {
+ ctor @VisibleForTesting public AppInfo(int, int, String);
+ method public int getLatestCarAppApiLevel();
+ method public String getLibraryDisplayVersion();
+ method public int getMinCarAppApiLevel();
+ field public static final String MIN_API_LEVEL_METADATA_KEY = "androidx.car.app.minCarApiLevel";
+ }
+
+ public class AppManager implements androidx.car.app.managers.Manager {
+ method @androidx.car.app.annotations.RequiresCarApi(5) public void dismissAlert(int);
+ method public void invalidate();
+ method public void setSurfaceCallback(androidx.car.app.SurfaceCallback?);
+ method @androidx.car.app.annotations.RequiresCarApi(5) public void showAlert(androidx.car.app.model.Alert);
+ method public void showToast(CharSequence, int);
+ }
+
+ public final class CarAppPermission {
+ method public static void checkHasLibraryPermission(android.content.Context, String);
+ method public static void checkHasPermission(android.content.Context, String);
+ field public static final String ACCESS_SURFACE = "androidx.car.app.ACCESS_SURFACE";
+ field public static final String MAP_TEMPLATES = "androidx.car.app.MAP_TEMPLATES";
+ field public static final String NAVIGATION_TEMPLATES = "androidx.car.app.NAVIGATION_TEMPLATES";
+ }
+
+ public abstract class CarAppService extends android.app.Service {
+ ctor public CarAppService();
+ method public abstract androidx.car.app.validation.HostValidator createHostValidator();
+ method @CallSuper public final void dump(java.io.FileDescriptor, java.io.PrintWriter, String![]?);
+ method @Deprecated public final androidx.car.app.Session? getCurrentSession();
+ method public final androidx.car.app.HostInfo? getHostInfo();
+ method public final androidx.car.app.Session? getSession(androidx.car.app.SessionInfo);
+ method @CallSuper public final android.os.IBinder onBind(android.content.Intent);
+ method public androidx.car.app.Session onCreateSession();
+ method @androidx.car.app.annotations.RequiresCarApi(6) public androidx.car.app.Session onCreateSession(androidx.car.app.SessionInfo);
+ method public final boolean onUnbind(android.content.Intent);
+ field @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public static final String CATEGORY_CALLING_APP = "androidx.car.app.category.CALLING";
+ field @Deprecated public static final String CATEGORY_CHARGING_APP = "androidx.car.app.category.CHARGING";
+ field @androidx.car.app.annotations.RequiresCarApi(6) public static final String CATEGORY_FEATURE_CLUSTER = "androidx.car.app.category.FEATURE_CLUSTER";
+ field @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public static final String CATEGORY_IOT_APP = "androidx.car.app.category.IOT";
+ field @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public static final String CATEGORY_MESSAGING_APP = "androidx.car.app.category.MESSAGING";
+ field public static final String CATEGORY_NAVIGATION_APP = "androidx.car.app.category.NAVIGATION";
+ field @Deprecated public static final String CATEGORY_PARKING_APP = "androidx.car.app.category.PARKING";
+ field public static final String CATEGORY_POI_APP = "androidx.car.app.category.POI";
+ field @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public static final String CATEGORY_SETTINGS_APP = "androidx.car.app.category.SETTINGS";
+ field @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public static final String CATEGORY_WEATHER_APP = "androidx.car.app.category.WEATHER";
+ field public static final String SERVICE_INTERFACE = "androidx.car.app.CarAppService";
+ }
+
+ public class CarContext extends android.content.ContextWrapper {
+ method public void finishCarApp();
+ method @androidx.car.app.annotations.RequiresCarApi(2) public android.content.ComponentName? getCallingComponent();
+ method public int getCarAppApiLevel();
+ method public <T> T getCarService(Class<T!>);
+ method public Object getCarService(String);
+ method public String getCarServiceName(Class<?>);
+ method public androidx.car.app.HostInfo? getHostInfo();
+ method public androidx.activity.OnBackPressedDispatcher getOnBackPressedDispatcher();
+ method public boolean isDarkMode();
+ method public void requestPermissions(java.util.List<java.lang.String!>, androidx.car.app.OnRequestPermissionsListener);
+ method public void requestPermissions(java.util.List<java.lang.String!>, java.util.concurrent.Executor, androidx.car.app.OnRequestPermissionsListener);
+ method @androidx.car.app.annotations.RequiresCarApi(2) public void setCarAppResult(int, android.content.Intent?);
+ method public void startCarApp(android.content.Intent);
+ method @Deprecated public static void startCarApp(android.content.Intent, android.content.Intent);
+ field public static final String ACTION_NAVIGATE = "androidx.car.app.action.NAVIGATE";
+ field public static final String APP_SERVICE = "app";
+ field public static final String CAR_SERVICE = "car";
+ field @androidx.car.app.annotations.RequiresCarApi(2) public static final String CONSTRAINT_SERVICE = "constraints";
+ field public static final String EXTRA_START_CAR_APP_BINDER_KEY = "androidx.car.app.extra.START_CAR_APP_BINDER_KEY";
+ field @androidx.car.app.annotations.RequiresCarApi(3) public static final String HARDWARE_SERVICE = "hardware";
+ field public static final String NAVIGATION_SERVICE = "navigation";
+ field public static final String SCREEN_SERVICE = "screen";
+ field public static final String SUGGESTION_SERVICE = "suggestion";
+ }
+
+ public final class CarToast {
+ method public static androidx.car.app.CarToast makeText(androidx.car.app.CarContext, @StringRes int, int);
+ method public static androidx.car.app.CarToast makeText(androidx.car.app.CarContext, CharSequence, int);
+ method public void setDuration(int);
+ method public void setText(@StringRes int);
+ method public void setText(CharSequence);
+ method public void show();
+ field public static final int LENGTH_LONG = 1; // 0x1
+ field public static final int LENGTH_SHORT = 0; // 0x0
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class FailureResponse {
+ ctor public FailureResponse(Throwable);
+ method public int getErrorType();
+ method public String getStackTrace();
+ field public static final int BUNDLER_EXCEPTION = 1; // 0x1
+ field public static final int ILLEGAL_STATE_EXCEPTION = 2; // 0x2
+ field public static final int INVALID_PARAMETER_EXCEPTION = 3; // 0x3
+ field public static final int REMOTE_EXCEPTION = 6; // 0x6
+ field public static final int RUNTIME_EXCEPTION = 5; // 0x5
+ field public static final int SECURITY_EXCEPTION = 4; // 0x4
+ field public static final int UNKNOWN_ERROR = 0; // 0x0
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class HandshakeInfo {
+ ctor public HandshakeInfo(String, int);
+ method public int getHostCarAppApiLevel();
+ method public String getHostPackageName();
+ }
+
+ public final class HostException extends java.lang.RuntimeException {
+ ctor public HostException(String);
+ ctor public HostException(String, Throwable);
+ ctor public HostException(Throwable);
+ }
+
+ public final class HostInfo {
+ ctor public HostInfo(String, int);
+ method public String getPackageName();
+ method public int getUid();
+ }
+
+ @androidx.car.app.annotations.CarProtocol public interface OnDoneCallback {
+ method public default void onFailure(androidx.car.app.serialization.Bundleable);
+ method public default void onSuccess(androidx.car.app.serialization.Bundleable?);
+ }
+
+ public interface OnRequestPermissionsListener {
+ method public void onRequestPermissionsResult(java.util.List<java.lang.String!>, java.util.List<java.lang.String!>);
+ }
+
+ public interface OnScreenResultListener {
+ method public void onScreenResult(Object?);
+ }
+
+ public abstract class Screen implements androidx.lifecycle.LifecycleOwner {
+ ctor protected Screen(androidx.car.app.CarContext);
+ method public final void finish();
+ method public final androidx.car.app.CarContext getCarContext();
+ method public final androidx.lifecycle.Lifecycle getLifecycle();
+ method public String? getMarker();
+ method public final androidx.car.app.ScreenManager getScreenManager();
+ method public final void invalidate();
+ method public abstract androidx.car.app.model.Template onGetTemplate();
+ method public void setMarker(String?);
+ method public void setResult(Object?);
+ }
+
+ @MainThread public class ScreenManager implements androidx.car.app.managers.Manager {
+ method public java.util.Collection<androidx.car.app.Screen!> getScreenStack();
+ method public int getStackSize();
+ method public androidx.car.app.Screen getTop();
+ method public void pop();
+ method public void popTo(String);
+ method public void popToRoot();
+ method public void push(androidx.car.app.Screen);
+ method public void pushForResult(androidx.car.app.Screen, androidx.car.app.OnScreenResultListener);
+ method public void remove(androidx.car.app.Screen);
+ }
+
+ public abstract class Session implements androidx.lifecycle.LifecycleOwner {
+ ctor public Session();
+ method public final androidx.car.app.CarContext getCarContext();
+ method public androidx.lifecycle.Lifecycle getLifecycle();
+ method public void onCarConfigurationChanged(android.content.res.Configuration);
+ method public abstract androidx.car.app.Screen onCreateScreen(android.content.Intent);
+ method public void onNewIntent(android.content.Intent);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public class SessionInfo {
+ ctor public SessionInfo(int, String);
+ method public int getDisplayType();
+ method public String getSessionId();
+ method public java.util.Set<java.lang.Class<? extends androidx.car.app.model.Template>!>? getSupportedTemplates(int);
+ field public static final androidx.car.app.SessionInfo DEFAULT_SESSION_INFO;
+ field public static final int DISPLAY_TYPE_CLUSTER = 1; // 0x1
+ field public static final int DISPLAY_TYPE_MAIN = 0; // 0x0
+ }
+
+ public class SessionInfoIntentEncoder {
+ method public static boolean containsSessionInfo(android.content.Intent);
+ method public static androidx.car.app.SessionInfo decode(android.content.Intent);
+ method public static void encode(androidx.car.app.SessionInfo, android.content.Intent);
+ }
+
+ public interface SurfaceCallback {
+ method @androidx.car.app.annotations.RequiresCarApi(5) public default void onClick(float, float);
+ method @androidx.car.app.annotations.RequiresCarApi(2) public default void onFling(float, float);
+ method @androidx.car.app.annotations.RequiresCarApi(2) public default void onScale(float, float, float);
+ method @androidx.car.app.annotations.RequiresCarApi(2) public default void onScroll(float, float);
+ method public default void onStableAreaChanged(android.graphics.Rect);
+ method public default void onSurfaceAvailable(androidx.car.app.SurfaceContainer);
+ method public default void onSurfaceDestroyed(androidx.car.app.SurfaceContainer);
+ method public default void onVisibleAreaChanged(android.graphics.Rect);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class SurfaceContainer {
+ ctor public SurfaceContainer(android.view.Surface?, int, int, int);
+ method public int getDpi();
+ method public int getHeight();
+ method public android.view.Surface? getSurface();
+ method public int getWidth();
+ }
+
+}
+
+package androidx.car.app.annotations {
+
+ @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.PARAMETER}) public @interface CarProtocol {
+ }
+
+ @SuppressCompatibility @RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD}) public @interface ExperimentalCarApi {
+ }
+
+ @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD}) public @interface RequiresCarApi {
+ method public abstract int value();
+ }
+
+}
+
+package androidx.car.app.connection {
+
+ public final class CarConnection {
+ ctor @MainThread public CarConnection(android.content.Context);
+ method public androidx.lifecycle.LiveData<java.lang.Integer!> getType();
+ field public static final String ACTION_CAR_CONNECTION_UPDATED = "androidx.car.app.connection.action.CAR_CONNECTION_UPDATED";
+ field public static final String CAR_CONNECTION_STATE = "CarConnectionState";
+ field public static final int CONNECTION_TYPE_NATIVE = 1; // 0x1
+ field public static final int CONNECTION_TYPE_NOT_CONNECTED = 0; // 0x0
+ field public static final int CONNECTION_TYPE_PROJECTION = 2; // 0x2
+ }
+
+}
+
+package androidx.car.app.constraints {
+
+ @androidx.car.app.annotations.RequiresCarApi(2) public class ConstraintManager implements androidx.car.app.managers.Manager {
+ method public int getContentLimit(int);
+ method @androidx.car.app.annotations.RequiresCarApi(6) public boolean isAppDrivenRefreshEnabled();
+ field public static final int CONTENT_LIMIT_TYPE_GRID = 1; // 0x1
+ field public static final int CONTENT_LIMIT_TYPE_LIST = 0; // 0x0
+ field public static final int CONTENT_LIMIT_TYPE_PANE = 4; // 0x4
+ field public static final int CONTENT_LIMIT_TYPE_PLACE_LIST = 2; // 0x2
+ field public static final int CONTENT_LIMIT_TYPE_ROUTE_LIST = 3; // 0x3
+ }
+
+}
+
+package androidx.car.app.hardware {
+
+ @MainThread @androidx.car.app.annotations.RequiresCarApi(3) public interface CarHardwareManager extends androidx.car.app.managers.Manager {
+ method @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public default androidx.car.app.hardware.climate.CarClimate getCarClimate();
+ method public default androidx.car.app.hardware.info.CarInfo getCarInfo();
+ method public default androidx.car.app.hardware.info.CarSensors getCarSensors();
+ }
+
+}
+
+package androidx.car.app.hardware.climate {
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class CabinTemperatureProfile {
+ method public java.util.Map<java.util.Set<androidx.car.app.hardware.common.CarZone!>!,android.util.Pair<java.lang.Float!,java.lang.Float!>!> getCarZoneSetsToCabinCelsiusTemperatureRanges();
+ method public float getCelsiusSupportedIncrement();
+ method public float getFahrenheitSupportedIncrement();
+ method public android.util.Pair<java.lang.Float!,java.lang.Float!> getSupportedMinMaxCelsiusRange();
+ method public android.util.Pair<java.lang.Float!,java.lang.Float!> getSupportedMinMaxFahrenheitRange();
+ method public boolean hasCarZoneSetsToCabinCelsiusTemperatureRanges();
+ method public boolean hasCelsiusSupportedIncrement();
+ method public boolean hasFahrenheitSupportedIncrement();
+ method public boolean hasSupportedMinMaxCelsiusRange();
+ method public boolean hasSupportedMinMaxFahrenheitRange();
+ }
+
+ public static final class CabinTemperatureProfile.Builder {
+ ctor public CabinTemperatureProfile.Builder();
+ method public androidx.car.app.hardware.climate.CabinTemperatureProfile build();
+ method public androidx.car.app.hardware.climate.CabinTemperatureProfile.Builder setCarZoneSetsToCabinCelsiusTemperatureRanges(java.util.Map<java.util.Set<androidx.car.app.hardware.common.CarZone!>!,android.util.Pair<java.lang.Float!,java.lang.Float!>!>);
+ method public androidx.car.app.hardware.climate.CabinTemperatureProfile.Builder setCelsiusSupportedIncrement(float);
+ method public androidx.car.app.hardware.climate.CabinTemperatureProfile.Builder setFahrenheitSupportedIncrement(float);
+ method public androidx.car.app.hardware.climate.CabinTemperatureProfile.Builder setSupportedMinMaxCelsiusRange(android.util.Pair<java.lang.Float!,java.lang.Float!>);
+ method public androidx.car.app.hardware.climate.CabinTemperatureProfile.Builder setSupportedMinMaxFahrenheitRange(android.util.Pair<java.lang.Float!,java.lang.Float!>);
+ }
+
+ @SuppressCompatibility @MainThread @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(5) public interface CarClimate {
+ method public void fetchClimateProfile(java.util.concurrent.Executor, androidx.car.app.hardware.climate.ClimateProfileRequest, androidx.car.app.hardware.climate.CarClimateProfileCallback);
+ method public void registerClimateStateCallback(java.util.concurrent.Executor, androidx.car.app.hardware.climate.RegisterClimateStateRequest, androidx.car.app.hardware.climate.CarClimateStateCallback);
+ method public <E> void setClimateState(java.util.concurrent.Executor, androidx.car.app.hardware.climate.ClimateStateRequest<E!>, androidx.car.app.hardware.common.CarSetOperationStatusCallback);
+ method public void unregisterClimateStateCallback(androidx.car.app.hardware.climate.CarClimateStateCallback);
+ }
+
+ @SuppressCompatibility @MainThread @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(5) public final class CarClimateFeature {
+ method public java.util.List<androidx.car.app.hardware.common.CarZone!> getCarZones();
+ method public int getFeature();
+ }
+
+ public static final class CarClimateFeature.Builder {
+ ctor public CarClimateFeature.Builder(int);
+ method public androidx.car.app.hardware.climate.CarClimateFeature.Builder addCarZones(androidx.car.app.hardware.common.CarZone!...);
+ method public androidx.car.app.hardware.climate.CarClimateFeature build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(5) public interface CarClimateProfileCallback {
+ method public default void onCabinTemperatureProfileAvailable(androidx.car.app.hardware.climate.CabinTemperatureProfile);
+ method public default void onCarZoneMappingInfoProfileAvailable(androidx.car.app.hardware.climate.CarZoneMappingInfoProfile);
+ method public default void onDefrosterProfileAvailable(androidx.car.app.hardware.climate.DefrosterProfile);
+ method public default void onElectricDefrosterProfileAvailable(androidx.car.app.hardware.climate.ElectricDefrosterProfile);
+ method public default void onFanDirectionProfileAvailable(androidx.car.app.hardware.climate.FanDirectionProfile);
+ method public default void onFanSpeedLevelProfileAvailable(androidx.car.app.hardware.climate.FanSpeedLevelProfile);
+ method public default void onHvacAcProfileAvailable(androidx.car.app.hardware.climate.HvacAcProfile);
+ method public default void onHvacAutoModeProfileAvailable(androidx.car.app.hardware.climate.HvacAutoModeProfile);
+ method public default void onHvacAutoRecirculationProfileAvailable(androidx.car.app.hardware.climate.HvacAutoRecirculationProfile);
+ method public default void onHvacDualModeProfileAvailable(androidx.car.app.hardware.climate.HvacDualModeProfile);
+ method public default void onHvacMaxAcModeProfileAvailable(androidx.car.app.hardware.climate.HvacMaxAcModeProfile);
+ method public default void onHvacPowerProfileAvailable(androidx.car.app.hardware.climate.HvacPowerProfile);
+ method public default void onHvacRecirculationProfileAvailable(androidx.car.app.hardware.climate.HvacRecirculationProfile);
+ method public default void onMaxDefrosterProfileAvailable(androidx.car.app.hardware.climate.MaxDefrosterProfile);
+ method public default void onSeatTemperatureLevelProfileAvailable(androidx.car.app.hardware.climate.SeatTemperatureProfile);
+ method public default void onSeatVentilationLevelProfileAvailable(androidx.car.app.hardware.climate.SeatVentilationProfile);
+ method public default void onSteeringWheelHeatProfileAvailable(androidx.car.app.hardware.climate.SteeringWheelHeatProfile);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(5) public interface CarClimateStateCallback {
+ method public default void onCabinTemperatureStateAvailable(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+ method public default void onDefrosterStateAvailable(androidx.car.app.hardware.common.CarValue<java.lang.Boolean!>);
+ method public default void onElectricDefrosterStateAvailable(androidx.car.app.hardware.common.CarValue<java.lang.Boolean!>);
+ method public default void onFanDirectionStateAvailable(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+ method public default void onFanSpeedLevelStateAvailable(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+ method public default void onHvacAcStateAvailable(androidx.car.app.hardware.common.CarValue<java.lang.Boolean!>);
+ method public default void onHvacAutoModeStateAvailable(androidx.car.app.hardware.common.CarValue<java.lang.Boolean!>);
+ method public default void onHvacAutoRecirculationStateAvailable(androidx.car.app.hardware.common.CarValue<java.lang.Boolean!>);
+ method public default void onHvacDualModeStateAvailable(androidx.car.app.hardware.common.CarValue<java.lang.Boolean!>);
+ method public default void onHvacMaxAcModeStateAvailable(androidx.car.app.hardware.common.CarValue<java.lang.Boolean!>);
+ method public default void onHvacPowerStateAvailable(androidx.car.app.hardware.common.CarValue<java.lang.Boolean!>);
+ method public default void onHvacRecirculationStateAvailable(androidx.car.app.hardware.common.CarValue<java.lang.Boolean!>);
+ method public default void onMaxDefrosterStateAvailable(androidx.car.app.hardware.common.CarValue<java.lang.Boolean!>);
+ method public default void onSeatTemperatureLevelStateAvailable(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+ method public default void onSeatVentilationLevelStateAvailable(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+ method public default void onSteeringWheelHeatStateAvailable(androidx.car.app.hardware.common.CarValue<java.lang.Boolean!>);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class CarZoneMappingInfoProfile {
+ method public java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!> getSupportedCarZoneSets();
+ }
+
+ public static final class CarZoneMappingInfoProfile.Builder {
+ ctor public CarZoneMappingInfoProfile.Builder(java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!>);
+ method public androidx.car.app.hardware.climate.CarZoneMappingInfoProfile build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(5) public final class ClimateProfileRequest {
+ method public java.util.Set<java.lang.Integer!> getAllClimateProfiles();
+ method public java.util.List<androidx.car.app.hardware.climate.CarClimateFeature!> getClimateProfileFeatures();
+ field public static final int FEATURE_CABIN_TEMPERATURE = 4; // 0x4
+ field public static final int FEATURE_CAR_ZONE_MAPPING = 17; // 0x11
+ field public static final int FEATURE_FAN_DIRECTION = 6; // 0x6
+ field public static final int FEATURE_FAN_SPEED = 5; // 0x5
+ field public static final int FEATURE_HVAC_AC = 2; // 0x2
+ field public static final int FEATURE_HVAC_AUTO_MODE = 12; // 0xc
+ field public static final int FEATURE_HVAC_AUTO_RECIRCULATION = 11; // 0xb
+ field public static final int FEATURE_HVAC_DEFROSTER = 14; // 0xe
+ field public static final int FEATURE_HVAC_DUAL_MODE = 13; // 0xd
+ field public static final int FEATURE_HVAC_ELECTRIC_DEFROSTER = 16; // 0x10
+ field public static final int FEATURE_HVAC_MAX_AC = 3; // 0x3
+ field public static final int FEATURE_HVAC_MAX_DEFROSTER = 15; // 0xf
+ field public static final int FEATURE_HVAC_POWER = 1; // 0x1
+ field public static final int FEATURE_HVAC_RECIRCULATION = 10; // 0xa
+ field public static final int FEATURE_SEAT_TEMPERATURE_LEVEL = 7; // 0x7
+ field public static final int FEATURE_SEAT_VENTILATION_LEVEL = 8; // 0x8
+ field public static final int FEATURE_STEERING_WHEEL_HEAT = 9; // 0x9
+ }
+
+ public static final class ClimateProfileRequest.Builder {
+ ctor public ClimateProfileRequest.Builder();
+ method public androidx.car.app.hardware.climate.ClimateProfileRequest.Builder addClimateProfileFeatures(androidx.car.app.hardware.climate.CarClimateFeature!...);
+ method public androidx.car.app.hardware.climate.ClimateProfileRequest build();
+ method public androidx.car.app.hardware.climate.ClimateProfileRequest.Builder setAllClimateProfiles();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(5) public final class ClimateStateRequest<T> {
+ method public java.util.List<androidx.car.app.hardware.common.CarZone!> getCarZones();
+ method public int getRequestedFeature();
+ method public T getRequestedValue();
+ }
+
+ public static final class ClimateStateRequest.Builder<T> {
+ ctor public ClimateStateRequest.Builder(int, T!);
+ method public androidx.car.app.hardware.climate.ClimateStateRequest.Builder<T!> addCarZones(androidx.car.app.hardware.common.CarZone);
+ method public androidx.car.app.hardware.climate.ClimateStateRequest<T!> build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class DefrosterProfile {
+ method public java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!> getSupportedCarZoneSets();
+ }
+
+ public static final class DefrosterProfile.Builder {
+ ctor public DefrosterProfile.Builder(java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!>);
+ method public androidx.car.app.hardware.climate.DefrosterProfile build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class ElectricDefrosterProfile {
+ method public java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!> getSupportedCarZoneSets();
+ }
+
+ public static final class ElectricDefrosterProfile.Builder {
+ ctor public ElectricDefrosterProfile.Builder(java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!>);
+ method public androidx.car.app.hardware.climate.ElectricDefrosterProfile build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class FanDirectionProfile {
+ method public java.util.Map<java.util.Set<androidx.car.app.hardware.common.CarZone!>!,java.util.Set<java.lang.Integer!>!> getCarZoneSetsToFanDirectionValues();
+ }
+
+ public static final class FanDirectionProfile.Builder {
+ ctor public FanDirectionProfile.Builder(java.util.Map<java.util.Set<androidx.car.app.hardware.common.CarZone!>!,java.util.Set<java.lang.Integer!>!>);
+ method public androidx.car.app.hardware.climate.FanDirectionProfile build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class FanSpeedLevelProfile {
+ method public java.util.Map<java.util.Set<androidx.car.app.hardware.common.CarZone!>!,android.util.Pair<java.lang.Integer!,java.lang.Integer!>!> getCarZoneSetsToFanSpeedLevelRanges();
+ }
+
+ public static final class FanSpeedLevelProfile.Builder {
+ ctor public FanSpeedLevelProfile.Builder(java.util.Map<java.util.Set<androidx.car.app.hardware.common.CarZone!>!,android.util.Pair<java.lang.Integer!,java.lang.Integer!>!>);
+ method public androidx.car.app.hardware.climate.FanSpeedLevelProfile build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class HvacAcProfile {
+ method public java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!> getSupportedCarZoneSets();
+ }
+
+ public static final class HvacAcProfile.Builder {
+ ctor public HvacAcProfile.Builder(java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!>);
+ method public androidx.car.app.hardware.climate.HvacAcProfile build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class HvacAutoModeProfile {
+ method public java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!> getSupportedCarZoneSets();
+ }
+
+ public static final class HvacAutoModeProfile.Builder {
+ ctor public HvacAutoModeProfile.Builder(java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!>);
+ method public androidx.car.app.hardware.climate.HvacAutoModeProfile build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class HvacAutoRecirculationProfile {
+ method public java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!> getSupportedCarZoneSets();
+ }
+
+ public static final class HvacAutoRecirculationProfile.Builder {
+ ctor public HvacAutoRecirculationProfile.Builder(java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!>);
+ method public androidx.car.app.hardware.climate.HvacAutoRecirculationProfile build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class HvacDualModeProfile {
+ method public java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!> getSupportedCarZoneSets();
+ }
+
+ public static final class HvacDualModeProfile.Builder {
+ ctor public HvacDualModeProfile.Builder(java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!>);
+ method public androidx.car.app.hardware.climate.HvacDualModeProfile build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class HvacMaxAcModeProfile {
+ method public java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!> getSupportedCarZoneSets();
+ }
+
+ public static final class HvacMaxAcModeProfile.Builder {
+ ctor public HvacMaxAcModeProfile.Builder(java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!>);
+ method public androidx.car.app.hardware.climate.HvacMaxAcModeProfile build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class HvacPowerProfile {
+ method public java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!> getSupportedCarZoneSets();
+ }
+
+ public static final class HvacPowerProfile.Builder {
+ ctor public HvacPowerProfile.Builder(java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!>);
+ method public androidx.car.app.hardware.climate.HvacPowerProfile build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class HvacRecirculationProfile {
+ method public java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!> getSupportedCarZones();
+ }
+
+ public static final class HvacRecirculationProfile.Builder {
+ ctor public HvacRecirculationProfile.Builder(java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!>);
+ method public androidx.car.app.hardware.climate.HvacRecirculationProfile build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class MaxDefrosterProfile {
+ method public java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!> getSupportedCarZoneSets();
+ }
+
+ public static final class MaxDefrosterProfile.Builder {
+ ctor public MaxDefrosterProfile.Builder(java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!>);
+ method public androidx.car.app.hardware.climate.MaxDefrosterProfile build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(5) public final class RegisterClimateStateRequest {
+ method public java.util.List<androidx.car.app.hardware.climate.CarClimateFeature!> getClimateRegisterFeatures();
+ }
+
+ public static final class RegisterClimateStateRequest.Builder {
+ ctor public RegisterClimateStateRequest.Builder(boolean);
+ method public androidx.car.app.hardware.climate.RegisterClimateStateRequest.Builder addClimateRegisterFeatures(androidx.car.app.hardware.climate.CarClimateFeature!...);
+ method public androidx.car.app.hardware.climate.RegisterClimateStateRequest build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class SeatTemperatureProfile {
+ method public java.util.Map<java.util.Set<androidx.car.app.hardware.common.CarZone!>!,android.util.Pair<java.lang.Integer!,java.lang.Integer!>!> getCarZoneSetsToSeatTemperatureValues();
+ }
+
+ public static final class SeatTemperatureProfile.Builder {
+ ctor public SeatTemperatureProfile.Builder(java.util.Map<java.util.Set<androidx.car.app.hardware.common.CarZone!>!,android.util.Pair<java.lang.Integer!,java.lang.Integer!>!>);
+ method public androidx.car.app.hardware.climate.SeatTemperatureProfile build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class SeatVentilationProfile {
+ method public java.util.Map<java.util.Set<androidx.car.app.hardware.common.CarZone!>!,android.util.Pair<java.lang.Integer!,java.lang.Integer!>!> getCarZoneSetsToSeatVentilationValues();
+ }
+
+ public static final class SeatVentilationProfile.Builder {
+ ctor public SeatVentilationProfile.Builder(java.util.Map<java.util.Set<androidx.car.app.hardware.common.CarZone!>!,android.util.Pair<java.lang.Integer!,java.lang.Integer!>!>);
+ method public androidx.car.app.hardware.climate.SeatVentilationProfile build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class SteeringWheelHeatProfile {
+ method public java.util.Map<java.util.Set<androidx.car.app.hardware.common.CarZone!>!,android.util.Pair<java.lang.Integer!,java.lang.Integer!>!> getCarZoneSetsToSteeringWheelHeatValues();
+ }
+
+ public static final class SteeringWheelHeatProfile.Builder {
+ ctor public SteeringWheelHeatProfile.Builder(java.util.Map<java.util.Set<androidx.car.app.hardware.common.CarZone!>!,android.util.Pair<java.lang.Integer!,java.lang.Integer!>!>);
+ method public androidx.car.app.hardware.climate.SteeringWheelHeatProfile build();
+ }
+
+}
+
+package androidx.car.app.hardware.common {
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(5) public interface CarSetOperationStatusCallback {
+ method public default void onSetCarClimateStateCabinTemperature(int);
+ method public default void onSetCarClimateStateDefroster(int);
+ method public default void onSetCarClimateStateElectricDefroster(int);
+ method public default void onSetCarClimateStateFanDirection(int);
+ method public default void onSetCarClimateStateFanSpeedLevel(int);
+ method public default void onSetCarClimateStateHvacAc(int);
+ method public default void onSetCarClimateStateHvacAutoMode(int);
+ method public default void onSetCarClimateStateHvacAutoRecirculation(int);
+ method public default void onSetCarClimateStateHvacDualMode(int);
+ method public default void onSetCarClimateStateHvacMaxAcMode(int);
+ method public default void onSetCarClimateStateHvacPower(int);
+ method public default void onSetCarClimateStateHvacRecirculation(int);
+ method public default void onSetCarClimateStateMaxDefroster(int);
+ method public default void onSetCarClimateStateSeatTemperatureLevel(int);
+ method public default void onSetCarClimateStateSeatVentilationLevel(int);
+ method public default void onSetCarClimateStateSteeringWheelHeat(int);
+ method public static String toString(int);
+ field public static final int OPERATION_STATUS_FEATURE_SETTING_NOT_ALLOWED = 4; // 0x4
+ field public static final int OPERATION_STATUS_FEATURE_TEMPORARILY_UNAVAILABLE = 3; // 0x3
+ field public static final int OPERATION_STATUS_FEATURE_UNIMPLEMENTED = 1; // 0x1
+ field public static final int OPERATION_STATUS_FEATURE_UNSUPPORTED = 2; // 0x2
+ field public static final int OPERATION_STATUS_ILLEGAL_CAR_HARDWARE_STATE = 7; // 0x7
+ field public static final int OPERATION_STATUS_INSUFFICIENT_PERMISSION = 6; // 0x6
+ field public static final int OPERATION_STATUS_SUCCESS = 0; // 0x0
+ field public static final int OPERATION_STATUS_UNSUPPORTED_VALUE = 5; // 0x5
+ field public static final int OPERATION_STATUS_UPDATE_TIMEOUT = 8; // 0x8
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class CarUnit {
+ method public static String toString(int);
+ field @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public static final int IMPERIAL_GALLON = 204; // 0xcc
+ field public static final int KILOMETER = 3; // 0x3
+ field public static final int KILOMETERS_PER_HOUR = 102; // 0x66
+ field @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public static final int LITER = 202; // 0xca
+ field public static final int METER = 2; // 0x2
+ field public static final int METERS_PER_SEC = 101; // 0x65
+ field public static final int MILE = 4; // 0x4
+ field public static final int MILES_PER_HOUR = 103; // 0x67
+ field @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public static final int MILLILITER = 201; // 0xc9
+ field public static final int MILLIMETER = 1; // 0x1
+ field @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public static final int US_GALLON = 203; // 0xcb
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class CarValue<T> {
+ ctor public CarValue(T?, long, int);
+ ctor @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public CarValue(T?, long, int, java.util.List<androidx.car.app.hardware.common.CarZone!>);
+ method @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public java.util.List<androidx.car.app.hardware.common.CarZone!> getCarZones();
+ method public int getStatus();
+ method public long getTimestampMillis();
+ method public T? getValue();
+ field public static final int STATUS_SUCCESS = 1; // 0x1
+ field public static final int STATUS_UNAVAILABLE = 3; // 0x3
+ field public static final int STATUS_UNIMPLEMENTED = 2; // 0x2
+ field public static final int STATUS_UNKNOWN = 0; // 0x0
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(5) public final class CarZone {
+ method public int getColumn();
+ method public int getRow();
+ field public static final int CAR_ZONE_COLUMN_ALL = 16; // 0x10
+ field public static final int CAR_ZONE_COLUMN_CENTER = 48; // 0x30
+ field public static final int CAR_ZONE_COLUMN_DRIVER = 80; // 0x50
+ field public static final int CAR_ZONE_COLUMN_LEFT = 32; // 0x20
+ field public static final int CAR_ZONE_COLUMN_PASSENGER = 96; // 0x60
+ field public static final int CAR_ZONE_COLUMN_RIGHT = 64; // 0x40
+ field public static final androidx.car.app.hardware.common.CarZone CAR_ZONE_GLOBAL;
+ field public static final int CAR_ZONE_ROW_ALL = 0; // 0x0
+ field public static final int CAR_ZONE_ROW_EXCLUDE_FIRST = 4; // 0x4
+ field public static final int CAR_ZONE_ROW_FIRST = 1; // 0x1
+ field public static final int CAR_ZONE_ROW_SECOND = 2; // 0x2
+ field public static final int CAR_ZONE_ROW_THIRD = 3; // 0x3
+ }
+
+ public static final class CarZone.Builder {
+ ctor public CarZone.Builder();
+ method public androidx.car.app.hardware.common.CarZone build();
+ method public androidx.car.app.hardware.common.CarZone.Builder setColumn(int);
+ method public androidx.car.app.hardware.common.CarZone.Builder setRow(int);
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public interface OnCarDataAvailableListener<T> {
+ method public void onCarDataAvailable(T);
+ }
+
+}
+
+package androidx.car.app.hardware.info {
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Accelerometer {
+ ctor public Accelerometer(androidx.car.app.hardware.common.CarValue<java.util.List<java.lang.Float!>!>);
+ method public androidx.car.app.hardware.common.CarValue<java.util.List<java.lang.Float!>!> getForces();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class CarHardwareLocation {
+ ctor public CarHardwareLocation(androidx.car.app.hardware.common.CarValue<android.location.Location!>);
+ method public androidx.car.app.hardware.common.CarValue<android.location.Location!> getLocation();
+ }
+
+ @MainThread @androidx.car.app.annotations.RequiresCarApi(3) public interface CarInfo {
+ method public void addEnergyLevelListener(java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataAvailableListener<androidx.car.app.hardware.info.EnergyLevel!>);
+ method @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public void addEvStatusListener(java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataAvailableListener<androidx.car.app.hardware.info.EvStatus!>);
+ method public void addMileageListener(java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataAvailableListener<androidx.car.app.hardware.info.Mileage!>);
+ method public void addSpeedListener(java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataAvailableListener<androidx.car.app.hardware.info.Speed!>);
+ method public void addTollListener(java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataAvailableListener<androidx.car.app.hardware.info.TollCard!>);
+ method public void fetchEnergyProfile(java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataAvailableListener<androidx.car.app.hardware.info.EnergyProfile!>);
+ method public void fetchModel(java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataAvailableListener<androidx.car.app.hardware.info.Model!>);
+ method public void removeEnergyLevelListener(androidx.car.app.hardware.common.OnCarDataAvailableListener<androidx.car.app.hardware.info.EnergyLevel!>);
+ method @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public void removeEvStatusListener(androidx.car.app.hardware.common.OnCarDataAvailableListener<androidx.car.app.hardware.info.EvStatus!>);
+ method public void removeMileageListener(androidx.car.app.hardware.common.OnCarDataAvailableListener<androidx.car.app.hardware.info.Mileage!>);
+ method public void removeSpeedListener(androidx.car.app.hardware.common.OnCarDataAvailableListener<androidx.car.app.hardware.info.Speed!>);
+ method public void removeTollListener(androidx.car.app.hardware.common.OnCarDataAvailableListener<androidx.car.app.hardware.info.TollCard!>);
+ }
+
+ @MainThread @androidx.car.app.annotations.RequiresCarApi(3) public interface CarSensors {
+ method public void addAccelerometerListener(int, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataAvailableListener<androidx.car.app.hardware.info.Accelerometer!>);
+ method public void addCarHardwareLocationListener(int, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataAvailableListener<androidx.car.app.hardware.info.CarHardwareLocation!>);
+ method public void addCompassListener(int, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataAvailableListener<androidx.car.app.hardware.info.Compass!>);
+ method public void addGyroscopeListener(int, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataAvailableListener<androidx.car.app.hardware.info.Gyroscope!>);
+ method public void removeAccelerometerListener(androidx.car.app.hardware.common.OnCarDataAvailableListener<androidx.car.app.hardware.info.Accelerometer!>);
+ method public void removeCarHardwareLocationListener(androidx.car.app.hardware.common.OnCarDataAvailableListener<androidx.car.app.hardware.info.CarHardwareLocation!>);
+ method public void removeCompassListener(androidx.car.app.hardware.common.OnCarDataAvailableListener<androidx.car.app.hardware.info.Compass!>);
+ method public void removeGyroscopeListener(androidx.car.app.hardware.common.OnCarDataAvailableListener<androidx.car.app.hardware.info.Gyroscope!>);
+ field public static final int UPDATE_RATE_FASTEST = 3; // 0x3
+ field public static final int UPDATE_RATE_NORMAL = 1; // 0x1
+ field public static final int UPDATE_RATE_UI = 2; // 0x2
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Compass {
+ ctor public Compass(androidx.car.app.hardware.common.CarValue<java.util.List<java.lang.Float!>!>);
+ method public androidx.car.app.hardware.common.CarValue<java.util.List<java.lang.Float!>!> getOrientations();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class EnergyLevel {
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getBatteryPercent();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Integer!> getDistanceDisplayUnit();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Boolean!> getEnergyIsLow();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getFuelPercent();
+ method @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public androidx.car.app.hardware.common.CarValue<java.lang.Integer!> getFuelVolumeDisplayUnit();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getRangeRemainingMeters();
+ }
+
+ public static final class EnergyLevel.Builder {
+ ctor public EnergyLevel.Builder();
+ method public androidx.car.app.hardware.info.EnergyLevel build();
+ method public androidx.car.app.hardware.info.EnergyLevel.Builder setBatteryPercent(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+ method public androidx.car.app.hardware.info.EnergyLevel.Builder setDistanceDisplayUnit(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+ method public androidx.car.app.hardware.info.EnergyLevel.Builder setEnergyIsLow(androidx.car.app.hardware.common.CarValue<java.lang.Boolean!>);
+ method public androidx.car.app.hardware.info.EnergyLevel.Builder setFuelPercent(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+ method @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public androidx.car.app.hardware.info.EnergyLevel.Builder setFuelVolumeDisplayUnit(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+ method public androidx.car.app.hardware.info.EnergyLevel.Builder setRangeRemainingMeters(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class EnergyProfile {
+ method public androidx.car.app.hardware.common.CarValue<java.util.List<java.lang.Integer!>!> getEvConnectorTypes();
+ method public androidx.car.app.hardware.common.CarValue<java.util.List<java.lang.Integer!>!> getFuelTypes();
+ field public static final int EVCONNECTOR_TYPE_CHADEMO = 3; // 0x3
+ field public static final int EVCONNECTOR_TYPE_COMBO_1 = 4; // 0x4
+ field public static final int EVCONNECTOR_TYPE_COMBO_2 = 5; // 0x5
+ field public static final int EVCONNECTOR_TYPE_GBT = 9; // 0x9
+ field public static final int EVCONNECTOR_TYPE_GBT_DC = 10; // 0xa
+ field public static final int EVCONNECTOR_TYPE_J1772 = 1; // 0x1
+ field public static final int EVCONNECTOR_TYPE_MENNEKES = 2; // 0x2
+ field public static final int EVCONNECTOR_TYPE_OTHER = 101; // 0x65
+ field public static final int EVCONNECTOR_TYPE_SCAME = 11; // 0xb
+ field public static final int EVCONNECTOR_TYPE_TESLA_HPWC = 7; // 0x7
+ field public static final int EVCONNECTOR_TYPE_TESLA_ROADSTER = 6; // 0x6
+ field public static final int EVCONNECTOR_TYPE_TESLA_SUPERCHARGER = 8; // 0x8
+ field public static final int EVCONNECTOR_TYPE_UNKNOWN = 0; // 0x0
+ field public static final int FUEL_TYPE_BIODIESEL = 5; // 0x5
+ field public static final int FUEL_TYPE_CNG = 8; // 0x8
+ field public static final int FUEL_TYPE_DIESEL_1 = 3; // 0x3
+ field public static final int FUEL_TYPE_DIESEL_2 = 4; // 0x4
+ field public static final int FUEL_TYPE_E85 = 6; // 0x6
+ field public static final int FUEL_TYPE_ELECTRIC = 10; // 0xa
+ field public static final int FUEL_TYPE_HYDROGEN = 11; // 0xb
+ field public static final int FUEL_TYPE_LEADED = 2; // 0x2
+ field public static final int FUEL_TYPE_LNG = 9; // 0x9
+ field public static final int FUEL_TYPE_LPG = 7; // 0x7
+ field public static final int FUEL_TYPE_OTHER = 12; // 0xc
+ field public static final int FUEL_TYPE_UNKNOWN = 0; // 0x0
+ field public static final int FUEL_TYPE_UNLEADED = 1; // 0x1
+ }
+
+ public static final class EnergyProfile.Builder {
+ ctor public EnergyProfile.Builder();
+ method public androidx.car.app.hardware.info.EnergyProfile build();
+ method public androidx.car.app.hardware.info.EnergyProfile.Builder setEvConnectorTypes(androidx.car.app.hardware.common.CarValue<java.util.List<java.lang.Integer!>!>);
+ method public androidx.car.app.hardware.info.EnergyProfile.Builder setFuelTypes(androidx.car.app.hardware.common.CarValue<java.util.List<java.lang.Integer!>!>);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi public class EvStatus {
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Boolean!> getEvChargePortConnected();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Boolean!> getEvChargePortOpen();
+ }
+
+ public static final class EvStatus.Builder {
+ ctor public EvStatus.Builder();
+ method public androidx.car.app.hardware.info.EvStatus build();
+ method public androidx.car.app.hardware.info.EvStatus.Builder setEvChargePortConnected(androidx.car.app.hardware.common.CarValue<java.lang.Boolean!>);
+ method public androidx.car.app.hardware.info.EvStatus.Builder setEvChargePortOpen(androidx.car.app.hardware.common.CarValue<java.lang.Boolean!>);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Gyroscope {
+ ctor public Gyroscope(androidx.car.app.hardware.common.CarValue<java.util.List<java.lang.Float!>!>);
+ method public androidx.car.app.hardware.common.CarValue<java.util.List<java.lang.Float!>!> getRotations();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Mileage {
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Integer!> getDistanceDisplayUnit();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getOdometerMeters();
+ }
+
+ public static final class Mileage.Builder {
+ ctor public Mileage.Builder();
+ method public androidx.car.app.hardware.info.Mileage build();
+ method public androidx.car.app.hardware.info.Mileage.Builder setDistanceDisplayUnit(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+ method public androidx.car.app.hardware.info.Mileage.Builder setOdometerMeters(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Model {
+ method public androidx.car.app.hardware.common.CarValue<java.lang.String!> getManufacturer();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.String!> getName();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Integer!> getYear();
+ }
+
+ public static final class Model.Builder {
+ ctor public Model.Builder();
+ method public androidx.car.app.hardware.info.Model build();
+ method public androidx.car.app.hardware.info.Model.Builder setManufacturer(androidx.car.app.hardware.common.CarValue<java.lang.String!>);
+ method public androidx.car.app.hardware.info.Model.Builder setName(androidx.car.app.hardware.common.CarValue<java.lang.String!>);
+ method public androidx.car.app.hardware.info.Model.Builder setYear(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Speed {
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getDisplaySpeedMetersPerSecond();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getRawSpeedMetersPerSecond();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Integer!> getSpeedDisplayUnit();
+ }
+
+ public static final class Speed.Builder {
+ ctor public Speed.Builder();
+ method public androidx.car.app.hardware.info.Speed build();
+ method public androidx.car.app.hardware.info.Speed.Builder setDisplaySpeedMetersPerSecond(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+ method public androidx.car.app.hardware.info.Speed.Builder setRawSpeedMetersPerSecond(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+ method public androidx.car.app.hardware.info.Speed.Builder setSpeedDisplayUnit(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class TollCard {
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Integer!> getCardState();
+ field public static final int TOLLCARD_STATE_INVALID = 2; // 0x2
+ field public static final int TOLLCARD_STATE_NOT_INSERTED = 3; // 0x3
+ field public static final int TOLLCARD_STATE_UNKNOWN = 0; // 0x0
+ field public static final int TOLLCARD_STATE_VALID = 1; // 0x1
+ }
+
+ public static final class TollCard.Builder {
+ ctor public TollCard.Builder();
+ method public androidx.car.app.hardware.info.TollCard build();
+ method public androidx.car.app.hardware.info.TollCard.Builder setCardState(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+ }
+
+}
+
+package androidx.car.app.managers {
+
+ public interface Manager {
+ }
+
+}
+
+package androidx.car.app.media {
+
+ @androidx.car.app.annotations.RequiresCarApi(5) public interface CarAudioCallback {
+ method public void onStopRecording();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(5) public class CarAudioCallbackDelegate {
+ method public void onStopRecording();
+ }
+
+ @androidx.car.app.annotations.RequiresCarApi(5) public abstract class CarAudioRecord {
+ method @RequiresPermission(android.Manifest.permission.RECORD_AUDIO) public static androidx.car.app.media.CarAudioRecord create(androidx.car.app.CarContext);
+ method public int read(byte[], int, int);
+ method public void startRecording();
+ method public void stopRecording();
+ field public static final int AUDIO_CONTENT_BUFFER_SIZE = 512; // 0x200
+ field public static final String AUDIO_CONTENT_MIME = "audio/l16";
+ field public static final int AUDIO_CONTENT_SAMPLING_RATE = 16000; // 0x3e80
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(5) public final class OpenMicrophoneRequest {
+ method public androidx.car.app.media.CarAudioCallbackDelegate getCarAudioCallbackDelegate();
+ }
+
+ public static final class OpenMicrophoneRequest.Builder {
+ ctor public OpenMicrophoneRequest.Builder(androidx.car.app.media.CarAudioCallback);
+ method public androidx.car.app.media.OpenMicrophoneRequest build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(5) public final class OpenMicrophoneResponse {
+ method public androidx.car.app.media.CarAudioCallbackDelegate getCarAudioCallback();
+ method public java.io.InputStream getCarMicrophoneInputStream();
+ }
+
+ public static final class OpenMicrophoneResponse.Builder {
+ ctor public OpenMicrophoneResponse.Builder(androidx.car.app.media.CarAudioCallback);
+ method public androidx.car.app.media.OpenMicrophoneResponse build();
+ method public androidx.car.app.media.OpenMicrophoneResponse.Builder setCarMicrophoneDescriptor(android.os.ParcelFileDescriptor);
+ }
+
+}
+
+package androidx.car.app.mediaextensions {
+
+ public final class MetadataExtras {
+ field public static final String KEY_CONTENT_FORMAT_DARK_MODE_LARGE_ICON_URI = "androidx.car.app.mediaextensions.KEY_CONTENT_FORMAT_DARK_MODE_LARGE_ICON_URI";
+ field public static final String KEY_CONTENT_FORMAT_DARK_MODE_SMALL_ICON_URI = "androidx.car.app.mediaextensions.KEY_CONTENT_FORMAT_DARK_MODE_SMALL_ICON_URI";
+ field public static final String KEY_CONTENT_FORMAT_LIGHT_MODE_LARGE_ICON_URI = "androidx.car.app.mediaextensions.KEY_CONTENT_FORMAT_LIGHT_MODE_LARGE_ICON_URI";
+ field public static final String KEY_CONTENT_FORMAT_LIGHT_MODE_SMALL_ICON_URI = "androidx.car.app.mediaextensions.KEY_CONTENT_FORMAT_LIGHT_MODE_SMALL_ICON_URI";
+ field public static final String KEY_CONTENT_FORMAT_TINTABLE_LARGE_ICON_URI = "androidx.car.app.mediaextensions.KEY_CONTENT_FORMAT_TINTABLE_LARGE_ICON_URI";
+ field public static final String KEY_CONTENT_FORMAT_TINTABLE_SMALL_ICON_URI = "androidx.car.app.mediaextensions.KEY_CONTENT_FORMAT_TINTABLE_SMALL_ICON_URI";
+ field public static final String KEY_DESCRIPTION_LINK_MEDIA_ID = "androidx.car.app.mediaextensions.KEY_DESCRIPTION_LINK_MEDIA_ID";
+ field public static final String KEY_IMMERSIVE_AUDIO = "androidx.car.app.mediaextensions.KEY_IMMERSIVE_AUDIO";
+ field public static final String KEY_SUBTITLE_LINK_MEDIA_ID = "androidx.car.app.mediaextensions.KEY_SUBTITLE_LINK_MEDIA_ID";
+ }
+
+}
+
+package androidx.car.app.messaging {
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public class MessagingServiceConstants {
+ field public static final String ACTION_HANDLE_CAR_MESSAGING = "androidx.car.app.messaging.action.HANDLE_CAR_MESSAGING";
+ }
+
+}
+
+package androidx.car.app.messaging.model {
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public class CarMessage {
+ method public androidx.car.app.model.CarText? getBody();
+ method public String? getMultimediaMimeType();
+ method public android.net.Uri? getMultimediaUri();
+ method public long getReceivedTimeEpochMillis();
+ method public androidx.core.app.Person? getSender();
+ method public boolean isRead();
+ }
+
+ public static final class CarMessage.Builder {
+ ctor public CarMessage.Builder();
+ method public androidx.car.app.messaging.model.CarMessage build();
+ method public androidx.car.app.messaging.model.CarMessage.Builder setBody(androidx.car.app.model.CarText?);
+ method public androidx.car.app.messaging.model.CarMessage.Builder setMultimediaMimeType(String?);
+ method public androidx.car.app.messaging.model.CarMessage.Builder setMultimediaUri(android.net.Uri?);
+ method public androidx.car.app.messaging.model.CarMessage.Builder setRead(boolean);
+ method public androidx.car.app.messaging.model.CarMessage.Builder setReceivedTimeEpochMillis(long);
+ method public androidx.car.app.messaging.model.CarMessage.Builder setSender(androidx.core.app.Person?);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi public interface ConversationCallback {
+ method public void onMarkAsRead();
+ method public void onTextReply(String);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public interface ConversationCallbackDelegate {
+ method public void sendMarkAsRead(androidx.car.app.OnDoneCallback);
+ method public void sendTextReply(String, androidx.car.app.OnDoneCallback);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public class ConversationItem implements androidx.car.app.model.Item {
+ method public java.util.List<androidx.car.app.model.Action!> getActions();
+ method public androidx.car.app.messaging.model.ConversationCallbackDelegate getConversationCallbackDelegate();
+ method public androidx.car.app.model.CarIcon? getIcon();
+ method public String getId();
+ method public java.util.List<androidx.car.app.messaging.model.CarMessage!> getMessages();
+ method public androidx.core.app.Person getSelf();
+ method public androidx.car.app.model.CarText getTitle();
+ method public boolean isGroupConversation();
+ }
+
+ public static final class ConversationItem.Builder {
+ ctor public ConversationItem.Builder();
+ ctor public ConversationItem.Builder(androidx.car.app.messaging.model.ConversationItem);
+ method public androidx.car.app.messaging.model.ConversationItem.Builder addAction(androidx.car.app.model.Action);
+ method public androidx.car.app.messaging.model.ConversationItem build();
+ method public androidx.car.app.messaging.model.ConversationItem.Builder setConversationCallback(androidx.car.app.messaging.model.ConversationCallback);
+ method public androidx.car.app.messaging.model.ConversationItem.Builder setGroupConversation(boolean);
+ method public androidx.car.app.messaging.model.ConversationItem.Builder setIcon(androidx.car.app.model.CarIcon);
+ method public androidx.car.app.messaging.model.ConversationItem.Builder setId(String);
+ method public androidx.car.app.messaging.model.ConversationItem.Builder setMessages(java.util.List<androidx.car.app.messaging.model.CarMessage!>);
+ method public androidx.car.app.messaging.model.ConversationItem.Builder setSelf(androidx.core.app.Person);
+ method public androidx.car.app.messaging.model.ConversationItem.Builder setTitle(androidx.car.app.model.CarText);
+ }
+
+}
+
+package androidx.car.app.model {
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class Action {
+ method public androidx.car.app.model.CarColor? getBackgroundColor();
+ method @androidx.car.app.annotations.RequiresCarApi(4) public int getFlags();
+ method public androidx.car.app.model.CarIcon? getIcon();
+ method public androidx.car.app.model.OnClickDelegate? getOnClickDelegate();
+ method public androidx.car.app.model.CarText? getTitle();
+ method public int getType();
+ method @androidx.car.app.annotations.RequiresCarApi(5) public boolean isEnabled();
+ method public boolean isStandard();
+ method public static String typeToString(int);
+ field public static final androidx.car.app.model.Action APP_ICON;
+ field public static final androidx.car.app.model.Action BACK;
+ field @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public static final androidx.car.app.model.Action COMPOSE_MESSAGE;
+ field @androidx.car.app.annotations.RequiresCarApi(5) public static final int FLAG_DEFAULT = 4; // 0x4
+ field @androidx.car.app.annotations.RequiresCarApi(5) public static final int FLAG_IS_PERSISTENT = 2; // 0x2
+ field @androidx.car.app.annotations.RequiresCarApi(4) public static final int FLAG_PRIMARY = 1; // 0x1
+ field public static final androidx.car.app.model.Action PAN;
+ field public static final int TYPE_APP_ICON = 65538; // 0x10002
+ field public static final int TYPE_BACK = 65539; // 0x10003
+ field @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public static final int TYPE_COMPOSE_MESSAGE = 65541; // 0x10005
+ field public static final int TYPE_CUSTOM = 1; // 0x1
+ field public static final int TYPE_PAN = 65540; // 0x10004
+ }
+
+ public static final class Action.Builder {
+ ctor public Action.Builder();
+ ctor @androidx.car.app.annotations.RequiresCarApi(2) public Action.Builder(androidx.car.app.model.Action);
+ method public androidx.car.app.model.Action build();
+ method public androidx.car.app.model.Action.Builder setBackgroundColor(androidx.car.app.model.CarColor);
+ method @androidx.car.app.annotations.RequiresCarApi(5) public androidx.car.app.model.Action.Builder setEnabled(boolean);
+ method @androidx.car.app.annotations.RequiresCarApi(4) public androidx.car.app.model.Action.Builder setFlags(int);
+ method public androidx.car.app.model.Action.Builder setIcon(androidx.car.app.model.CarIcon);
+ method public androidx.car.app.model.Action.Builder setOnClickListener(androidx.car.app.model.OnClickListener);
+ method public androidx.car.app.model.Action.Builder setTitle(androidx.car.app.model.CarText);
+ method public androidx.car.app.model.Action.Builder setTitle(CharSequence);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class ActionStrip {
+ method public java.util.List<androidx.car.app.model.Action!> getActions();
+ method public androidx.car.app.model.Action? getFirstActionOfType(int);
+ }
+
+ public static final class ActionStrip.Builder {
+ ctor public ActionStrip.Builder();
+ method public androidx.car.app.model.ActionStrip.Builder addAction(androidx.car.app.model.Action);
+ method public androidx.car.app.model.ActionStrip build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(5) public final class Alert {
+ method public java.util.List<androidx.car.app.model.Action!> getActions();
+ method public androidx.car.app.model.AlertCallbackDelegate? getCallbackDelegate();
+ method public long getDurationMillis();
+ method public androidx.car.app.model.CarIcon? getIcon();
+ method public int getId();
+ method public androidx.car.app.model.CarText? getSubtitle();
+ method public androidx.car.app.model.CarText getTitle();
+ }
+
+ public static final class Alert.Builder {
+ ctor public Alert.Builder(int, androidx.car.app.model.CarText, long);
+ method public androidx.car.app.model.Alert.Builder addAction(androidx.car.app.model.Action);
+ method public androidx.car.app.model.Alert build();
+ method public androidx.car.app.model.Alert.Builder setCallback(androidx.car.app.model.AlertCallback);
+ method public androidx.car.app.model.Alert.Builder setIcon(androidx.car.app.model.CarIcon);
+ method public androidx.car.app.model.Alert.Builder setSubtitle(androidx.car.app.model.CarText);
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(5) public interface AlertCallback {
+ method public void onCancel(int);
+ method public void onDismiss();
+ field public static final int REASON_NOT_SUPPORTED = 3; // 0x3
+ field public static final int REASON_TIMEOUT = 1; // 0x1
+ field public static final int REASON_USER_ACTION = 2; // 0x2
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(5) public interface AlertCallbackDelegate {
+ method public void sendCancel(int, androidx.car.app.OnDoneCallback);
+ method public void sendDismiss(androidx.car.app.OnDoneCallback);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public class Badge {
+ method public androidx.car.app.model.CarColor? getBackgroundColor();
+ method public androidx.car.app.model.CarIcon? getIcon();
+ method public boolean hasDot();
+ }
+
+ public static final class Badge.Builder {
+ ctor public Badge.Builder();
+ method public androidx.car.app.model.Badge build();
+ method public androidx.car.app.model.Badge.Builder setBackgroundColor(androidx.car.app.model.CarColor);
+ method public androidx.car.app.model.Badge.Builder setHasDot(boolean);
+ method public androidx.car.app.model.Badge.Builder setIcon(androidx.car.app.model.CarIcon);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class CarColor {
+ method public static androidx.car.app.model.CarColor createCustom(@ColorInt int, @ColorInt int);
+ method @ColorInt public int getColor();
+ method @ColorInt public int getColorDark();
+ method public int getType();
+ field public static final androidx.car.app.model.CarColor BLUE;
+ field public static final androidx.car.app.model.CarColor DEFAULT;
+ field public static final androidx.car.app.model.CarColor GREEN;
+ field public static final androidx.car.app.model.CarColor PRIMARY;
+ field public static final androidx.car.app.model.CarColor RED;
+ field public static final androidx.car.app.model.CarColor SECONDARY;
+ field public static final int TYPE_BLUE = 6; // 0x6
+ field public static final int TYPE_CUSTOM = 0; // 0x0
+ field public static final int TYPE_DEFAULT = 1; // 0x1
+ field public static final int TYPE_GREEN = 5; // 0x5
+ field public static final int TYPE_PRIMARY = 2; // 0x2
+ field public static final int TYPE_RED = 4; // 0x4
+ field public static final int TYPE_SECONDARY = 3; // 0x3
+ field public static final int TYPE_YELLOW = 7; // 0x7
+ field public static final androidx.car.app.model.CarColor YELLOW;
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class CarIcon {
+ method public androidx.core.graphics.drawable.IconCompat? getIcon();
+ method public androidx.car.app.model.CarColor? getTint();
+ method public int getType();
+ field public static final androidx.car.app.model.CarIcon ALERT;
+ field public static final androidx.car.app.model.CarIcon APP_ICON;
+ field public static final androidx.car.app.model.CarIcon BACK;
+ field @androidx.car.app.annotations.RequiresCarApi(7) public static final androidx.car.app.model.CarIcon COMPOSE_MESSAGE;
+ field public static final androidx.car.app.model.CarIcon ERROR;
+ field @androidx.car.app.annotations.RequiresCarApi(2) public static final androidx.car.app.model.CarIcon PAN;
+ field public static final int TYPE_ALERT = 4; // 0x4
+ field public static final int TYPE_APP_ICON = 5; // 0x5
+ field public static final int TYPE_BACK = 3; // 0x3
+ field public static final int TYPE_COMPOSE_MESSAGE = 8; // 0x8
+ field public static final int TYPE_CUSTOM = 1; // 0x1
+ field public static final int TYPE_ERROR = 6; // 0x6
+ field public static final int TYPE_PAN = 7; // 0x7
+ }
+
+ public static final class CarIcon.Builder {
+ ctor public CarIcon.Builder(androidx.car.app.model.CarIcon);
+ ctor public CarIcon.Builder(androidx.core.graphics.drawable.IconCompat);
+ method public androidx.car.app.model.CarIcon build();
+ method public androidx.car.app.model.CarIcon.Builder setTint(androidx.car.app.model.CarColor);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class CarIconSpan extends androidx.car.app.model.CarSpan {
+ method public static androidx.car.app.model.CarIconSpan create(androidx.car.app.model.CarIcon);
+ method public static androidx.car.app.model.CarIconSpan create(androidx.car.app.model.CarIcon, int);
+ method public int getAlignment();
+ method public androidx.car.app.model.CarIcon getIcon();
+ field public static final int ALIGN_BASELINE = 1; // 0x1
+ field public static final int ALIGN_BOTTOM = 0; // 0x0
+ field public static final int ALIGN_CENTER = 2; // 0x2
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class CarLocation {
+ method public static androidx.car.app.model.CarLocation create(android.location.Location);
+ method public static androidx.car.app.model.CarLocation create(double, double);
+ method public double getLatitude();
+ method public double getLongitude();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public class CarSpan extends android.text.style.CharacterStyle {
+ ctor public CarSpan();
+ method public void updateDrawState(android.text.TextPaint);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class CarText {
+ method public static androidx.car.app.model.CarText create(CharSequence);
+ method public java.util.List<java.lang.CharSequence!> getVariants();
+ method public boolean isEmpty();
+ method public static boolean isNullOrEmpty(androidx.car.app.model.CarText?);
+ method public CharSequence toCharSequence();
+ }
+
+ @SuppressCompatibility public static final class CarText.Builder {
+ ctor public CarText.Builder(CharSequence);
+ method @androidx.car.app.annotations.RequiresCarApi(2) public androidx.car.app.model.CarText.Builder addVariant(CharSequence);
+ method public androidx.car.app.model.CarText build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(2) public final class ClickableSpan extends androidx.car.app.model.CarSpan {
+ method public static androidx.car.app.model.ClickableSpan create(androidx.car.app.model.OnClickListener);
+ method public androidx.car.app.model.OnClickDelegate getOnClickDelegate();
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public interface Content {
+ method public String getContentId();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class DateTimeWithZone {
+ method @RequiresApi(26) public static androidx.car.app.model.DateTimeWithZone create(java.time.ZonedDateTime);
+ method public static androidx.car.app.model.DateTimeWithZone create(long, @IntRange(from=0xffff02e0, to=64800) int, String);
+ method public static androidx.car.app.model.DateTimeWithZone create(long, java.util.TimeZone);
+ method public long getTimeSinceEpochMillis();
+ method public int getZoneOffsetSeconds();
+ method public String? getZoneShortName();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class Distance {
+ method public static androidx.car.app.model.Distance create(double, int);
+ method public double getDisplayDistance();
+ method public int getDisplayUnit();
+ field public static final int UNIT_FEET = 6; // 0x6
+ field public static final int UNIT_KILOMETERS = 2; // 0x2
+ field public static final int UNIT_KILOMETERS_P1 = 3; // 0x3
+ field public static final int UNIT_METERS = 1; // 0x1
+ field public static final int UNIT_MILES = 4; // 0x4
+ field public static final int UNIT_MILES_P1 = 5; // 0x5
+ field public static final int UNIT_YARDS = 7; // 0x7
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class DistanceSpan extends androidx.car.app.model.CarSpan {
+ method public static androidx.car.app.model.DistanceSpan create(androidx.car.app.model.Distance);
+ method public androidx.car.app.model.Distance getDistance();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class DurationSpan extends androidx.car.app.model.CarSpan {
+ method @RequiresApi(26) public static androidx.car.app.model.DurationSpan create(java.time.Duration);
+ method public static androidx.car.app.model.DurationSpan create(long);
+ method public long getDurationSeconds();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class ForegroundCarColorSpan extends androidx.car.app.model.CarSpan {
+ method public static androidx.car.app.model.ForegroundCarColorSpan create(androidx.car.app.model.CarColor);
+ method public androidx.car.app.model.CarColor getColor();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class GridItem implements androidx.car.app.model.Item {
+ method @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public androidx.car.app.model.Badge? getBadge();
+ method public androidx.car.app.model.CarIcon? getImage();
+ method public int getImageType();
+ method public androidx.car.app.model.OnClickDelegate? getOnClickDelegate();
+ method public androidx.car.app.model.CarText? getText();
+ method public androidx.car.app.model.CarText? getTitle();
+ method public boolean isLoading();
+ field public static final int IMAGE_TYPE_ICON = 1; // 0x1
+ field public static final int IMAGE_TYPE_LARGE = 2; // 0x2
+ }
+
+ public static final class GridItem.Builder {
+ ctor public GridItem.Builder();
+ method public androidx.car.app.model.GridItem build();
+ method public androidx.car.app.model.GridItem.Builder setImage(androidx.car.app.model.CarIcon);
+ method @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public androidx.car.app.model.GridItem.Builder setImage(androidx.car.app.model.CarIcon, androidx.car.app.model.Badge);
+ method public androidx.car.app.model.GridItem.Builder setImage(androidx.car.app.model.CarIcon, int);
+ method @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public androidx.car.app.model.GridItem.Builder setImage(androidx.car.app.model.CarIcon, int, androidx.car.app.model.Badge);
+ method public androidx.car.app.model.GridItem.Builder setLoading(boolean);
+ method public androidx.car.app.model.GridItem.Builder setOnClickListener(androidx.car.app.model.OnClickListener);
+ method public androidx.car.app.model.GridItem.Builder setText(androidx.car.app.model.CarText);
+ method public androidx.car.app.model.GridItem.Builder setText(CharSequence);
+ method public androidx.car.app.model.GridItem.Builder setTitle(androidx.car.app.model.CarText);
+ method public androidx.car.app.model.GridItem.Builder setTitle(CharSequence);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class GridTemplate implements androidx.car.app.model.Template {
+ method public androidx.car.app.model.ActionStrip? getActionStrip();
+ method @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public java.util.List<androidx.car.app.model.Action!> getActions();
+ method public androidx.car.app.model.Action? getHeaderAction();
+ method @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public int getItemImageShape();
+ method @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public int getItemSize();
+ method public androidx.car.app.model.ItemList? getSingleList();
+ method public androidx.car.app.model.CarText? getTitle();
+ method public boolean isLoading();
+ field @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public static final int ITEM_IMAGE_SHAPE_CIRCLE = 2; // 0x2
+ field @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public static final int ITEM_IMAGE_SHAPE_UNSET = 1; // 0x1
+ field @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public static final int ITEM_SIZE_LARGE = 4; // 0x4
+ field @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public static final int ITEM_SIZE_MEDIUM = 2; // 0x2
+ field @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public static final int ITEM_SIZE_SMALL = 1; // 0x1
+ }
+
+ public static final class GridTemplate.Builder {
+ ctor public GridTemplate.Builder();
+ method @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public androidx.car.app.model.GridTemplate.Builder addAction(androidx.car.app.model.Action);
+ method public androidx.car.app.model.GridTemplate build();
+ method public androidx.car.app.model.GridTemplate.Builder setActionStrip(androidx.car.app.model.ActionStrip);
+ method public androidx.car.app.model.GridTemplate.Builder setHeaderAction(androidx.car.app.model.Action);
+ method @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public androidx.car.app.model.GridTemplate.Builder setItemImageShape(@SuppressCompatibility int);
+ method @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public androidx.car.app.model.GridTemplate.Builder setItemSize(@SuppressCompatibility int);
+ method public androidx.car.app.model.GridTemplate.Builder setLoading(boolean);
+ method public androidx.car.app.model.GridTemplate.Builder setSingleList(androidx.car.app.model.ItemList);
+ method public androidx.car.app.model.GridTemplate.Builder setTitle(CharSequence);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(5) public final class Header {
+ method public java.util.List<androidx.car.app.model.Action!> getEndHeaderActions();
+ method public androidx.car.app.model.Action? getStartHeaderAction();
+ method public androidx.car.app.model.CarText? getTitle();
+ }
+
+ public static final class Header.Builder {
+ ctor public Header.Builder();
+ method public androidx.car.app.model.Header.Builder addEndHeaderAction(androidx.car.app.model.Action);
+ method public androidx.car.app.model.Header build();
+ method public androidx.car.app.model.Header.Builder setStartHeaderAction(androidx.car.app.model.Action);
+ method public androidx.car.app.model.Header.Builder setTitle(androidx.car.app.model.CarText);
+ method public androidx.car.app.model.Header.Builder setTitle(CharSequence);
+ }
+
+ @androidx.car.app.annotations.RequiresCarApi(2) public interface InputCallback {
+ method public default void onInputSubmitted(String);
+ method public default void onInputTextChanged(String);
+ }
+
+ @androidx.car.app.annotations.RequiresCarApi(2) public interface InputCallbackDelegate {
+ method public void sendInputSubmitted(String, androidx.car.app.OnDoneCallback);
+ method public void sendInputTextChanged(String, androidx.car.app.OnDoneCallback);
+ }
+
+ @androidx.car.app.annotations.CarProtocol public interface Item {
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class ItemList {
+ method public java.util.List<androidx.car.app.model.Item!> getItems();
+ method public androidx.car.app.model.CarText? getNoItemsMessage();
+ method public androidx.car.app.model.OnItemVisibilityChangedDelegate? getOnItemVisibilityChangedDelegate();
+ method public androidx.car.app.model.OnSelectedDelegate? getOnSelectedDelegate();
+ method public int getSelectedIndex();
+ method @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public androidx.car.app.model.ItemList.Builder toBuilder();
+ }
+
+ public static final class ItemList.Builder {
+ ctor public ItemList.Builder();
+ method public androidx.car.app.model.ItemList.Builder addItem(androidx.car.app.model.Item);
+ method public androidx.car.app.model.ItemList build();
+ method @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public androidx.car.app.model.ItemList.Builder clearItems();
+ method public androidx.car.app.model.ItemList.Builder setNoItemsMessage(CharSequence);
+ method public androidx.car.app.model.ItemList.Builder setOnItemsVisibilityChangedListener(androidx.car.app.model.ItemList.OnItemVisibilityChangedListener);
+ method public androidx.car.app.model.ItemList.Builder setOnSelectedListener(androidx.car.app.model.ItemList.OnSelectedListener);
+ method public androidx.car.app.model.ItemList.Builder setSelectedIndex(@IntRange(from=0) int);
+ }
+
+ public static interface ItemList.OnItemVisibilityChangedListener {
+ method public void onItemVisibilityChanged(int, int);
+ }
+
+ public static interface ItemList.OnSelectedListener {
+ method public void onSelected(int);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class ListTemplate implements androidx.car.app.model.Template {
+ method public androidx.car.app.model.ActionStrip? getActionStrip();
+ method @androidx.car.app.annotations.RequiresCarApi(6) public java.util.List<androidx.car.app.model.Action!> getActions();
+ method public androidx.car.app.model.Action? getHeaderAction();
+ method public java.util.List<androidx.car.app.model.SectionedItemList!> getSectionedLists();
+ method public androidx.car.app.model.ItemList? getSingleList();
+ method public androidx.car.app.model.CarText? getTitle();
+ method public boolean isLoading();
+ method @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public androidx.car.app.model.ListTemplate.Builder toBuilder();
+ }
+
+ public static final class ListTemplate.Builder {
+ ctor public ListTemplate.Builder();
+ method @androidx.car.app.annotations.RequiresCarApi(6) public androidx.car.app.model.ListTemplate.Builder addAction(androidx.car.app.model.Action);
+ method public androidx.car.app.model.ListTemplate.Builder addSectionedList(androidx.car.app.model.SectionedItemList);
+ method public androidx.car.app.model.ListTemplate build();
+ method @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public androidx.car.app.model.ListTemplate.Builder clearSectionedLists();
+ method public androidx.car.app.model.ListTemplate.Builder setActionStrip(androidx.car.app.model.ActionStrip);
+ method public androidx.car.app.model.ListTemplate.Builder setHeaderAction(androidx.car.app.model.Action);
+ method public androidx.car.app.model.ListTemplate.Builder setLoading(boolean);
+ method public androidx.car.app.model.ListTemplate.Builder setSingleList(androidx.car.app.model.ItemList);
+ method public androidx.car.app.model.ListTemplate.Builder setTitle(CharSequence);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.RequiresCarApi(2) public final class LongMessageTemplate implements androidx.car.app.model.Template {
+ method public androidx.car.app.model.ActionStrip? getActionStrip();
+ method public java.util.List<androidx.car.app.model.Action!> getActions();
+ method public androidx.car.app.model.Action? getHeaderAction();
+ method public androidx.car.app.model.CarText getMessage();
+ method public androidx.car.app.model.CarText? getTitle();
+ }
+
+ @androidx.car.app.annotations.RequiresCarApi(2) public static final class LongMessageTemplate.Builder {
+ ctor public LongMessageTemplate.Builder(CharSequence);
+ method public androidx.car.app.model.LongMessageTemplate.Builder addAction(androidx.car.app.model.Action);
+ method public androidx.car.app.model.LongMessageTemplate build();
+ method public androidx.car.app.model.LongMessageTemplate.Builder setActionStrip(androidx.car.app.model.ActionStrip);
+ method public androidx.car.app.model.LongMessageTemplate.Builder setHeaderAction(androidx.car.app.model.Action);
+ method public androidx.car.app.model.LongMessageTemplate.Builder setTitle(CharSequence);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class MessageTemplate implements androidx.car.app.model.Template {
+ method @androidx.car.app.annotations.RequiresCarApi(2) public androidx.car.app.model.ActionStrip? getActionStrip();
+ method public java.util.List<androidx.car.app.model.Action!> getActions();
+ method public androidx.car.app.model.CarText? getDebugMessage();
+ method public androidx.car.app.model.Action? getHeaderAction();
+ method public androidx.car.app.model.CarIcon? getIcon();
+ method public androidx.car.app.model.CarText getMessage();
+ method public androidx.car.app.model.CarText? getTitle();
+ method @androidx.car.app.annotations.RequiresCarApi(2) public boolean isLoading();
+ }
+
+ public static final class MessageTemplate.Builder {
+ ctor public MessageTemplate.Builder(androidx.car.app.model.CarText);
+ ctor public MessageTemplate.Builder(CharSequence);
+ method public androidx.car.app.model.MessageTemplate.Builder addAction(androidx.car.app.model.Action);
+ method public androidx.car.app.model.MessageTemplate build();
+ method @androidx.car.app.annotations.RequiresCarApi(2) public androidx.car.app.model.MessageTemplate.Builder setActionStrip(androidx.car.app.model.ActionStrip);
+ method public androidx.car.app.model.MessageTemplate.Builder setDebugMessage(String);
+ method public androidx.car.app.model.MessageTemplate.Builder setDebugMessage(Throwable);
+ method public androidx.car.app.model.MessageTemplate.Builder setHeaderAction(androidx.car.app.model.Action);
+ method public androidx.car.app.model.MessageTemplate.Builder setIcon(androidx.car.app.model.CarIcon);
+ method @androidx.car.app.annotations.RequiresCarApi(2) public androidx.car.app.model.MessageTemplate.Builder setLoading(boolean);
+ method public androidx.car.app.model.MessageTemplate.Builder setTitle(CharSequence);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class Metadata {
+ method public androidx.car.app.model.Place? getPlace();
+ field public static final androidx.car.app.model.Metadata EMPTY_METADATA;
+ }
+
+ public static final class Metadata.Builder {
+ ctor public Metadata.Builder();
+ ctor public Metadata.Builder(androidx.car.app.model.Metadata);
+ method public androidx.car.app.model.Metadata build();
+ method public androidx.car.app.model.Metadata.Builder setPlace(androidx.car.app.model.Place);
+ }
+
+ @androidx.car.app.annotations.CarProtocol public interface OnCheckedChangeDelegate {
+ method public void sendCheckedChange(boolean, androidx.car.app.OnDoneCallback);
+ }
+
+ @androidx.car.app.annotations.CarProtocol public interface OnClickDelegate {
+ method public boolean isParkedOnly();
+ method public void sendClick(androidx.car.app.OnDoneCallback);
+ }
+
+ public interface OnClickListener {
+ method public void onClick();
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(5) public interface OnContentRefreshDelegate {
+ method public void sendContentRefreshRequested(androidx.car.app.OnDoneCallback);
+ }
+
+ @androidx.car.app.annotations.RequiresCarApi(5) public interface OnContentRefreshListener {
+ method public void onContentRefreshRequested();
+ }
+
+ @androidx.car.app.annotations.CarProtocol public interface OnItemVisibilityChangedDelegate {
+ method public void sendItemVisibilityChanged(int, int, androidx.car.app.OnDoneCallback);
+ }
+
+ @androidx.car.app.annotations.CarProtocol public interface OnSelectedDelegate {
+ method public void sendSelected(int, androidx.car.app.OnDoneCallback);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class Pane {
+ method public java.util.List<androidx.car.app.model.Action!> getActions();
+ method @androidx.car.app.annotations.RequiresCarApi(4) public androidx.car.app.model.CarIcon? getImage();
+ method public java.util.List<androidx.car.app.model.Row!> getRows();
+ method public boolean isLoading();
+ }
+
+ public static final class Pane.Builder {
+ ctor public Pane.Builder();
+ method public androidx.car.app.model.Pane.Builder addAction(androidx.car.app.model.Action);
+ method public androidx.car.app.model.Pane.Builder addRow(androidx.car.app.model.Row);
+ method public androidx.car.app.model.Pane build();
+ method @androidx.car.app.annotations.RequiresCarApi(4) public androidx.car.app.model.Pane.Builder setImage(androidx.car.app.model.CarIcon);
+ method public androidx.car.app.model.Pane.Builder setLoading(boolean);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class PaneTemplate implements androidx.car.app.model.Template {
+ method public androidx.car.app.model.ActionStrip? getActionStrip();
+ method public androidx.car.app.model.Action? getHeaderAction();
+ method public androidx.car.app.model.Pane getPane();
+ method public androidx.car.app.model.CarText? getTitle();
+ }
+
+ public static final class PaneTemplate.Builder {
+ ctor public PaneTemplate.Builder(androidx.car.app.model.Pane);
+ method public androidx.car.app.model.PaneTemplate build();
+ method public androidx.car.app.model.PaneTemplate.Builder setActionStrip(androidx.car.app.model.ActionStrip);
+ method public androidx.car.app.model.PaneTemplate.Builder setHeaderAction(androidx.car.app.model.Action);
+ method public androidx.car.app.model.PaneTemplate.Builder setTitle(CharSequence);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class ParkedOnlyOnClickListener implements androidx.car.app.model.OnClickListener {
+ method public static androidx.car.app.model.ParkedOnlyOnClickListener create(androidx.car.app.model.OnClickListener);
+ method public void onClick();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class Place {
+ method public androidx.car.app.model.CarLocation getLocation();
+ method public androidx.car.app.model.PlaceMarker? getMarker();
+ }
+
+ public static final class Place.Builder {
+ ctor public Place.Builder(androidx.car.app.model.CarLocation);
+ ctor public Place.Builder(androidx.car.app.model.Place);
+ method public androidx.car.app.model.Place build();
+ method public androidx.car.app.model.Place.Builder setMarker(androidx.car.app.model.PlaceMarker);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class PlaceListMapTemplate implements androidx.car.app.model.Template {
+ method public androidx.car.app.model.ActionStrip? getActionStrip();
+ method public androidx.car.app.model.Place? getAnchor();
+ method public androidx.car.app.model.Action? getHeaderAction();
+ method public androidx.car.app.model.ItemList? getItemList();
+ method @androidx.car.app.annotations.RequiresCarApi(5) public androidx.car.app.model.OnContentRefreshDelegate? getOnContentRefreshDelegate();
+ method public androidx.car.app.model.CarText? getTitle();
+ method public boolean isCurrentLocationEnabled();
+ method public boolean isLoading();
+ }
+
+ public static final class PlaceListMapTemplate.Builder {
+ ctor public PlaceListMapTemplate.Builder();
+ method public androidx.car.app.model.PlaceListMapTemplate build();
+ method public androidx.car.app.model.PlaceListMapTemplate.Builder setActionStrip(androidx.car.app.model.ActionStrip);
+ method public androidx.car.app.model.PlaceListMapTemplate.Builder setAnchor(androidx.car.app.model.Place);
+ method public androidx.car.app.model.PlaceListMapTemplate.Builder setCurrentLocationEnabled(boolean);
+ method public androidx.car.app.model.PlaceListMapTemplate.Builder setHeaderAction(androidx.car.app.model.Action);
+ method public androidx.car.app.model.PlaceListMapTemplate.Builder setItemList(androidx.car.app.model.ItemList);
+ method public androidx.car.app.model.PlaceListMapTemplate.Builder setLoading(boolean);
+ method @androidx.car.app.annotations.RequiresCarApi(5) public androidx.car.app.model.PlaceListMapTemplate.Builder setOnContentRefreshListener(androidx.car.app.model.OnContentRefreshListener);
+ method public androidx.car.app.model.PlaceListMapTemplate.Builder setTitle(androidx.car.app.model.CarText);
+ method public androidx.car.app.model.PlaceListMapTemplate.Builder setTitle(CharSequence);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class PlaceMarker {
+ method public androidx.car.app.model.CarColor? getColor();
+ method public androidx.car.app.model.CarIcon? getIcon();
+ method public int getIconType();
+ method public androidx.car.app.model.CarText? getLabel();
+ field public static final int TYPE_ICON = 0; // 0x0
+ field public static final int TYPE_IMAGE = 1; // 0x1
+ }
+
+ public static final class PlaceMarker.Builder {
+ ctor public PlaceMarker.Builder();
+ method public androidx.car.app.model.PlaceMarker build();
+ method public androidx.car.app.model.PlaceMarker.Builder setColor(androidx.car.app.model.CarColor);
+ method public androidx.car.app.model.PlaceMarker.Builder setIcon(androidx.car.app.model.CarIcon, int);
+ method public androidx.car.app.model.PlaceMarker.Builder setLabel(CharSequence);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class Row implements androidx.car.app.model.Item {
+ method @androidx.car.app.annotations.RequiresCarApi(6) public java.util.List<androidx.car.app.model.Action!> getActions();
+ method public androidx.car.app.model.CarIcon? getImage();
+ method public androidx.car.app.model.Metadata? getMetadata();
+ method @androidx.car.app.annotations.RequiresCarApi(6) public int getNumericDecoration();
+ method public androidx.car.app.model.OnClickDelegate? getOnClickDelegate();
+ method public int getRowImageType();
+ method public java.util.List<androidx.car.app.model.CarText!> getTexts();
+ method public androidx.car.app.model.CarText? getTitle();
+ method public androidx.car.app.model.Toggle? getToggle();
+ method public boolean isBrowsable();
+ method @androidx.car.app.annotations.RequiresCarApi(5) public boolean isEnabled();
+ method public androidx.car.app.model.Row row();
+ method public CharSequence yourBoat();
+ field public static final int IMAGE_TYPE_ICON = 4; // 0x4
+ field public static final int IMAGE_TYPE_LARGE = 2; // 0x2
+ field public static final int IMAGE_TYPE_SMALL = 1; // 0x1
+ field public static final int NO_DECORATION = -1; // 0xffffffff
+ }
+
+ public static final class Row.Builder {
+ ctor public Row.Builder();
+ method @androidx.car.app.annotations.RequiresCarApi(6) public androidx.car.app.model.Row.Builder addAction(androidx.car.app.model.Action);
+ method public androidx.car.app.model.Row.Builder addText(androidx.car.app.model.CarText);
+ method public androidx.car.app.model.Row.Builder addText(CharSequence);
+ method public androidx.car.app.model.Row build();
+ method public androidx.car.app.model.Row.Builder setBrowsable(boolean);
+ method @androidx.car.app.annotations.RequiresCarApi(5) public androidx.car.app.model.Row.Builder setEnabled(boolean);
+ method public androidx.car.app.model.Row.Builder setImage(androidx.car.app.model.CarIcon);
+ method public androidx.car.app.model.Row.Builder setImage(androidx.car.app.model.CarIcon, int);
+ method public androidx.car.app.model.Row.Builder setMetadata(androidx.car.app.model.Metadata);
+ method @IntRange(from=0) @androidx.car.app.annotations.RequiresCarApi(6) public androidx.car.app.model.Row.Builder setNumericDecoration(int);
+ method public androidx.car.app.model.Row.Builder setOnClickListener(androidx.car.app.model.OnClickListener);
+ method public androidx.car.app.model.Row.Builder setTitle(androidx.car.app.model.CarText);
+ method public androidx.car.app.model.Row.Builder setTitle(CharSequence);
+ method public androidx.car.app.model.Row.Builder setToggle(androidx.car.app.model.Toggle);
+ }
+
+ @androidx.car.app.annotations.CarProtocol public interface SearchCallbackDelegate {
+ method public void sendSearchSubmitted(String, androidx.car.app.OnDoneCallback);
+ method public void sendSearchTextChanged(String, androidx.car.app.OnDoneCallback);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class SearchTemplate implements androidx.car.app.model.Template {
+ method public androidx.car.app.model.ActionStrip? getActionStrip();
+ method public androidx.car.app.model.Action? getHeaderAction();
+ method public String? getInitialSearchText();
+ method public androidx.car.app.model.ItemList? getItemList();
+ method public androidx.car.app.model.SearchCallbackDelegate getSearchCallbackDelegate();
+ method public String? getSearchHint();
+ method public boolean isLoading();
+ method public boolean isShowKeyboardByDefault();
+ }
+
+ public static final class SearchTemplate.Builder {
+ ctor public SearchTemplate.Builder(androidx.car.app.model.SearchTemplate.SearchCallback);
+ method public androidx.car.app.model.SearchTemplate build();
+ method public androidx.car.app.model.SearchTemplate.Builder setActionStrip(androidx.car.app.model.ActionStrip);
+ method public androidx.car.app.model.SearchTemplate.Builder setHeaderAction(androidx.car.app.model.Action);
+ method public androidx.car.app.model.SearchTemplate.Builder setInitialSearchText(String);
+ method public androidx.car.app.model.SearchTemplate.Builder setItemList(androidx.car.app.model.ItemList);
+ method public androidx.car.app.model.SearchTemplate.Builder setLoading(boolean);
+ method public androidx.car.app.model.SearchTemplate.Builder setSearchHint(String);
+ method public androidx.car.app.model.SearchTemplate.Builder setShowKeyboardByDefault(boolean);
+ }
+
+ public static interface SearchTemplate.SearchCallback {
+ method public default void onSearchSubmitted(String);
+ method public default void onSearchTextChanged(String);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class SectionedItemList {
+ method public static androidx.car.app.model.SectionedItemList create(androidx.car.app.model.ItemList, CharSequence);
+ method public androidx.car.app.model.CarText getHeader();
+ method public androidx.car.app.model.ItemList getItemList();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public final class Tab implements androidx.car.app.model.Content {
+ method public String getContentId();
+ method public androidx.car.app.model.CarIcon getIcon();
+ method public androidx.car.app.model.CarText getTitle();
+ }
+
+ public static final class Tab.Builder {
+ ctor public Tab.Builder();
+ ctor public Tab.Builder(androidx.car.app.model.Tab);
+ method public androidx.car.app.model.Tab build();
+ method public androidx.car.app.model.Tab.Builder setContentId(String);
+ method public androidx.car.app.model.Tab.Builder setIcon(androidx.car.app.model.CarIcon);
+ method public androidx.car.app.model.Tab.Builder setTitle(CharSequence);
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public interface TabCallbackDelegate {
+ method public void sendTabSelected(String, androidx.car.app.OnDoneCallback);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public class TabContents implements androidx.car.app.model.Content {
+ method public String getContentId();
+ method public androidx.car.app.model.Template getTemplate();
+ field public static final String CONTENT_ID = "TAB_CONTENTS_CONTENT_ID";
+ }
+
+ public static final class TabContents.Builder {
+ ctor public TabContents.Builder(androidx.car.app.model.Template);
+ method public androidx.car.app.model.TabContents build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public class TabTemplate implements androidx.car.app.model.Template {
+ method public String getActiveTabContentId();
+ method public androidx.car.app.model.Action getHeaderAction();
+ method public androidx.car.app.model.TabCallbackDelegate getTabCallbackDelegate();
+ method public androidx.car.app.model.TabContents getTabContents();
+ method public java.util.List<androidx.car.app.model.Tab!> getTabs();
+ method public boolean isLoading();
+ }
+
+ public static final class TabTemplate.Builder {
+ ctor public TabTemplate.Builder(androidx.car.app.model.TabTemplate);
+ ctor public TabTemplate.Builder(androidx.car.app.model.TabTemplate.TabCallback);
+ method public androidx.car.app.model.TabTemplate.Builder addTab(androidx.car.app.model.Tab);
+ method public androidx.car.app.model.TabTemplate build();
+ method public androidx.car.app.model.TabTemplate.Builder setActiveTabContentId(String);
+ method public androidx.car.app.model.TabTemplate.Builder setHeaderAction(androidx.car.app.model.Action);
+ method public androidx.car.app.model.TabTemplate.Builder setLoading(boolean);
+ method public androidx.car.app.model.TabTemplate.Builder setTabContents(androidx.car.app.model.TabContents);
+ }
+
+ public static interface TabTemplate.TabCallback {
+ method public default void onTabSelected(String);
+ }
+
+ @androidx.car.app.annotations.CarProtocol public interface Template {
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class TemplateInfo {
+ ctor public TemplateInfo(Class<? extends androidx.car.app.model.Template>, String);
+ method public Class<? extends androidx.car.app.model.Template> getTemplateClass();
+ method public String getTemplateId();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class TemplateWrapper {
+ method public static androidx.car.app.model.TemplateWrapper copyOf(androidx.car.app.model.TemplateWrapper);
+ method public int getCurrentTaskStep();
+ method public String getId();
+ method public androidx.car.app.model.Template getTemplate();
+ method public java.util.List<androidx.car.app.model.TemplateInfo!> getTemplateInfosForScreenStack();
+ method public boolean isRefresh();
+ method public void setCurrentTaskStep(int);
+ method public void setId(String);
+ method public void setRefresh(boolean);
+ method public void setTemplate(androidx.car.app.model.Template);
+ method public static androidx.car.app.model.TemplateWrapper wrap(androidx.car.app.model.Template);
+ method public static androidx.car.app.model.TemplateWrapper wrap(androidx.car.app.model.Template, String);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class Toggle {
+ method public androidx.car.app.model.OnCheckedChangeDelegate getOnCheckedChangeDelegate();
+ method public boolean isChecked();
+ method @androidx.car.app.annotations.RequiresCarApi(5) public boolean isEnabled();
+ }
+
+ public static final class Toggle.Builder {
+ ctor public Toggle.Builder(androidx.car.app.model.Toggle.OnCheckedChangeListener);
+ method public androidx.car.app.model.Toggle build();
+ method public androidx.car.app.model.Toggle.Builder setChecked(boolean);
+ method @androidx.car.app.annotations.RequiresCarApi(5) public androidx.car.app.model.Toggle.Builder setEnabled(boolean);
+ }
+
+ public static interface Toggle.OnCheckedChangeListener {
+ method public void onCheckedChange(boolean);
+ }
+
+}
+
+package androidx.car.app.model.signin {
+
+ @SuppressCompatibility @androidx.car.app.annotations.RequiresCarApi(2) public final class InputSignInMethod implements androidx.car.app.model.signin.SignInTemplate.SignInMethod {
+ method public androidx.car.app.model.CarText? getDefaultValue();
+ method public androidx.car.app.model.CarText? getErrorMessage();
+ method public androidx.car.app.model.CarText? getHint();
+ method public androidx.car.app.model.InputCallbackDelegate getInputCallbackDelegate();
+ method public int getInputType();
+ method public int getKeyboardType();
+ method public boolean isShowKeyboardByDefault();
+ field public static final int INPUT_TYPE_DEFAULT = 1; // 0x1
+ field public static final int INPUT_TYPE_PASSWORD = 2; // 0x2
+ field public static final int KEYBOARD_DEFAULT = 1; // 0x1
+ field public static final int KEYBOARD_EMAIL = 2; // 0x2
+ field public static final int KEYBOARD_NUMBER = 4; // 0x4
+ field public static final int KEYBOARD_PHONE = 3; // 0x3
+ }
+
+ public static final class InputSignInMethod.Builder {
+ ctor public InputSignInMethod.Builder(androidx.car.app.model.InputCallback);
+ method public androidx.car.app.model.signin.InputSignInMethod build();
+ method public androidx.car.app.model.signin.InputSignInMethod.Builder setDefaultValue(String);
+ method public androidx.car.app.model.signin.InputSignInMethod.Builder setErrorMessage(CharSequence);
+ method public androidx.car.app.model.signin.InputSignInMethod.Builder setHint(CharSequence);
+ method public androidx.car.app.model.signin.InputSignInMethod.Builder setInputType(int);
+ method public androidx.car.app.model.signin.InputSignInMethod.Builder setKeyboardType(int);
+ method public androidx.car.app.model.signin.InputSignInMethod.Builder setShowKeyboardByDefault(boolean);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.RequiresCarApi(2) public final class PinSignInMethod implements androidx.car.app.model.signin.SignInTemplate.SignInMethod {
+ ctor public PinSignInMethod(CharSequence);
+ method public androidx.car.app.model.CarText getPinCode();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.RequiresCarApi(2) public final class ProviderSignInMethod implements androidx.car.app.model.signin.SignInTemplate.SignInMethod {
+ ctor public ProviderSignInMethod(androidx.car.app.model.Action);
+ method public androidx.car.app.model.Action getAction();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.RequiresCarApi(4) public final class QRCodeSignInMethod implements androidx.car.app.model.signin.SignInTemplate.SignInMethod {
+ ctor public QRCodeSignInMethod(android.net.Uri);
+ method public android.net.Uri getUri();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.RequiresCarApi(2) public final class SignInTemplate implements androidx.car.app.model.Template {
+ method public androidx.car.app.model.ActionStrip? getActionStrip();
+ method public java.util.List<androidx.car.app.model.Action!> getActions();
+ method public androidx.car.app.model.CarText? getAdditionalText();
+ method public androidx.car.app.model.Action? getHeaderAction();
+ method public androidx.car.app.model.CarText? getInstructions();
+ method public androidx.car.app.model.signin.SignInTemplate.SignInMethod getSignInMethod();
+ method public androidx.car.app.model.CarText? getTitle();
+ method public boolean isLoading();
+ }
+
+ @androidx.car.app.annotations.RequiresCarApi(2) public static final class SignInTemplate.Builder {
+ ctor public SignInTemplate.Builder(androidx.car.app.model.signin.SignInTemplate.SignInMethod);
+ method public androidx.car.app.model.signin.SignInTemplate.Builder addAction(androidx.car.app.model.Action);
+ method public androidx.car.app.model.signin.SignInTemplate build();
+ method public androidx.car.app.model.signin.SignInTemplate.Builder setActionStrip(androidx.car.app.model.ActionStrip);
+ method public androidx.car.app.model.signin.SignInTemplate.Builder setAdditionalText(CharSequence);
+ method public androidx.car.app.model.signin.SignInTemplate.Builder setHeaderAction(androidx.car.app.model.Action);
+ method public androidx.car.app.model.signin.SignInTemplate.Builder setInstructions(CharSequence);
+ method public androidx.car.app.model.signin.SignInTemplate.Builder setLoading(boolean);
+ method public androidx.car.app.model.signin.SignInTemplate.Builder setTitle(CharSequence);
+ }
+
+ public static interface SignInTemplate.SignInMethod {
+ }
+
+}
+
+package androidx.car.app.navigation {
+
+ public class NavigationManager implements androidx.car.app.managers.Manager {
+ method @MainThread public void clearNavigationManagerCallback();
+ method @MainThread public void navigationEnded();
+ method @MainThread public void navigationStarted();
+ method @MainThread public void setNavigationManagerCallback(androidx.car.app.navigation.NavigationManagerCallback);
+ method @MainThread public void setNavigationManagerCallback(java.util.concurrent.Executor, androidx.car.app.navigation.NavigationManagerCallback);
+ method @MainThread public void updateTrip(androidx.car.app.navigation.model.Trip);
+ }
+
+ public interface NavigationManagerCallback {
+ method public default void onAutoDriveEnabled();
+ method public default void onStopNavigation();
+ }
+
+}
+
+package androidx.car.app.navigation.model {
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class Destination {
+ method public androidx.car.app.model.CarText? getAddress();
+ method public androidx.car.app.model.CarIcon? getImage();
+ method public androidx.car.app.model.CarText? getName();
+ }
+
+ public static final class Destination.Builder {
+ ctor public Destination.Builder();
+ method public androidx.car.app.navigation.model.Destination build();
+ method public androidx.car.app.navigation.model.Destination.Builder setAddress(CharSequence);
+ method public androidx.car.app.navigation.model.Destination.Builder setImage(androidx.car.app.model.CarIcon);
+ method public androidx.car.app.navigation.model.Destination.Builder setName(CharSequence);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class Lane {
+ method public java.util.List<androidx.car.app.navigation.model.LaneDirection!> getDirections();
+ }
+
+ public static final class Lane.Builder {
+ ctor public Lane.Builder();
+ method public androidx.car.app.navigation.model.Lane.Builder addDirection(androidx.car.app.navigation.model.LaneDirection);
+ method public androidx.car.app.navigation.model.Lane build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class LaneDirection {
+ method public static androidx.car.app.navigation.model.LaneDirection create(int, boolean);
+ method public int getShape();
+ method public boolean isRecommended();
+ field public static final int SHAPE_NORMAL_LEFT = 5; // 0x5
+ field public static final int SHAPE_NORMAL_RIGHT = 6; // 0x6
+ field public static final int SHAPE_SHARP_LEFT = 7; // 0x7
+ field public static final int SHAPE_SHARP_RIGHT = 8; // 0x8
+ field public static final int SHAPE_SLIGHT_LEFT = 3; // 0x3
+ field public static final int SHAPE_SLIGHT_RIGHT = 4; // 0x4
+ field public static final int SHAPE_STRAIGHT = 2; // 0x2
+ field public static final int SHAPE_UNKNOWN = 1; // 0x1
+ field public static final int SHAPE_U_TURN_LEFT = 9; // 0x9
+ field public static final int SHAPE_U_TURN_RIGHT = 10; // 0xa
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class Maneuver {
+ method public androidx.car.app.model.CarIcon? getIcon();
+ method public int getRoundaboutExitAngle();
+ method public int getRoundaboutExitNumber();
+ method public int getType();
+ field public static final int TYPE_DEPART = 1; // 0x1
+ field public static final int TYPE_DESTINATION = 39; // 0x27
+ field public static final int TYPE_DESTINATION_LEFT = 41; // 0x29
+ field public static final int TYPE_DESTINATION_RIGHT = 42; // 0x2a
+ field public static final int TYPE_DESTINATION_STRAIGHT = 40; // 0x28
+ field public static final int TYPE_FERRY_BOAT = 37; // 0x25
+ field public static final int TYPE_FERRY_BOAT_LEFT = 47; // 0x2f
+ field public static final int TYPE_FERRY_BOAT_RIGHT = 48; // 0x30
+ field public static final int TYPE_FERRY_TRAIN = 38; // 0x26
+ field public static final int TYPE_FERRY_TRAIN_LEFT = 49; // 0x31
+ field public static final int TYPE_FERRY_TRAIN_RIGHT = 50; // 0x32
+ field public static final int TYPE_FORK_LEFT = 25; // 0x19
+ field public static final int TYPE_FORK_RIGHT = 26; // 0x1a
+ field public static final int TYPE_KEEP_LEFT = 3; // 0x3
+ field public static final int TYPE_KEEP_RIGHT = 4; // 0x4
+ field public static final int TYPE_MERGE_LEFT = 27; // 0x1b
+ field public static final int TYPE_MERGE_RIGHT = 28; // 0x1c
+ field public static final int TYPE_MERGE_SIDE_UNSPECIFIED = 29; // 0x1d
+ field public static final int TYPE_NAME_CHANGE = 2; // 0x2
+ field public static final int TYPE_OFF_RAMP_NORMAL_LEFT = 23; // 0x17
+ field public static final int TYPE_OFF_RAMP_NORMAL_RIGHT = 24; // 0x18
+ field public static final int TYPE_OFF_RAMP_SLIGHT_LEFT = 21; // 0x15
+ field public static final int TYPE_OFF_RAMP_SLIGHT_RIGHT = 22; // 0x16
+ field public static final int TYPE_ON_RAMP_NORMAL_LEFT = 15; // 0xf
+ field public static final int TYPE_ON_RAMP_NORMAL_RIGHT = 16; // 0x10
+ field public static final int TYPE_ON_RAMP_SHARP_LEFT = 17; // 0x11
+ field public static final int TYPE_ON_RAMP_SHARP_RIGHT = 18; // 0x12
+ field public static final int TYPE_ON_RAMP_SLIGHT_LEFT = 13; // 0xd
+ field public static final int TYPE_ON_RAMP_SLIGHT_RIGHT = 14; // 0xe
+ field public static final int TYPE_ON_RAMP_U_TURN_LEFT = 19; // 0x13
+ field public static final int TYPE_ON_RAMP_U_TURN_RIGHT = 20; // 0x14
+ field public static final int TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW = 34; // 0x22
+ field public static final int TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW_WITH_ANGLE = 35; // 0x23
+ field public static final int TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW = 32; // 0x20
+ field public static final int TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW_WITH_ANGLE = 33; // 0x21
+ field public static final int TYPE_ROUNDABOUT_ENTER_CCW = 45; // 0x2d
+ field public static final int TYPE_ROUNDABOUT_ENTER_CW = 43; // 0x2b
+ field public static final int TYPE_ROUNDABOUT_EXIT_CCW = 46; // 0x2e
+ field public static final int TYPE_ROUNDABOUT_EXIT_CW = 44; // 0x2c
+ field public static final int TYPE_STRAIGHT = 36; // 0x24
+ field public static final int TYPE_TURN_NORMAL_LEFT = 7; // 0x7
+ field public static final int TYPE_TURN_NORMAL_RIGHT = 8; // 0x8
+ field public static final int TYPE_TURN_SHARP_LEFT = 9; // 0x9
+ field public static final int TYPE_TURN_SHARP_RIGHT = 10; // 0xa
+ field public static final int TYPE_TURN_SLIGHT_LEFT = 5; // 0x5
+ field public static final int TYPE_TURN_SLIGHT_RIGHT = 6; // 0x6
+ field public static final int TYPE_UNKNOWN = 0; // 0x0
+ field public static final int TYPE_U_TURN_LEFT = 11; // 0xb
+ field public static final int TYPE_U_TURN_RIGHT = 12; // 0xc
+ }
+
+ public static final class Maneuver.Builder {
+ ctor public Maneuver.Builder(int);
+ method public androidx.car.app.navigation.model.Maneuver build();
+ method public androidx.car.app.navigation.model.Maneuver.Builder setIcon(androidx.car.app.model.CarIcon);
+ method public androidx.car.app.navigation.model.Maneuver.Builder setRoundaboutExitAngle(@IntRange(from=1, to=360) int);
+ method public androidx.car.app.navigation.model.Maneuver.Builder setRoundaboutExitNumber(@IntRange(from=1) int);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(5) public final class MapController {
+ method public androidx.car.app.model.ActionStrip? getMapActionStrip();
+ method public androidx.car.app.navigation.model.PanModeDelegate? getPanModeDelegate();
+ }
+
+ public static final class MapController.Builder {
+ ctor public MapController.Builder();
+ method public androidx.car.app.navigation.model.MapController build();
+ method public androidx.car.app.navigation.model.MapController.Builder setMapActionStrip(androidx.car.app.model.ActionStrip);
+ method public androidx.car.app.navigation.model.MapController.Builder setPanModeListener(androidx.car.app.navigation.model.PanModeListener);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(5) public final class MapTemplate implements androidx.car.app.model.Template {
+ method public androidx.car.app.model.ActionStrip? getActionStrip();
+ method public androidx.car.app.model.Header? getHeader();
+ method public androidx.car.app.model.ItemList? getItemList();
+ method public androidx.car.app.navigation.model.MapController? getMapController();
+ method public androidx.car.app.model.Pane? getPane();
+ }
+
+ public static final class MapTemplate.Builder {
+ ctor public MapTemplate.Builder();
+ method public androidx.car.app.navigation.model.MapTemplate build();
+ method public androidx.car.app.navigation.model.MapTemplate.Builder setActionStrip(androidx.car.app.model.ActionStrip);
+ method public androidx.car.app.navigation.model.MapTemplate.Builder setHeader(androidx.car.app.model.Header);
+ method public androidx.car.app.navigation.model.MapTemplate.Builder setItemList(androidx.car.app.model.ItemList);
+ method public androidx.car.app.navigation.model.MapTemplate.Builder setMapController(androidx.car.app.navigation.model.MapController);
+ method public androidx.car.app.navigation.model.MapTemplate.Builder setPane(androidx.car.app.model.Pane);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public final class MapWithContentTemplate implements androidx.car.app.model.Template {
+ method public androidx.car.app.model.ActionStrip? getActionStrip();
+ method public androidx.car.app.model.Template? getContentTemplate();
+ method public androidx.car.app.navigation.model.MapController? getMapController();
+ method public boolean isLoading();
+ }
+
+ public static final class MapWithContentTemplate.Builder {
+ ctor public MapWithContentTemplate.Builder();
+ method public androidx.car.app.navigation.model.MapWithContentTemplate build();
+ method public androidx.car.app.navigation.model.MapWithContentTemplate.Builder setActionStrip(androidx.car.app.model.ActionStrip);
+ method public androidx.car.app.navigation.model.MapWithContentTemplate.Builder setContentTemplate(androidx.car.app.model.Template);
+ method public androidx.car.app.navigation.model.MapWithContentTemplate.Builder setLoading(boolean);
+ method public androidx.car.app.navigation.model.MapWithContentTemplate.Builder setMapController(androidx.car.app.navigation.model.MapController);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class MessageInfo implements androidx.car.app.navigation.model.NavigationTemplate.NavigationInfo {
+ method public androidx.car.app.model.CarIcon? getImage();
+ method public androidx.car.app.model.CarText? getText();
+ method public androidx.car.app.model.CarText? getTitle();
+ }
+
+ public static final class MessageInfo.Builder {
+ ctor public MessageInfo.Builder(androidx.car.app.model.CarText);
+ ctor public MessageInfo.Builder(CharSequence);
+ method public androidx.car.app.navigation.model.MessageInfo build();
+ method public androidx.car.app.navigation.model.MessageInfo.Builder setImage(androidx.car.app.model.CarIcon);
+ method public androidx.car.app.navigation.model.MessageInfo.Builder setText(androidx.car.app.model.CarText);
+ method public androidx.car.app.navigation.model.MessageInfo.Builder setText(CharSequence);
+ method public androidx.car.app.navigation.model.MessageInfo.Builder setTitle(CharSequence);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class NavigationTemplate implements androidx.car.app.model.Template {
+ method public androidx.car.app.model.ActionStrip? getActionStrip();
+ method public androidx.car.app.model.CarColor? getBackgroundColor();
+ method public androidx.car.app.navigation.model.TravelEstimate? getDestinationTravelEstimate();
+ method @androidx.car.app.annotations.RequiresCarApi(2) public androidx.car.app.model.ActionStrip? getMapActionStrip();
+ method public androidx.car.app.navigation.model.NavigationTemplate.NavigationInfo? getNavigationInfo();
+ method @androidx.car.app.annotations.RequiresCarApi(2) public androidx.car.app.navigation.model.PanModeDelegate? getPanModeDelegate();
+ method @Deprecated @androidx.car.app.annotations.RequiresCarApi(2) public androidx.car.app.model.Toggle? getPanModeToggle();
+ }
+
+ public static final class NavigationTemplate.Builder {
+ ctor public NavigationTemplate.Builder();
+ method public androidx.car.app.navigation.model.NavigationTemplate build();
+ method public androidx.car.app.navigation.model.NavigationTemplate.Builder setActionStrip(androidx.car.app.model.ActionStrip);
+ method public androidx.car.app.navigation.model.NavigationTemplate.Builder setBackgroundColor(androidx.car.app.model.CarColor);
+ method public androidx.car.app.navigation.model.NavigationTemplate.Builder setDestinationTravelEstimate(androidx.car.app.navigation.model.TravelEstimate);
+ method @androidx.car.app.annotations.RequiresCarApi(2) public androidx.car.app.navigation.model.NavigationTemplate.Builder setMapActionStrip(androidx.car.app.model.ActionStrip);
+ method public androidx.car.app.navigation.model.NavigationTemplate.Builder setNavigationInfo(androidx.car.app.navigation.model.NavigationTemplate.NavigationInfo);
+ method @androidx.car.app.annotations.RequiresCarApi(2) public androidx.car.app.navigation.model.NavigationTemplate.Builder setPanModeListener(androidx.car.app.navigation.model.PanModeListener);
+ }
+
+ public static interface NavigationTemplate.NavigationInfo {
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(2) public interface PanModeDelegate {
+ method public void sendPanModeChanged(boolean, androidx.car.app.OnDoneCallback);
+ }
+
+ public interface PanModeListener {
+ method public void onPanModeChanged(boolean);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class PlaceListNavigationTemplate implements androidx.car.app.model.Template {
+ method public androidx.car.app.model.ActionStrip? getActionStrip();
+ method @androidx.car.app.annotations.RequiresCarApi(5) public androidx.car.app.model.Header? getHeader();
+ method @Deprecated public androidx.car.app.model.Action? getHeaderAction();
+ method public androidx.car.app.model.ItemList? getItemList();
+ method @androidx.car.app.annotations.RequiresCarApi(4) public androidx.car.app.model.ActionStrip? getMapActionStrip();
+ method public androidx.car.app.model.OnContentRefreshDelegate? getOnContentRefreshDelegate();
+ method @androidx.car.app.annotations.RequiresCarApi(4) public androidx.car.app.navigation.model.PanModeDelegate? getPanModeDelegate();
+ method @Deprecated public androidx.car.app.model.CarText? getTitle();
+ method public boolean isLoading();
+ }
+
+ public static final class PlaceListNavigationTemplate.Builder {
+ ctor public PlaceListNavigationTemplate.Builder();
+ method public androidx.car.app.navigation.model.PlaceListNavigationTemplate build();
+ method public androidx.car.app.navigation.model.PlaceListNavigationTemplate.Builder setActionStrip(androidx.car.app.model.ActionStrip);
+ method @androidx.car.app.annotations.RequiresCarApi(5) public androidx.car.app.navigation.model.PlaceListNavigationTemplate.Builder setHeader(androidx.car.app.model.Header);
+ method @Deprecated public androidx.car.app.navigation.model.PlaceListNavigationTemplate.Builder setHeaderAction(androidx.car.app.model.Action);
+ method public androidx.car.app.navigation.model.PlaceListNavigationTemplate.Builder setItemList(androidx.car.app.model.ItemList);
+ method public androidx.car.app.navigation.model.PlaceListNavigationTemplate.Builder setLoading(boolean);
+ method @androidx.car.app.annotations.RequiresCarApi(4) public androidx.car.app.navigation.model.PlaceListNavigationTemplate.Builder setMapActionStrip(androidx.car.app.model.ActionStrip);
+ method public androidx.car.app.navigation.model.PlaceListNavigationTemplate.Builder setOnContentRefreshListener(androidx.car.app.model.OnContentRefreshListener);
+ method @androidx.car.app.annotations.RequiresCarApi(4) public androidx.car.app.navigation.model.PlaceListNavigationTemplate.Builder setPanModeListener(androidx.car.app.navigation.model.PanModeListener);
+ method @Deprecated public androidx.car.app.navigation.model.PlaceListNavigationTemplate.Builder setTitle(androidx.car.app.model.CarText);
+ method @Deprecated public androidx.car.app.navigation.model.PlaceListNavigationTemplate.Builder setTitle(CharSequence);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class RoutePreviewNavigationTemplate implements androidx.car.app.model.Template {
+ method public androidx.car.app.model.ActionStrip? getActionStrip();
+ method @androidx.car.app.annotations.RequiresCarApi(5) public androidx.car.app.model.Header? getHeader();
+ method @Deprecated public androidx.car.app.model.Action? getHeaderAction();
+ method public androidx.car.app.model.ItemList? getItemList();
+ method @androidx.car.app.annotations.RequiresCarApi(4) public androidx.car.app.model.ActionStrip? getMapActionStrip();
+ method public androidx.car.app.model.Action? getNavigateAction();
+ method @androidx.car.app.annotations.RequiresCarApi(4) public androidx.car.app.navigation.model.PanModeDelegate? getPanModeDelegate();
+ method @Deprecated public androidx.car.app.model.CarText? getTitle();
+ method public boolean isLoading();
+ }
+
+ public static final class RoutePreviewNavigationTemplate.Builder {
+ ctor public RoutePreviewNavigationTemplate.Builder();
+ method public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate build();
+ method public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setActionStrip(androidx.car.app.model.ActionStrip);
+ method @androidx.car.app.annotations.RequiresCarApi(5) public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setHeader(androidx.car.app.model.Header);
+ method @Deprecated public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setHeaderAction(androidx.car.app.model.Action);
+ method public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setItemList(androidx.car.app.model.ItemList);
+ method public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setLoading(boolean);
+ method @androidx.car.app.annotations.RequiresCarApi(4) public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setMapActionStrip(androidx.car.app.model.ActionStrip);
+ method public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setNavigateAction(androidx.car.app.model.Action);
+ method @androidx.car.app.annotations.RequiresCarApi(4) public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setPanModeListener(androidx.car.app.navigation.model.PanModeListener);
+ method @Deprecated public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setTitle(androidx.car.app.model.CarText);
+ method @Deprecated public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setTitle(CharSequence);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class RoutingInfo implements androidx.car.app.navigation.model.NavigationTemplate.NavigationInfo {
+ method public androidx.car.app.model.Distance? getCurrentDistance();
+ method public androidx.car.app.navigation.model.Step? getCurrentStep();
+ method public androidx.car.app.model.CarIcon? getJunctionImage();
+ method public androidx.car.app.navigation.model.Step? getNextStep();
+ method public boolean isLoading();
+ }
+
+ public static final class RoutingInfo.Builder {
+ ctor public RoutingInfo.Builder();
+ method public androidx.car.app.navigation.model.RoutingInfo build();
+ method public androidx.car.app.navigation.model.RoutingInfo.Builder setCurrentStep(androidx.car.app.navigation.model.Step, androidx.car.app.model.Distance);
+ method public androidx.car.app.navigation.model.RoutingInfo.Builder setJunctionImage(androidx.car.app.model.CarIcon);
+ method public androidx.car.app.navigation.model.RoutingInfo.Builder setLoading(boolean);
+ method public androidx.car.app.navigation.model.RoutingInfo.Builder setNextStep(androidx.car.app.navigation.model.Step);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class Step {
+ method public androidx.car.app.model.CarText? getCue();
+ method public java.util.List<androidx.car.app.navigation.model.Lane!> getLanes();
+ method public androidx.car.app.model.CarIcon? getLanesImage();
+ method public androidx.car.app.navigation.model.Maneuver? getManeuver();
+ method public androidx.car.app.model.CarText? getRoad();
+ }
+
+ public static final class Step.Builder {
+ ctor public Step.Builder();
+ ctor public Step.Builder(androidx.car.app.model.CarText);
+ ctor public Step.Builder(CharSequence);
+ method public androidx.car.app.navigation.model.Step.Builder addLane(androidx.car.app.navigation.model.Lane);
+ method public androidx.car.app.navigation.model.Step build();
+ method public androidx.car.app.navigation.model.Step.Builder setCue(CharSequence);
+ method public androidx.car.app.navigation.model.Step.Builder setLanesImage(androidx.car.app.model.CarIcon);
+ method public androidx.car.app.navigation.model.Step.Builder setManeuver(androidx.car.app.navigation.model.Maneuver);
+ method public androidx.car.app.navigation.model.Step.Builder setRoad(CharSequence);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class TravelEstimate {
+ method public androidx.car.app.model.DateTimeWithZone? getArrivalTimeAtDestination();
+ method public androidx.car.app.model.Distance? getRemainingDistance();
+ method public androidx.car.app.model.CarColor? getRemainingDistanceColor();
+ method public androidx.car.app.model.CarColor? getRemainingTimeColor();
+ method public long getRemainingTimeSeconds();
+ method @androidx.car.app.annotations.RequiresCarApi(5) public androidx.car.app.model.CarIcon? getTripIcon();
+ method @androidx.car.app.annotations.RequiresCarApi(5) public androidx.car.app.model.CarText? getTripText();
+ field public static final long REMAINING_TIME_UNKNOWN = -1L; // 0xffffffffffffffffL
+ }
+
+ public static final class TravelEstimate.Builder {
+ ctor public TravelEstimate.Builder(androidx.car.app.model.Distance, androidx.car.app.model.DateTimeWithZone);
+ ctor @RequiresApi(26) public TravelEstimate.Builder(androidx.car.app.model.Distance, java.time.ZonedDateTime);
+ method public androidx.car.app.navigation.model.TravelEstimate build();
+ method public androidx.car.app.navigation.model.TravelEstimate.Builder setRemainingDistanceColor(androidx.car.app.model.CarColor);
+ method @RequiresApi(26) public androidx.car.app.navigation.model.TravelEstimate.Builder setRemainingTime(java.time.Duration);
+ method public androidx.car.app.navigation.model.TravelEstimate.Builder setRemainingTimeColor(androidx.car.app.model.CarColor);
+ method public androidx.car.app.navigation.model.TravelEstimate.Builder setRemainingTimeSeconds(@IntRange(from=0xffffffff) long);
+ method @androidx.car.app.annotations.RequiresCarApi(5) public androidx.car.app.navigation.model.TravelEstimate.Builder setTripIcon(androidx.car.app.model.CarIcon);
+ method @androidx.car.app.annotations.RequiresCarApi(5) public androidx.car.app.navigation.model.TravelEstimate.Builder setTripText(androidx.car.app.model.CarText);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class Trip {
+ method public androidx.car.app.model.CarText? getCurrentRoad();
+ method public java.util.List<androidx.car.app.navigation.model.TravelEstimate!> getDestinationTravelEstimates();
+ method public java.util.List<androidx.car.app.navigation.model.Destination!> getDestinations();
+ method public java.util.List<androidx.car.app.navigation.model.TravelEstimate!> getStepTravelEstimates();
+ method public java.util.List<androidx.car.app.navigation.model.Step!> getSteps();
+ method public boolean isLoading();
+ }
+
+ public static final class Trip.Builder {
+ ctor public Trip.Builder();
+ method public androidx.car.app.navigation.model.Trip.Builder addDestination(androidx.car.app.navigation.model.Destination, androidx.car.app.navigation.model.TravelEstimate);
+ method public androidx.car.app.navigation.model.Trip.Builder addStep(androidx.car.app.navigation.model.Step, androidx.car.app.navigation.model.TravelEstimate);
+ method public androidx.car.app.navigation.model.Trip build();
+ method public androidx.car.app.navigation.model.Trip.Builder setCurrentRoad(CharSequence);
+ method public androidx.car.app.navigation.model.Trip.Builder setLoading(boolean);
+ }
+
+}
+
+package androidx.car.app.notification {
+
+ public final class CarAppExtender implements androidx.core.app.NotificationCompat.Extender {
+ ctor public CarAppExtender(android.app.Notification);
+ method public androidx.core.app.NotificationCompat.Builder extend(androidx.core.app.NotificationCompat.Builder);
+ method public java.util.List<android.app.Notification.Action!> getActions();
+ method public String? getChannelId();
+ method public androidx.car.app.model.CarColor? getColor();
+ method public android.app.PendingIntent? getContentIntent();
+ method public CharSequence? getContentText();
+ method public CharSequence? getContentTitle();
+ method public android.app.PendingIntent? getDeleteIntent();
+ method public int getImportance();
+ method public android.graphics.Bitmap? getLargeIcon();
+ method @DrawableRes public int getSmallIcon();
+ method public static boolean isExtended(android.app.Notification);
+ }
+
+ public static final class CarAppExtender.Builder {
+ ctor public CarAppExtender.Builder();
+ method public androidx.car.app.notification.CarAppExtender.Builder addAction(@DrawableRes int, CharSequence, android.app.PendingIntent);
+ method public androidx.car.app.notification.CarAppExtender build();
+ method public androidx.car.app.notification.CarAppExtender.Builder setChannelId(String);
+ method public androidx.car.app.notification.CarAppExtender.Builder setColor(androidx.car.app.model.CarColor);
+ method public androidx.car.app.notification.CarAppExtender.Builder setContentIntent(android.app.PendingIntent);
+ method public androidx.car.app.notification.CarAppExtender.Builder setContentText(CharSequence);
+ method public androidx.car.app.notification.CarAppExtender.Builder setContentTitle(CharSequence);
+ method public androidx.car.app.notification.CarAppExtender.Builder setDeleteIntent(android.app.PendingIntent);
+ method public androidx.car.app.notification.CarAppExtender.Builder setImportance(int);
+ method public androidx.car.app.notification.CarAppExtender.Builder setLargeIcon(android.graphics.Bitmap);
+ method public androidx.car.app.notification.CarAppExtender.Builder setSmallIcon(int);
+ }
+
+ public final class CarNotificationManager {
+ method public boolean areNotificationsEnabled();
+ method public void cancel(int);
+ method public void cancel(String?, int);
+ method public void cancelAll();
+ method public void createNotificationChannel(androidx.core.app.NotificationChannelCompat);
+ method public void createNotificationChannelGroup(androidx.core.app.NotificationChannelGroupCompat);
+ method public void createNotificationChannelGroups(java.util.List<androidx.core.app.NotificationChannelGroupCompat!>);
+ method public void createNotificationChannels(java.util.List<androidx.core.app.NotificationChannelCompat!>);
+ method public void deleteNotificationChannel(String);
+ method public void deleteNotificationChannelGroup(String);
+ method public void deleteUnlistedNotificationChannels(java.util.Collection<java.lang.String!>);
+ method public static androidx.car.app.notification.CarNotificationManager from(android.content.Context);
+ method public static java.util.Set<java.lang.String!> getEnabledListenerPackages(android.content.Context);
+ method public int getImportance();
+ method public androidx.core.app.NotificationChannelCompat? getNotificationChannel(String);
+ method public androidx.core.app.NotificationChannelCompat? getNotificationChannel(String, String);
+ method public androidx.core.app.NotificationChannelGroupCompat? getNotificationChannelGroup(String);
+ method public java.util.List<androidx.core.app.NotificationChannelGroupCompat!> getNotificationChannelGroups();
+ method public java.util.List<androidx.core.app.NotificationChannelCompat!> getNotificationChannels();
+ method public void notify(int, androidx.core.app.NotificationCompat.Builder);
+ method public void notify(String?, int, androidx.core.app.NotificationCompat.Builder);
+ }
+
+ public final class CarPendingIntent {
+ method public static android.app.PendingIntent getCarApp(android.content.Context, int, android.content.Intent, int);
+ }
+
+}
+
+package androidx.car.app.serialization {
+
+ public final class Bundleable implements android.os.Parcelable {
+ method public static androidx.car.app.serialization.Bundleable create(Object) throws androidx.car.app.serialization.BundlerException;
+ method public int describeContents();
+ method public Object get() throws androidx.car.app.serialization.BundlerException;
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<androidx.car.app.serialization.Bundleable!> CREATOR;
+ }
+
+ public class BundlerException extends java.lang.Exception {
+ ctor public BundlerException(String?);
+ ctor public BundlerException(String?, Throwable);
+ }
+
+}
+
+package androidx.car.app.suggestion {
+
+ @androidx.car.app.annotations.RequiresCarApi(5) public class SuggestionManager implements androidx.car.app.managers.Manager {
+ method @MainThread public void updateSuggestions(java.util.List<androidx.car.app.suggestion.model.Suggestion!>);
+ }
+
+}
+
+package androidx.car.app.suggestion.model {
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class Suggestion {
+ method public android.app.PendingIntent? getAction();
+ method public androidx.car.app.model.CarIcon? getIcon();
+ method public String getIdentifier();
+ method public androidx.car.app.model.CarText? getSubtitle();
+ method public androidx.car.app.model.CarText getTitle();
+ }
+
+ public static final class Suggestion.Builder {
+ ctor public Suggestion.Builder();
+ method public androidx.car.app.suggestion.model.Suggestion build();
+ method public androidx.car.app.suggestion.model.Suggestion.Builder setAction(android.app.PendingIntent);
+ method public androidx.car.app.suggestion.model.Suggestion.Builder setIcon(androidx.car.app.model.CarIcon);
+ method public androidx.car.app.suggestion.model.Suggestion.Builder setIdentifier(String);
+ method public androidx.car.app.suggestion.model.Suggestion.Builder setSubtitle(CharSequence);
+ method public androidx.car.app.suggestion.model.Suggestion.Builder setTitle(CharSequence);
+ }
+
+}
+
+package androidx.car.app.validation {
+
+ public final class HostValidator {
+ method public java.util.Map<java.lang.String!,java.util.List<java.lang.String!>!> getAllowedHosts();
+ method public boolean isValidHost(androidx.car.app.HostInfo);
+ field public static final androidx.car.app.validation.HostValidator ALLOW_ALL_HOSTS_VALIDATOR;
+ field public static final String TEMPLATE_RENDERER_PERMISSION = "android.car.permission.TEMPLATE_RENDERER";
+ }
+
+ public static final class HostValidator.Builder {
+ ctor public HostValidator.Builder(android.content.Context);
+ method public androidx.car.app.validation.HostValidator.Builder addAllowedHost(String, String);
+ method public androidx.car.app.validation.HostValidator.Builder addAllowedHosts(@ArrayRes int);
+ method public androidx.car.app.validation.HostValidator build();
+ }
+
+}
+
+package androidx.car.app.versioning {
+
+ public final class CarAppApiLevels {
+ method public static int getLatest();
+ method public static int getOldest();
+ field public static final int LEVEL_1 = 1; // 0x1
+ field public static final int LEVEL_2 = 2; // 0x2
+ field public static final int LEVEL_3 = 3; // 0x3
+ field public static final int LEVEL_4 = 4; // 0x4
+ field public static final int LEVEL_5 = 5; // 0x5
+ field public static final int LEVEL_6 = 6; // 0x6
+ field public static final int LEVEL_7 = 7; // 0x7
+ }
+
+}
+
diff --git a/car/app/app/api/res-1.4.0-beta01.txt b/car/app/app/api/res-1.4.0-beta01.txt
new file mode 100644
index 0000000..686fc80
--- /dev/null
+++ b/car/app/app/api/res-1.4.0-beta01.txt
@@ -0,0 +1,5 @@
+attr carColorPrimary
+attr carColorPrimaryDark
+attr carColorSecondary
+attr carColorSecondaryDark
+attr carPermissionActivityLayout
diff --git a/car/app/app/api/restricted_1.4.0-beta01.txt b/car/app/app/api/restricted_1.4.0-beta01.txt
new file mode 100644
index 0000000..73c12de
--- /dev/null
+++ b/car/app/app/api/restricted_1.4.0-beta01.txt
@@ -0,0 +1,2210 @@
+// Signature format: 4.0
+package androidx.car.app {
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class AppInfo {
+ ctor @VisibleForTesting public AppInfo(int, int, String);
+ method public int getLatestCarAppApiLevel();
+ method public String getLibraryDisplayVersion();
+ method public int getMinCarAppApiLevel();
+ field public static final String MIN_API_LEVEL_METADATA_KEY = "androidx.car.app.minCarApiLevel";
+ }
+
+ public class AppManager implements androidx.car.app.managers.Manager {
+ method @androidx.car.app.annotations.RequiresCarApi(5) public void dismissAlert(int);
+ method public void invalidate();
+ method public void setSurfaceCallback(androidx.car.app.SurfaceCallback?);
+ method @androidx.car.app.annotations.RequiresCarApi(5) public void showAlert(androidx.car.app.model.Alert);
+ method public void showToast(CharSequence, int);
+ }
+
+ public final class CarAppPermission {
+ method public static void checkHasLibraryPermission(android.content.Context, String);
+ method public static void checkHasPermission(android.content.Context, String);
+ field public static final String ACCESS_SURFACE = "androidx.car.app.ACCESS_SURFACE";
+ field public static final String MAP_TEMPLATES = "androidx.car.app.MAP_TEMPLATES";
+ field public static final String NAVIGATION_TEMPLATES = "androidx.car.app.NAVIGATION_TEMPLATES";
+ }
+
+ public abstract class CarAppService extends android.app.Service {
+ ctor public CarAppService();
+ method public abstract androidx.car.app.validation.HostValidator createHostValidator();
+ method @CallSuper public final void dump(java.io.FileDescriptor, java.io.PrintWriter, String![]?);
+ method @Deprecated public final androidx.car.app.Session? getCurrentSession();
+ method public final androidx.car.app.HostInfo? getHostInfo();
+ method public final androidx.car.app.Session? getSession(androidx.car.app.SessionInfo);
+ method @CallSuper public final android.os.IBinder onBind(android.content.Intent);
+ method public androidx.car.app.Session onCreateSession();
+ method @androidx.car.app.annotations.RequiresCarApi(6) public androidx.car.app.Session onCreateSession(androidx.car.app.SessionInfo);
+ method public final boolean onUnbind(android.content.Intent);
+ field @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public static final String CATEGORY_CALLING_APP = "androidx.car.app.category.CALLING";
+ field @Deprecated public static final String CATEGORY_CHARGING_APP = "androidx.car.app.category.CHARGING";
+ field @androidx.car.app.annotations.RequiresCarApi(6) public static final String CATEGORY_FEATURE_CLUSTER = "androidx.car.app.category.FEATURE_CLUSTER";
+ field @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public static final String CATEGORY_IOT_APP = "androidx.car.app.category.IOT";
+ field @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public static final String CATEGORY_MESSAGING_APP = "androidx.car.app.category.MESSAGING";
+ field public static final String CATEGORY_NAVIGATION_APP = "androidx.car.app.category.NAVIGATION";
+ field @Deprecated public static final String CATEGORY_PARKING_APP = "androidx.car.app.category.PARKING";
+ field public static final String CATEGORY_POI_APP = "androidx.car.app.category.POI";
+ field @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public static final String CATEGORY_SETTINGS_APP = "androidx.car.app.category.SETTINGS";
+ field @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public static final String CATEGORY_WEATHER_APP = "androidx.car.app.category.WEATHER";
+ field public static final String SERVICE_INTERFACE = "androidx.car.app.CarAppService";
+ }
+
+ public class CarContext extends android.content.ContextWrapper {
+ method public void finishCarApp();
+ method @androidx.car.app.annotations.RequiresCarApi(2) public android.content.ComponentName? getCallingComponent();
+ method public int getCarAppApiLevel();
+ method public <T> T getCarService(Class<T!>);
+ method public Object getCarService(String);
+ method public String getCarServiceName(Class<?>);
+ method public androidx.car.app.HostInfo? getHostInfo();
+ method public androidx.activity.OnBackPressedDispatcher getOnBackPressedDispatcher();
+ method public boolean isDarkMode();
+ method public void requestPermissions(java.util.List<java.lang.String!>, androidx.car.app.OnRequestPermissionsListener);
+ method public void requestPermissions(java.util.List<java.lang.String!>, java.util.concurrent.Executor, androidx.car.app.OnRequestPermissionsListener);
+ method @androidx.car.app.annotations.RequiresCarApi(2) public void setCarAppResult(int, android.content.Intent?);
+ method public void startCarApp(android.content.Intent);
+ method @Deprecated public static void startCarApp(android.content.Intent, android.content.Intent);
+ field public static final String ACTION_NAVIGATE = "androidx.car.app.action.NAVIGATE";
+ field public static final String APP_SERVICE = "app";
+ field public static final String CAR_SERVICE = "car";
+ field @androidx.car.app.annotations.RequiresCarApi(2) public static final String CONSTRAINT_SERVICE = "constraints";
+ field public static final String EXTRA_START_CAR_APP_BINDER_KEY = "androidx.car.app.extra.START_CAR_APP_BINDER_KEY";
+ field @androidx.car.app.annotations.RequiresCarApi(3) public static final String HARDWARE_SERVICE = "hardware";
+ field public static final String NAVIGATION_SERVICE = "navigation";
+ field public static final String SCREEN_SERVICE = "screen";
+ field public static final String SUGGESTION_SERVICE = "suggestion";
+ }
+
+ public final class CarToast {
+ method public static androidx.car.app.CarToast makeText(androidx.car.app.CarContext, @StringRes int, int);
+ method public static androidx.car.app.CarToast makeText(androidx.car.app.CarContext, CharSequence, int);
+ method public void setDuration(int);
+ method public void setText(@StringRes int);
+ method public void setText(CharSequence);
+ method public void show();
+ field public static final int LENGTH_LONG = 1; // 0x1
+ field public static final int LENGTH_SHORT = 0; // 0x0
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class FailureResponse {
+ ctor public FailureResponse(Throwable);
+ method public int getErrorType();
+ method public String getStackTrace();
+ field public static final int BUNDLER_EXCEPTION = 1; // 0x1
+ field public static final int ILLEGAL_STATE_EXCEPTION = 2; // 0x2
+ field public static final int INVALID_PARAMETER_EXCEPTION = 3; // 0x3
+ field public static final int REMOTE_EXCEPTION = 6; // 0x6
+ field public static final int RUNTIME_EXCEPTION = 5; // 0x5
+ field public static final int SECURITY_EXCEPTION = 4; // 0x4
+ field public static final int UNKNOWN_ERROR = 0; // 0x0
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class HandshakeInfo {
+ ctor public HandshakeInfo(String, int);
+ method public int getHostCarAppApiLevel();
+ method public String getHostPackageName();
+ }
+
+ public final class HostException extends java.lang.RuntimeException {
+ ctor public HostException(String);
+ ctor public HostException(String, Throwable);
+ ctor public HostException(Throwable);
+ }
+
+ public final class HostInfo {
+ ctor public HostInfo(String, int);
+ method public String getPackageName();
+ method public int getUid();
+ }
+
+ @androidx.car.app.annotations.CarProtocol public interface OnDoneCallback {
+ method public default void onFailure(androidx.car.app.serialization.Bundleable);
+ method public default void onSuccess(androidx.car.app.serialization.Bundleable?);
+ }
+
+ public interface OnRequestPermissionsListener {
+ method public void onRequestPermissionsResult(java.util.List<java.lang.String!>, java.util.List<java.lang.String!>);
+ }
+
+ public interface OnScreenResultListener {
+ method public void onScreenResult(Object?);
+ }
+
+ public abstract class Screen implements androidx.lifecycle.LifecycleOwner {
+ ctor protected Screen(androidx.car.app.CarContext);
+ method public final void finish();
+ method public final androidx.car.app.CarContext getCarContext();
+ method public final androidx.lifecycle.Lifecycle getLifecycle();
+ method public String? getMarker();
+ method public final androidx.car.app.ScreenManager getScreenManager();
+ method public final void invalidate();
+ method public abstract androidx.car.app.model.Template onGetTemplate();
+ method public void setMarker(String?);
+ method public void setResult(Object?);
+ }
+
+ @MainThread public class ScreenManager implements androidx.car.app.managers.Manager {
+ method public java.util.Collection<androidx.car.app.Screen!> getScreenStack();
+ method public int getStackSize();
+ method public androidx.car.app.Screen getTop();
+ method public void pop();
+ method public void popTo(String);
+ method public void popToRoot();
+ method public void push(androidx.car.app.Screen);
+ method public void pushForResult(androidx.car.app.Screen, androidx.car.app.OnScreenResultListener);
+ method public void remove(androidx.car.app.Screen);
+ }
+
+ public abstract class Session implements androidx.lifecycle.LifecycleOwner {
+ ctor public Session();
+ method public final androidx.car.app.CarContext getCarContext();
+ method public androidx.lifecycle.Lifecycle getLifecycle();
+ method public void onCarConfigurationChanged(android.content.res.Configuration);
+ method public abstract androidx.car.app.Screen onCreateScreen(android.content.Intent);
+ method public void onNewIntent(android.content.Intent);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public class SessionInfo {
+ ctor public SessionInfo(int, String);
+ method public int getDisplayType();
+ method public String getSessionId();
+ method public java.util.Set<java.lang.Class<? extends androidx.car.app.model.Template>!>? getSupportedTemplates(int);
+ field public static final androidx.car.app.SessionInfo DEFAULT_SESSION_INFO;
+ field public static final int DISPLAY_TYPE_CLUSTER = 1; // 0x1
+ field public static final int DISPLAY_TYPE_MAIN = 0; // 0x0
+ }
+
+ public class SessionInfoIntentEncoder {
+ method public static boolean containsSessionInfo(android.content.Intent);
+ method public static androidx.car.app.SessionInfo decode(android.content.Intent);
+ method public static void encode(androidx.car.app.SessionInfo, android.content.Intent);
+ }
+
+ public interface SurfaceCallback {
+ method @androidx.car.app.annotations.RequiresCarApi(5) public default void onClick(float, float);
+ method @androidx.car.app.annotations.RequiresCarApi(2) public default void onFling(float, float);
+ method @androidx.car.app.annotations.RequiresCarApi(2) public default void onScale(float, float, float);
+ method @androidx.car.app.annotations.RequiresCarApi(2) public default void onScroll(float, float);
+ method public default void onStableAreaChanged(android.graphics.Rect);
+ method public default void onSurfaceAvailable(androidx.car.app.SurfaceContainer);
+ method public default void onSurfaceDestroyed(androidx.car.app.SurfaceContainer);
+ method public default void onVisibleAreaChanged(android.graphics.Rect);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class SurfaceContainer {
+ ctor public SurfaceContainer(android.view.Surface?, int, int, int);
+ method public int getDpi();
+ method public int getHeight();
+ method public android.view.Surface? getSurface();
+ method public int getWidth();
+ }
+
+}
+
+package androidx.car.app.annotations {
+
+ @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.PARAMETER}) public @interface CarProtocol {
+ }
+
+ @SuppressCompatibility @RequiresOptIn @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD}) public @interface ExperimentalCarApi {
+ }
+
+ @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD}) public @interface RequiresCarApi {
+ method public abstract int value();
+ }
+
+}
+
+package androidx.car.app.connection {
+
+ public final class CarConnection {
+ ctor @MainThread public CarConnection(android.content.Context);
+ method public androidx.lifecycle.LiveData<java.lang.Integer!> getType();
+ field public static final String ACTION_CAR_CONNECTION_UPDATED = "androidx.car.app.connection.action.CAR_CONNECTION_UPDATED";
+ field public static final String CAR_CONNECTION_STATE = "CarConnectionState";
+ field public static final int CONNECTION_TYPE_NATIVE = 1; // 0x1
+ field public static final int CONNECTION_TYPE_NOT_CONNECTED = 0; // 0x0
+ field public static final int CONNECTION_TYPE_PROJECTION = 2; // 0x2
+ }
+
+}
+
+package androidx.car.app.constraints {
+
+ @androidx.car.app.annotations.RequiresCarApi(2) public class ConstraintManager implements androidx.car.app.managers.Manager {
+ method public int getContentLimit(int);
+ method @androidx.car.app.annotations.RequiresCarApi(6) public boolean isAppDrivenRefreshEnabled();
+ field public static final int CONTENT_LIMIT_TYPE_GRID = 1; // 0x1
+ field public static final int CONTENT_LIMIT_TYPE_LIST = 0; // 0x0
+ field public static final int CONTENT_LIMIT_TYPE_PANE = 4; // 0x4
+ field public static final int CONTENT_LIMIT_TYPE_PLACE_LIST = 2; // 0x2
+ field public static final int CONTENT_LIMIT_TYPE_ROUTE_LIST = 3; // 0x3
+ }
+
+}
+
+package androidx.car.app.hardware {
+
+ @MainThread @androidx.car.app.annotations.RequiresCarApi(3) public interface CarHardwareManager extends androidx.car.app.managers.Manager {
+ method @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public default androidx.car.app.hardware.climate.CarClimate getCarClimate();
+ method public default androidx.car.app.hardware.info.CarInfo getCarInfo();
+ method public default androidx.car.app.hardware.info.CarSensors getCarSensors();
+ }
+
+}
+
+package androidx.car.app.hardware.climate {
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class CabinTemperatureProfile {
+ method public java.util.Map<java.util.Set<androidx.car.app.hardware.common.CarZone!>!,android.util.Pair<java.lang.Float!,java.lang.Float!>!> getCarZoneSetsToCabinCelsiusTemperatureRanges();
+ method public float getCelsiusSupportedIncrement();
+ method public float getFahrenheitSupportedIncrement();
+ method public android.util.Pair<java.lang.Float!,java.lang.Float!> getSupportedMinMaxCelsiusRange();
+ method public android.util.Pair<java.lang.Float!,java.lang.Float!> getSupportedMinMaxFahrenheitRange();
+ method public boolean hasCarZoneSetsToCabinCelsiusTemperatureRanges();
+ method public boolean hasCelsiusSupportedIncrement();
+ method public boolean hasFahrenheitSupportedIncrement();
+ method public boolean hasSupportedMinMaxCelsiusRange();
+ method public boolean hasSupportedMinMaxFahrenheitRange();
+ }
+
+ public static final class CabinTemperatureProfile.Builder {
+ ctor public CabinTemperatureProfile.Builder();
+ method public androidx.car.app.hardware.climate.CabinTemperatureProfile build();
+ method public androidx.car.app.hardware.climate.CabinTemperatureProfile.Builder setCarZoneSetsToCabinCelsiusTemperatureRanges(java.util.Map<java.util.Set<androidx.car.app.hardware.common.CarZone!>!,android.util.Pair<java.lang.Float!,java.lang.Float!>!>);
+ method public androidx.car.app.hardware.climate.CabinTemperatureProfile.Builder setCelsiusSupportedIncrement(float);
+ method public androidx.car.app.hardware.climate.CabinTemperatureProfile.Builder setFahrenheitSupportedIncrement(float);
+ method public androidx.car.app.hardware.climate.CabinTemperatureProfile.Builder setSupportedMinMaxCelsiusRange(android.util.Pair<java.lang.Float!,java.lang.Float!>);
+ method public androidx.car.app.hardware.climate.CabinTemperatureProfile.Builder setSupportedMinMaxFahrenheitRange(android.util.Pair<java.lang.Float!,java.lang.Float!>);
+ }
+
+ @SuppressCompatibility @MainThread @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(5) public interface CarClimate {
+ method public void fetchClimateProfile(java.util.concurrent.Executor, androidx.car.app.hardware.climate.ClimateProfileRequest, androidx.car.app.hardware.climate.CarClimateProfileCallback);
+ method public void registerClimateStateCallback(java.util.concurrent.Executor, androidx.car.app.hardware.climate.RegisterClimateStateRequest, androidx.car.app.hardware.climate.CarClimateStateCallback);
+ method public <E> void setClimateState(java.util.concurrent.Executor, androidx.car.app.hardware.climate.ClimateStateRequest<E!>, androidx.car.app.hardware.common.CarSetOperationStatusCallback);
+ method public void unregisterClimateStateCallback(androidx.car.app.hardware.climate.CarClimateStateCallback);
+ }
+
+ @SuppressCompatibility @MainThread @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(5) public final class CarClimateFeature {
+ method public java.util.List<androidx.car.app.hardware.common.CarZone!> getCarZones();
+ method public int getFeature();
+ }
+
+ public static final class CarClimateFeature.Builder {
+ ctor public CarClimateFeature.Builder(int);
+ method public androidx.car.app.hardware.climate.CarClimateFeature.Builder addCarZones(androidx.car.app.hardware.common.CarZone!...);
+ method public androidx.car.app.hardware.climate.CarClimateFeature build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(5) public interface CarClimateProfileCallback {
+ method public default void onCabinTemperatureProfileAvailable(androidx.car.app.hardware.climate.CabinTemperatureProfile);
+ method public default void onCarZoneMappingInfoProfileAvailable(androidx.car.app.hardware.climate.CarZoneMappingInfoProfile);
+ method public default void onDefrosterProfileAvailable(androidx.car.app.hardware.climate.DefrosterProfile);
+ method public default void onElectricDefrosterProfileAvailable(androidx.car.app.hardware.climate.ElectricDefrosterProfile);
+ method public default void onFanDirectionProfileAvailable(androidx.car.app.hardware.climate.FanDirectionProfile);
+ method public default void onFanSpeedLevelProfileAvailable(androidx.car.app.hardware.climate.FanSpeedLevelProfile);
+ method public default void onHvacAcProfileAvailable(androidx.car.app.hardware.climate.HvacAcProfile);
+ method public default void onHvacAutoModeProfileAvailable(androidx.car.app.hardware.climate.HvacAutoModeProfile);
+ method public default void onHvacAutoRecirculationProfileAvailable(androidx.car.app.hardware.climate.HvacAutoRecirculationProfile);
+ method public default void onHvacDualModeProfileAvailable(androidx.car.app.hardware.climate.HvacDualModeProfile);
+ method public default void onHvacMaxAcModeProfileAvailable(androidx.car.app.hardware.climate.HvacMaxAcModeProfile);
+ method public default void onHvacPowerProfileAvailable(androidx.car.app.hardware.climate.HvacPowerProfile);
+ method public default void onHvacRecirculationProfileAvailable(androidx.car.app.hardware.climate.HvacRecirculationProfile);
+ method public default void onMaxDefrosterProfileAvailable(androidx.car.app.hardware.climate.MaxDefrosterProfile);
+ method public default void onSeatTemperatureLevelProfileAvailable(androidx.car.app.hardware.climate.SeatTemperatureProfile);
+ method public default void onSeatVentilationLevelProfileAvailable(androidx.car.app.hardware.climate.SeatVentilationProfile);
+ method public default void onSteeringWheelHeatProfileAvailable(androidx.car.app.hardware.climate.SteeringWheelHeatProfile);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(5) public interface CarClimateStateCallback {
+ method public default void onCabinTemperatureStateAvailable(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+ method public default void onDefrosterStateAvailable(androidx.car.app.hardware.common.CarValue<java.lang.Boolean!>);
+ method public default void onElectricDefrosterStateAvailable(androidx.car.app.hardware.common.CarValue<java.lang.Boolean!>);
+ method public default void onFanDirectionStateAvailable(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+ method public default void onFanSpeedLevelStateAvailable(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+ method public default void onHvacAcStateAvailable(androidx.car.app.hardware.common.CarValue<java.lang.Boolean!>);
+ method public default void onHvacAutoModeStateAvailable(androidx.car.app.hardware.common.CarValue<java.lang.Boolean!>);
+ method public default void onHvacAutoRecirculationStateAvailable(androidx.car.app.hardware.common.CarValue<java.lang.Boolean!>);
+ method public default void onHvacDualModeStateAvailable(androidx.car.app.hardware.common.CarValue<java.lang.Boolean!>);
+ method public default void onHvacMaxAcModeStateAvailable(androidx.car.app.hardware.common.CarValue<java.lang.Boolean!>);
+ method public default void onHvacPowerStateAvailable(androidx.car.app.hardware.common.CarValue<java.lang.Boolean!>);
+ method public default void onHvacRecirculationStateAvailable(androidx.car.app.hardware.common.CarValue<java.lang.Boolean!>);
+ method public default void onMaxDefrosterStateAvailable(androidx.car.app.hardware.common.CarValue<java.lang.Boolean!>);
+ method public default void onSeatTemperatureLevelStateAvailable(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+ method public default void onSeatVentilationLevelStateAvailable(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+ method public default void onSteeringWheelHeatStateAvailable(androidx.car.app.hardware.common.CarValue<java.lang.Boolean!>);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class CarZoneMappingInfoProfile {
+ method public java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!> getSupportedCarZoneSets();
+ }
+
+ public static final class CarZoneMappingInfoProfile.Builder {
+ ctor public CarZoneMappingInfoProfile.Builder(java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!>);
+ method public androidx.car.app.hardware.climate.CarZoneMappingInfoProfile build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(5) public final class ClimateProfileRequest {
+ method public java.util.Set<java.lang.Integer!> getAllClimateProfiles();
+ method public java.util.List<androidx.car.app.hardware.climate.CarClimateFeature!> getClimateProfileFeatures();
+ field public static final int FEATURE_CABIN_TEMPERATURE = 4; // 0x4
+ field public static final int FEATURE_CAR_ZONE_MAPPING = 17; // 0x11
+ field public static final int FEATURE_FAN_DIRECTION = 6; // 0x6
+ field public static final int FEATURE_FAN_SPEED = 5; // 0x5
+ field public static final int FEATURE_HVAC_AC = 2; // 0x2
+ field public static final int FEATURE_HVAC_AUTO_MODE = 12; // 0xc
+ field public static final int FEATURE_HVAC_AUTO_RECIRCULATION = 11; // 0xb
+ field public static final int FEATURE_HVAC_DEFROSTER = 14; // 0xe
+ field public static final int FEATURE_HVAC_DUAL_MODE = 13; // 0xd
+ field public static final int FEATURE_HVAC_ELECTRIC_DEFROSTER = 16; // 0x10
+ field public static final int FEATURE_HVAC_MAX_AC = 3; // 0x3
+ field public static final int FEATURE_HVAC_MAX_DEFROSTER = 15; // 0xf
+ field public static final int FEATURE_HVAC_POWER = 1; // 0x1
+ field public static final int FEATURE_HVAC_RECIRCULATION = 10; // 0xa
+ field public static final int FEATURE_SEAT_TEMPERATURE_LEVEL = 7; // 0x7
+ field public static final int FEATURE_SEAT_VENTILATION_LEVEL = 8; // 0x8
+ field public static final int FEATURE_STEERING_WHEEL_HEAT = 9; // 0x9
+ }
+
+ public static final class ClimateProfileRequest.Builder {
+ ctor public ClimateProfileRequest.Builder();
+ method public androidx.car.app.hardware.climate.ClimateProfileRequest.Builder addClimateProfileFeatures(androidx.car.app.hardware.climate.CarClimateFeature!...);
+ method public androidx.car.app.hardware.climate.ClimateProfileRequest build();
+ method public androidx.car.app.hardware.climate.ClimateProfileRequest.Builder setAllClimateProfiles();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(5) public final class ClimateStateRequest<T> {
+ method public java.util.List<androidx.car.app.hardware.common.CarZone!> getCarZones();
+ method public int getRequestedFeature();
+ method public T getRequestedValue();
+ }
+
+ public static final class ClimateStateRequest.Builder<T> {
+ ctor public ClimateStateRequest.Builder(int, T!);
+ method public androidx.car.app.hardware.climate.ClimateStateRequest.Builder<T!> addCarZones(androidx.car.app.hardware.common.CarZone);
+ method public androidx.car.app.hardware.climate.ClimateStateRequest<T!> build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class DefrosterProfile {
+ method public java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!> getSupportedCarZoneSets();
+ }
+
+ public static final class DefrosterProfile.Builder {
+ ctor public DefrosterProfile.Builder(java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!>);
+ method public androidx.car.app.hardware.climate.DefrosterProfile build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class ElectricDefrosterProfile {
+ method public java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!> getSupportedCarZoneSets();
+ }
+
+ public static final class ElectricDefrosterProfile.Builder {
+ ctor public ElectricDefrosterProfile.Builder(java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!>);
+ method public androidx.car.app.hardware.climate.ElectricDefrosterProfile build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class FanDirectionProfile {
+ method public java.util.Map<java.util.Set<androidx.car.app.hardware.common.CarZone!>!,java.util.Set<java.lang.Integer!>!> getCarZoneSetsToFanDirectionValues();
+ }
+
+ public static final class FanDirectionProfile.Builder {
+ ctor public FanDirectionProfile.Builder(java.util.Map<java.util.Set<androidx.car.app.hardware.common.CarZone!>!,java.util.Set<java.lang.Integer!>!>);
+ method public androidx.car.app.hardware.climate.FanDirectionProfile build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class FanSpeedLevelProfile {
+ method public java.util.Map<java.util.Set<androidx.car.app.hardware.common.CarZone!>!,android.util.Pair<java.lang.Integer!,java.lang.Integer!>!> getCarZoneSetsToFanSpeedLevelRanges();
+ }
+
+ public static final class FanSpeedLevelProfile.Builder {
+ ctor public FanSpeedLevelProfile.Builder(java.util.Map<java.util.Set<androidx.car.app.hardware.common.CarZone!>!,android.util.Pair<java.lang.Integer!,java.lang.Integer!>!>);
+ method public androidx.car.app.hardware.climate.FanSpeedLevelProfile build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class HvacAcProfile {
+ method public java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!> getSupportedCarZoneSets();
+ }
+
+ public static final class HvacAcProfile.Builder {
+ ctor public HvacAcProfile.Builder(java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!>);
+ method public androidx.car.app.hardware.climate.HvacAcProfile build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class HvacAutoModeProfile {
+ method public java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!> getSupportedCarZoneSets();
+ }
+
+ public static final class HvacAutoModeProfile.Builder {
+ ctor public HvacAutoModeProfile.Builder(java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!>);
+ method public androidx.car.app.hardware.climate.HvacAutoModeProfile build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class HvacAutoRecirculationProfile {
+ method public java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!> getSupportedCarZoneSets();
+ }
+
+ public static final class HvacAutoRecirculationProfile.Builder {
+ ctor public HvacAutoRecirculationProfile.Builder(java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!>);
+ method public androidx.car.app.hardware.climate.HvacAutoRecirculationProfile build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class HvacDualModeProfile {
+ method public java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!> getSupportedCarZoneSets();
+ }
+
+ public static final class HvacDualModeProfile.Builder {
+ ctor public HvacDualModeProfile.Builder(java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!>);
+ method public androidx.car.app.hardware.climate.HvacDualModeProfile build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class HvacMaxAcModeProfile {
+ method public java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!> getSupportedCarZoneSets();
+ }
+
+ public static final class HvacMaxAcModeProfile.Builder {
+ ctor public HvacMaxAcModeProfile.Builder(java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!>);
+ method public androidx.car.app.hardware.climate.HvacMaxAcModeProfile build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class HvacPowerProfile {
+ method public java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!> getSupportedCarZoneSets();
+ }
+
+ public static final class HvacPowerProfile.Builder {
+ ctor public HvacPowerProfile.Builder(java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!>);
+ method public androidx.car.app.hardware.climate.HvacPowerProfile build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class HvacRecirculationProfile {
+ method public java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!> getSupportedCarZones();
+ }
+
+ public static final class HvacRecirculationProfile.Builder {
+ ctor public HvacRecirculationProfile.Builder(java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!>);
+ method public androidx.car.app.hardware.climate.HvacRecirculationProfile build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class MaxDefrosterProfile {
+ method public java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!> getSupportedCarZoneSets();
+ }
+
+ public static final class MaxDefrosterProfile.Builder {
+ ctor public MaxDefrosterProfile.Builder(java.util.List<java.util.Set<androidx.car.app.hardware.common.CarZone!>!>);
+ method public androidx.car.app.hardware.climate.MaxDefrosterProfile build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(5) public final class RegisterClimateStateRequest {
+ method public java.util.List<androidx.car.app.hardware.climate.CarClimateFeature!> getClimateRegisterFeatures();
+ }
+
+ public static final class RegisterClimateStateRequest.Builder {
+ ctor public RegisterClimateStateRequest.Builder(boolean);
+ method public androidx.car.app.hardware.climate.RegisterClimateStateRequest.Builder addClimateRegisterFeatures(androidx.car.app.hardware.climate.CarClimateFeature!...);
+ method public androidx.car.app.hardware.climate.RegisterClimateStateRequest build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class SeatTemperatureProfile {
+ method public java.util.Map<java.util.Set<androidx.car.app.hardware.common.CarZone!>!,android.util.Pair<java.lang.Integer!,java.lang.Integer!>!> getCarZoneSetsToSeatTemperatureValues();
+ }
+
+ public static final class SeatTemperatureProfile.Builder {
+ ctor public SeatTemperatureProfile.Builder(java.util.Map<java.util.Set<androidx.car.app.hardware.common.CarZone!>!,android.util.Pair<java.lang.Integer!,java.lang.Integer!>!>);
+ method public androidx.car.app.hardware.climate.SeatTemperatureProfile build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class SeatVentilationProfile {
+ method public java.util.Map<java.util.Set<androidx.car.app.hardware.common.CarZone!>!,android.util.Pair<java.lang.Integer!,java.lang.Integer!>!> getCarZoneSetsToSeatVentilationValues();
+ }
+
+ public static final class SeatVentilationProfile.Builder {
+ ctor public SeatVentilationProfile.Builder(java.util.Map<java.util.Set<androidx.car.app.hardware.common.CarZone!>!,android.util.Pair<java.lang.Integer!,java.lang.Integer!>!>);
+ method public androidx.car.app.hardware.climate.SeatVentilationProfile build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public final class SteeringWheelHeatProfile {
+ method public java.util.Map<java.util.Set<androidx.car.app.hardware.common.CarZone!>!,android.util.Pair<java.lang.Integer!,java.lang.Integer!>!> getCarZoneSetsToSteeringWheelHeatValues();
+ }
+
+ public static final class SteeringWheelHeatProfile.Builder {
+ ctor public SteeringWheelHeatProfile.Builder(java.util.Map<java.util.Set<androidx.car.app.hardware.common.CarZone!>!,android.util.Pair<java.lang.Integer!,java.lang.Integer!>!>);
+ method public androidx.car.app.hardware.climate.SteeringWheelHeatProfile build();
+ }
+
+}
+
+package androidx.car.app.hardware.common {
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(5) public interface CarSetOperationStatusCallback {
+ method public default void onSetCarClimateStateCabinTemperature(int);
+ method public default void onSetCarClimateStateDefroster(int);
+ method public default void onSetCarClimateStateElectricDefroster(int);
+ method public default void onSetCarClimateStateFanDirection(int);
+ method public default void onSetCarClimateStateFanSpeedLevel(int);
+ method public default void onSetCarClimateStateHvacAc(int);
+ method public default void onSetCarClimateStateHvacAutoMode(int);
+ method public default void onSetCarClimateStateHvacAutoRecirculation(int);
+ method public default void onSetCarClimateStateHvacDualMode(int);
+ method public default void onSetCarClimateStateHvacMaxAcMode(int);
+ method public default void onSetCarClimateStateHvacPower(int);
+ method public default void onSetCarClimateStateHvacRecirculation(int);
+ method public default void onSetCarClimateStateMaxDefroster(int);
+ method public default void onSetCarClimateStateSeatTemperatureLevel(int);
+ method public default void onSetCarClimateStateSeatVentilationLevel(int);
+ method public default void onSetCarClimateStateSteeringWheelHeat(int);
+ method public static String toString(int);
+ field public static final int OPERATION_STATUS_FEATURE_SETTING_NOT_ALLOWED = 4; // 0x4
+ field public static final int OPERATION_STATUS_FEATURE_TEMPORARILY_UNAVAILABLE = 3; // 0x3
+ field public static final int OPERATION_STATUS_FEATURE_UNIMPLEMENTED = 1; // 0x1
+ field public static final int OPERATION_STATUS_FEATURE_UNSUPPORTED = 2; // 0x2
+ field public static final int OPERATION_STATUS_ILLEGAL_CAR_HARDWARE_STATE = 7; // 0x7
+ field public static final int OPERATION_STATUS_INSUFFICIENT_PERMISSION = 6; // 0x6
+ field public static final int OPERATION_STATUS_SUCCESS = 0; // 0x0
+ field public static final int OPERATION_STATUS_UNSUPPORTED_VALUE = 5; // 0x5
+ field public static final int OPERATION_STATUS_UPDATE_TIMEOUT = 8; // 0x8
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class CarUnit {
+ method public static String toString(int);
+ field @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public static final int IMPERIAL_GALLON = 204; // 0xcc
+ field public static final int KILOMETER = 3; // 0x3
+ field public static final int KILOMETERS_PER_HOUR = 102; // 0x66
+ field @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public static final int LITER = 202; // 0xca
+ field public static final int METER = 2; // 0x2
+ field public static final int METERS_PER_SEC = 101; // 0x65
+ field public static final int MILE = 4; // 0x4
+ field public static final int MILES_PER_HOUR = 103; // 0x67
+ field @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public static final int MILLILITER = 201; // 0xc9
+ field public static final int MILLIMETER = 1; // 0x1
+ field @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public static final int US_GALLON = 203; // 0xcb
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class CarValue<T> {
+ ctor public CarValue(T?, long, int);
+ ctor @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public CarValue(T?, long, int, java.util.List<androidx.car.app.hardware.common.CarZone!>);
+ method @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public java.util.List<androidx.car.app.hardware.common.CarZone!> getCarZones();
+ method public int getStatus();
+ method public long getTimestampMillis();
+ method public T? getValue();
+ field public static final int STATUS_SUCCESS = 1; // 0x1
+ field public static final int STATUS_UNAVAILABLE = 3; // 0x3
+ field public static final int STATUS_UNIMPLEMENTED = 2; // 0x2
+ field public static final int STATUS_UNKNOWN = 0; // 0x0
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(5) public final class CarZone {
+ method public int getColumn();
+ method public int getRow();
+ field public static final int CAR_ZONE_COLUMN_ALL = 16; // 0x10
+ field public static final int CAR_ZONE_COLUMN_CENTER = 48; // 0x30
+ field public static final int CAR_ZONE_COLUMN_DRIVER = 80; // 0x50
+ field public static final int CAR_ZONE_COLUMN_LEFT = 32; // 0x20
+ field public static final int CAR_ZONE_COLUMN_PASSENGER = 96; // 0x60
+ field public static final int CAR_ZONE_COLUMN_RIGHT = 64; // 0x40
+ field public static final androidx.car.app.hardware.common.CarZone CAR_ZONE_GLOBAL;
+ field public static final int CAR_ZONE_ROW_ALL = 0; // 0x0
+ field public static final int CAR_ZONE_ROW_EXCLUDE_FIRST = 4; // 0x4
+ field public static final int CAR_ZONE_ROW_FIRST = 1; // 0x1
+ field public static final int CAR_ZONE_ROW_SECOND = 2; // 0x2
+ field public static final int CAR_ZONE_ROW_THIRD = 3; // 0x3
+ }
+
+ public static final class CarZone.Builder {
+ ctor public CarZone.Builder();
+ method public androidx.car.app.hardware.common.CarZone build();
+ method public androidx.car.app.hardware.common.CarZone.Builder setColumn(int);
+ method public androidx.car.app.hardware.common.CarZone.Builder setRow(int);
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public interface OnCarDataAvailableListener<T> {
+ method public void onCarDataAvailable(T);
+ }
+
+}
+
+package androidx.car.app.hardware.info {
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Accelerometer {
+ ctor public Accelerometer(androidx.car.app.hardware.common.CarValue<java.util.List<java.lang.Float!>!>);
+ method public androidx.car.app.hardware.common.CarValue<java.util.List<java.lang.Float!>!> getForces();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class CarHardwareLocation {
+ ctor public CarHardwareLocation(androidx.car.app.hardware.common.CarValue<android.location.Location!>);
+ method public androidx.car.app.hardware.common.CarValue<android.location.Location!> getLocation();
+ }
+
+ @MainThread @androidx.car.app.annotations.RequiresCarApi(3) public interface CarInfo {
+ method public void addEnergyLevelListener(java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataAvailableListener<androidx.car.app.hardware.info.EnergyLevel!>);
+ method @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public void addEvStatusListener(java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataAvailableListener<androidx.car.app.hardware.info.EvStatus!>);
+ method public void addMileageListener(java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataAvailableListener<androidx.car.app.hardware.info.Mileage!>);
+ method public void addSpeedListener(java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataAvailableListener<androidx.car.app.hardware.info.Speed!>);
+ method public void addTollListener(java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataAvailableListener<androidx.car.app.hardware.info.TollCard!>);
+ method public void fetchEnergyProfile(java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataAvailableListener<androidx.car.app.hardware.info.EnergyProfile!>);
+ method public void fetchModel(java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataAvailableListener<androidx.car.app.hardware.info.Model!>);
+ method public void removeEnergyLevelListener(androidx.car.app.hardware.common.OnCarDataAvailableListener<androidx.car.app.hardware.info.EnergyLevel!>);
+ method @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public void removeEvStatusListener(androidx.car.app.hardware.common.OnCarDataAvailableListener<androidx.car.app.hardware.info.EvStatus!>);
+ method public void removeMileageListener(androidx.car.app.hardware.common.OnCarDataAvailableListener<androidx.car.app.hardware.info.Mileage!>);
+ method public void removeSpeedListener(androidx.car.app.hardware.common.OnCarDataAvailableListener<androidx.car.app.hardware.info.Speed!>);
+ method public void removeTollListener(androidx.car.app.hardware.common.OnCarDataAvailableListener<androidx.car.app.hardware.info.TollCard!>);
+ }
+
+ @MainThread @androidx.car.app.annotations.RequiresCarApi(3) public interface CarSensors {
+ method public void addAccelerometerListener(int, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataAvailableListener<androidx.car.app.hardware.info.Accelerometer!>);
+ method public void addCarHardwareLocationListener(int, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataAvailableListener<androidx.car.app.hardware.info.CarHardwareLocation!>);
+ method public void addCompassListener(int, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataAvailableListener<androidx.car.app.hardware.info.Compass!>);
+ method public void addGyroscopeListener(int, java.util.concurrent.Executor, androidx.car.app.hardware.common.OnCarDataAvailableListener<androidx.car.app.hardware.info.Gyroscope!>);
+ method public void removeAccelerometerListener(androidx.car.app.hardware.common.OnCarDataAvailableListener<androidx.car.app.hardware.info.Accelerometer!>);
+ method public void removeCarHardwareLocationListener(androidx.car.app.hardware.common.OnCarDataAvailableListener<androidx.car.app.hardware.info.CarHardwareLocation!>);
+ method public void removeCompassListener(androidx.car.app.hardware.common.OnCarDataAvailableListener<androidx.car.app.hardware.info.Compass!>);
+ method public void removeGyroscopeListener(androidx.car.app.hardware.common.OnCarDataAvailableListener<androidx.car.app.hardware.info.Gyroscope!>);
+ field public static final int UPDATE_RATE_FASTEST = 3; // 0x3
+ field public static final int UPDATE_RATE_NORMAL = 1; // 0x1
+ field public static final int UPDATE_RATE_UI = 2; // 0x2
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Compass {
+ ctor public Compass(androidx.car.app.hardware.common.CarValue<java.util.List<java.lang.Float!>!>);
+ method public androidx.car.app.hardware.common.CarValue<java.util.List<java.lang.Float!>!> getOrientations();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class EnergyLevel {
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getBatteryPercent();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Integer!> getDistanceDisplayUnit();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Boolean!> getEnergyIsLow();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getFuelPercent();
+ method @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public androidx.car.app.hardware.common.CarValue<java.lang.Integer!> getFuelVolumeDisplayUnit();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getRangeRemainingMeters();
+ }
+
+ public static final class EnergyLevel.Builder {
+ ctor public EnergyLevel.Builder();
+ method public androidx.car.app.hardware.info.EnergyLevel build();
+ method public androidx.car.app.hardware.info.EnergyLevel.Builder setBatteryPercent(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+ method public androidx.car.app.hardware.info.EnergyLevel.Builder setDistanceDisplayUnit(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+ method public androidx.car.app.hardware.info.EnergyLevel.Builder setEnergyIsLow(androidx.car.app.hardware.common.CarValue<java.lang.Boolean!>);
+ method public androidx.car.app.hardware.info.EnergyLevel.Builder setFuelPercent(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+ method @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public androidx.car.app.hardware.info.EnergyLevel.Builder setFuelVolumeDisplayUnit(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+ method public androidx.car.app.hardware.info.EnergyLevel.Builder setRangeRemainingMeters(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class EnergyProfile {
+ method public androidx.car.app.hardware.common.CarValue<java.util.List<java.lang.Integer!>!> getEvConnectorTypes();
+ method public androidx.car.app.hardware.common.CarValue<java.util.List<java.lang.Integer!>!> getFuelTypes();
+ field public static final int EVCONNECTOR_TYPE_CHADEMO = 3; // 0x3
+ field public static final int EVCONNECTOR_TYPE_COMBO_1 = 4; // 0x4
+ field public static final int EVCONNECTOR_TYPE_COMBO_2 = 5; // 0x5
+ field public static final int EVCONNECTOR_TYPE_GBT = 9; // 0x9
+ field public static final int EVCONNECTOR_TYPE_GBT_DC = 10; // 0xa
+ field public static final int EVCONNECTOR_TYPE_J1772 = 1; // 0x1
+ field public static final int EVCONNECTOR_TYPE_MENNEKES = 2; // 0x2
+ field public static final int EVCONNECTOR_TYPE_OTHER = 101; // 0x65
+ field public static final int EVCONNECTOR_TYPE_SCAME = 11; // 0xb
+ field public static final int EVCONNECTOR_TYPE_TESLA_HPWC = 7; // 0x7
+ field public static final int EVCONNECTOR_TYPE_TESLA_ROADSTER = 6; // 0x6
+ field public static final int EVCONNECTOR_TYPE_TESLA_SUPERCHARGER = 8; // 0x8
+ field public static final int EVCONNECTOR_TYPE_UNKNOWN = 0; // 0x0
+ field public static final int FUEL_TYPE_BIODIESEL = 5; // 0x5
+ field public static final int FUEL_TYPE_CNG = 8; // 0x8
+ field public static final int FUEL_TYPE_DIESEL_1 = 3; // 0x3
+ field public static final int FUEL_TYPE_DIESEL_2 = 4; // 0x4
+ field public static final int FUEL_TYPE_E85 = 6; // 0x6
+ field public static final int FUEL_TYPE_ELECTRIC = 10; // 0xa
+ field public static final int FUEL_TYPE_HYDROGEN = 11; // 0xb
+ field public static final int FUEL_TYPE_LEADED = 2; // 0x2
+ field public static final int FUEL_TYPE_LNG = 9; // 0x9
+ field public static final int FUEL_TYPE_LPG = 7; // 0x7
+ field public static final int FUEL_TYPE_OTHER = 12; // 0xc
+ field public static final int FUEL_TYPE_UNKNOWN = 0; // 0x0
+ field public static final int FUEL_TYPE_UNLEADED = 1; // 0x1
+ }
+
+ public static final class EnergyProfile.Builder {
+ ctor public EnergyProfile.Builder();
+ method public androidx.car.app.hardware.info.EnergyProfile build();
+ method public androidx.car.app.hardware.info.EnergyProfile.Builder setEvConnectorTypes(androidx.car.app.hardware.common.CarValue<java.util.List<java.lang.Integer!>!>);
+ method public androidx.car.app.hardware.info.EnergyProfile.Builder setFuelTypes(androidx.car.app.hardware.common.CarValue<java.util.List<java.lang.Integer!>!>);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi public class EvStatus {
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Boolean!> getEvChargePortConnected();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Boolean!> getEvChargePortOpen();
+ }
+
+ public static final class EvStatus.Builder {
+ ctor public EvStatus.Builder();
+ method public androidx.car.app.hardware.info.EvStatus build();
+ method public androidx.car.app.hardware.info.EvStatus.Builder setEvChargePortConnected(androidx.car.app.hardware.common.CarValue<java.lang.Boolean!>);
+ method public androidx.car.app.hardware.info.EvStatus.Builder setEvChargePortOpen(androidx.car.app.hardware.common.CarValue<java.lang.Boolean!>);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Gyroscope {
+ ctor public Gyroscope(androidx.car.app.hardware.common.CarValue<java.util.List<java.lang.Float!>!>);
+ method public androidx.car.app.hardware.common.CarValue<java.util.List<java.lang.Float!>!> getRotations();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Mileage {
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Integer!> getDistanceDisplayUnit();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getOdometerMeters();
+ }
+
+ public static final class Mileage.Builder {
+ ctor public Mileage.Builder();
+ method public androidx.car.app.hardware.info.Mileage build();
+ method public androidx.car.app.hardware.info.Mileage.Builder setDistanceDisplayUnit(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+ method public androidx.car.app.hardware.info.Mileage.Builder setOdometerMeters(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Model {
+ method public androidx.car.app.hardware.common.CarValue<java.lang.String!> getManufacturer();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.String!> getName();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Integer!> getYear();
+ }
+
+ public static final class Model.Builder {
+ ctor public Model.Builder();
+ method public androidx.car.app.hardware.info.Model build();
+ method public androidx.car.app.hardware.info.Model.Builder setManufacturer(androidx.car.app.hardware.common.CarValue<java.lang.String!>);
+ method public androidx.car.app.hardware.info.Model.Builder setName(androidx.car.app.hardware.common.CarValue<java.lang.String!>);
+ method public androidx.car.app.hardware.info.Model.Builder setYear(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class Speed {
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getDisplaySpeedMetersPerSecond();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Float!> getRawSpeedMetersPerSecond();
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Integer!> getSpeedDisplayUnit();
+ }
+
+ public static final class Speed.Builder {
+ ctor public Speed.Builder();
+ method public androidx.car.app.hardware.info.Speed build();
+ method public androidx.car.app.hardware.info.Speed.Builder setDisplaySpeedMetersPerSecond(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+ method public androidx.car.app.hardware.info.Speed.Builder setRawSpeedMetersPerSecond(androidx.car.app.hardware.common.CarValue<java.lang.Float!>);
+ method public androidx.car.app.hardware.info.Speed.Builder setSpeedDisplayUnit(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(3) public final class TollCard {
+ method public androidx.car.app.hardware.common.CarValue<java.lang.Integer!> getCardState();
+ field public static final int TOLLCARD_STATE_INVALID = 2; // 0x2
+ field public static final int TOLLCARD_STATE_NOT_INSERTED = 3; // 0x3
+ field public static final int TOLLCARD_STATE_UNKNOWN = 0; // 0x0
+ field public static final int TOLLCARD_STATE_VALID = 1; // 0x1
+ }
+
+ public static final class TollCard.Builder {
+ ctor public TollCard.Builder();
+ method public androidx.car.app.hardware.info.TollCard build();
+ method public androidx.car.app.hardware.info.TollCard.Builder setCardState(androidx.car.app.hardware.common.CarValue<java.lang.Integer!>);
+ }
+
+}
+
+package androidx.car.app.managers {
+
+ public interface Manager {
+ }
+
+}
+
+package androidx.car.app.media {
+
+ @androidx.car.app.annotations.RequiresCarApi(5) public interface CarAudioCallback {
+ method public void onStopRecording();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(5) public class CarAudioCallbackDelegate {
+ method public void onStopRecording();
+ }
+
+ @androidx.car.app.annotations.RequiresCarApi(5) public abstract class CarAudioRecord {
+ method @RequiresPermission(android.Manifest.permission.RECORD_AUDIO) public static androidx.car.app.media.CarAudioRecord create(androidx.car.app.CarContext);
+ method public int read(byte[], int, int);
+ method public void startRecording();
+ method public void stopRecording();
+ field public static final int AUDIO_CONTENT_BUFFER_SIZE = 512; // 0x200
+ field public static final String AUDIO_CONTENT_MIME = "audio/l16";
+ field public static final int AUDIO_CONTENT_SAMPLING_RATE = 16000; // 0x3e80
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(5) public final class OpenMicrophoneRequest {
+ method public androidx.car.app.media.CarAudioCallbackDelegate getCarAudioCallbackDelegate();
+ }
+
+ public static final class OpenMicrophoneRequest.Builder {
+ ctor public OpenMicrophoneRequest.Builder(androidx.car.app.media.CarAudioCallback);
+ method public androidx.car.app.media.OpenMicrophoneRequest build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(5) public final class OpenMicrophoneResponse {
+ method public androidx.car.app.media.CarAudioCallbackDelegate getCarAudioCallback();
+ method public java.io.InputStream getCarMicrophoneInputStream();
+ }
+
+ public static final class OpenMicrophoneResponse.Builder {
+ ctor public OpenMicrophoneResponse.Builder(androidx.car.app.media.CarAudioCallback);
+ method public androidx.car.app.media.OpenMicrophoneResponse build();
+ method public androidx.car.app.media.OpenMicrophoneResponse.Builder setCarMicrophoneDescriptor(android.os.ParcelFileDescriptor);
+ }
+
+}
+
+package androidx.car.app.mediaextensions {
+
+ public final class MetadataExtras {
+ field public static final String KEY_CONTENT_FORMAT_DARK_MODE_LARGE_ICON_URI = "androidx.car.app.mediaextensions.KEY_CONTENT_FORMAT_DARK_MODE_LARGE_ICON_URI";
+ field public static final String KEY_CONTENT_FORMAT_DARK_MODE_SMALL_ICON_URI = "androidx.car.app.mediaextensions.KEY_CONTENT_FORMAT_DARK_MODE_SMALL_ICON_URI";
+ field public static final String KEY_CONTENT_FORMAT_LIGHT_MODE_LARGE_ICON_URI = "androidx.car.app.mediaextensions.KEY_CONTENT_FORMAT_LIGHT_MODE_LARGE_ICON_URI";
+ field public static final String KEY_CONTENT_FORMAT_LIGHT_MODE_SMALL_ICON_URI = "androidx.car.app.mediaextensions.KEY_CONTENT_FORMAT_LIGHT_MODE_SMALL_ICON_URI";
+ field public static final String KEY_CONTENT_FORMAT_TINTABLE_LARGE_ICON_URI = "androidx.car.app.mediaextensions.KEY_CONTENT_FORMAT_TINTABLE_LARGE_ICON_URI";
+ field public static final String KEY_CONTENT_FORMAT_TINTABLE_SMALL_ICON_URI = "androidx.car.app.mediaextensions.KEY_CONTENT_FORMAT_TINTABLE_SMALL_ICON_URI";
+ field public static final String KEY_DESCRIPTION_LINK_MEDIA_ID = "androidx.car.app.mediaextensions.KEY_DESCRIPTION_LINK_MEDIA_ID";
+ field public static final String KEY_IMMERSIVE_AUDIO = "androidx.car.app.mediaextensions.KEY_IMMERSIVE_AUDIO";
+ field public static final String KEY_SUBTITLE_LINK_MEDIA_ID = "androidx.car.app.mediaextensions.KEY_SUBTITLE_LINK_MEDIA_ID";
+ }
+
+}
+
+package androidx.car.app.messaging {
+
+ @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public class MessagingServiceConstants {
+ field public static final String ACTION_HANDLE_CAR_MESSAGING = "androidx.car.app.messaging.action.HANDLE_CAR_MESSAGING";
+ }
+
+}
+
+package androidx.car.app.messaging.model {
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public class CarMessage {
+ method public androidx.car.app.model.CarText? getBody();
+ method public String? getMultimediaMimeType();
+ method public android.net.Uri? getMultimediaUri();
+ method public long getReceivedTimeEpochMillis();
+ method public androidx.core.app.Person? getSender();
+ method public boolean isRead();
+ }
+
+ public static final class CarMessage.Builder {
+ ctor public CarMessage.Builder();
+ method public androidx.car.app.messaging.model.CarMessage build();
+ method public androidx.car.app.messaging.model.CarMessage.Builder setBody(androidx.car.app.model.CarText?);
+ method public androidx.car.app.messaging.model.CarMessage.Builder setMultimediaMimeType(String?);
+ method public androidx.car.app.messaging.model.CarMessage.Builder setMultimediaUri(android.net.Uri?);
+ method public androidx.car.app.messaging.model.CarMessage.Builder setRead(boolean);
+ method public androidx.car.app.messaging.model.CarMessage.Builder setReceivedTimeEpochMillis(long);
+ method public androidx.car.app.messaging.model.CarMessage.Builder setSender(androidx.core.app.Person?);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi public interface ConversationCallback {
+ method public void onMarkAsRead();
+ method public void onTextReply(String);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public interface ConversationCallbackDelegate {
+ method public void sendMarkAsRead(androidx.car.app.OnDoneCallback);
+ method public void sendTextReply(String, androidx.car.app.OnDoneCallback);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public class ConversationItem implements androidx.car.app.model.Item {
+ method public java.util.List<androidx.car.app.model.Action!> getActions();
+ method public androidx.car.app.messaging.model.ConversationCallbackDelegate getConversationCallbackDelegate();
+ method public androidx.car.app.model.CarIcon? getIcon();
+ method public String getId();
+ method public java.util.List<androidx.car.app.messaging.model.CarMessage!> getMessages();
+ method public androidx.core.app.Person getSelf();
+ method public androidx.car.app.model.CarText getTitle();
+ method public boolean isGroupConversation();
+ }
+
+ public static final class ConversationItem.Builder {
+ ctor public ConversationItem.Builder();
+ ctor public ConversationItem.Builder(androidx.car.app.messaging.model.ConversationItem);
+ method public androidx.car.app.messaging.model.ConversationItem.Builder addAction(androidx.car.app.model.Action);
+ method public androidx.car.app.messaging.model.ConversationItem build();
+ method public androidx.car.app.messaging.model.ConversationItem.Builder setConversationCallback(androidx.car.app.messaging.model.ConversationCallback);
+ method public androidx.car.app.messaging.model.ConversationItem.Builder setGroupConversation(boolean);
+ method public androidx.car.app.messaging.model.ConversationItem.Builder setIcon(androidx.car.app.model.CarIcon);
+ method public androidx.car.app.messaging.model.ConversationItem.Builder setId(String);
+ method public androidx.car.app.messaging.model.ConversationItem.Builder setMessages(java.util.List<androidx.car.app.messaging.model.CarMessage!>);
+ method public androidx.car.app.messaging.model.ConversationItem.Builder setSelf(androidx.core.app.Person);
+ method public androidx.car.app.messaging.model.ConversationItem.Builder setTitle(androidx.car.app.model.CarText);
+ }
+
+}
+
+package androidx.car.app.model {
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class Action {
+ method public androidx.car.app.model.CarColor? getBackgroundColor();
+ method @androidx.car.app.annotations.RequiresCarApi(4) public int getFlags();
+ method public androidx.car.app.model.CarIcon? getIcon();
+ method public androidx.car.app.model.OnClickDelegate? getOnClickDelegate();
+ method public androidx.car.app.model.CarText? getTitle();
+ method public int getType();
+ method @androidx.car.app.annotations.RequiresCarApi(5) public boolean isEnabled();
+ method public boolean isStandard();
+ method public static String typeToString(int);
+ field public static final androidx.car.app.model.Action APP_ICON;
+ field public static final androidx.car.app.model.Action BACK;
+ field @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public static final androidx.car.app.model.Action COMPOSE_MESSAGE;
+ field @androidx.car.app.annotations.RequiresCarApi(5) public static final int FLAG_DEFAULT = 4; // 0x4
+ field @androidx.car.app.annotations.RequiresCarApi(5) public static final int FLAG_IS_PERSISTENT = 2; // 0x2
+ field @androidx.car.app.annotations.RequiresCarApi(4) public static final int FLAG_PRIMARY = 1; // 0x1
+ field public static final androidx.car.app.model.Action PAN;
+ field public static final int TYPE_APP_ICON = 65538; // 0x10002
+ field public static final int TYPE_BACK = 65539; // 0x10003
+ field @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public static final int TYPE_COMPOSE_MESSAGE = 65541; // 0x10005
+ field public static final int TYPE_CUSTOM = 1; // 0x1
+ field public static final int TYPE_PAN = 65540; // 0x10004
+ }
+
+ public static final class Action.Builder {
+ ctor public Action.Builder();
+ ctor @androidx.car.app.annotations.RequiresCarApi(2) public Action.Builder(androidx.car.app.model.Action);
+ method public androidx.car.app.model.Action build();
+ method public androidx.car.app.model.Action.Builder setBackgroundColor(androidx.car.app.model.CarColor);
+ method @androidx.car.app.annotations.RequiresCarApi(5) public androidx.car.app.model.Action.Builder setEnabled(boolean);
+ method @androidx.car.app.annotations.RequiresCarApi(4) public androidx.car.app.model.Action.Builder setFlags(int);
+ method public androidx.car.app.model.Action.Builder setIcon(androidx.car.app.model.CarIcon);
+ method public androidx.car.app.model.Action.Builder setOnClickListener(androidx.car.app.model.OnClickListener);
+ method public androidx.car.app.model.Action.Builder setTitle(androidx.car.app.model.CarText);
+ method public androidx.car.app.model.Action.Builder setTitle(CharSequence);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class ActionStrip {
+ method public java.util.List<androidx.car.app.model.Action!> getActions();
+ method public androidx.car.app.model.Action? getFirstActionOfType(int);
+ }
+
+ public static final class ActionStrip.Builder {
+ ctor public ActionStrip.Builder();
+ method public androidx.car.app.model.ActionStrip.Builder addAction(androidx.car.app.model.Action);
+ method public androidx.car.app.model.ActionStrip build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(5) public final class Alert {
+ method public java.util.List<androidx.car.app.model.Action!> getActions();
+ method public androidx.car.app.model.AlertCallbackDelegate? getCallbackDelegate();
+ method public long getDurationMillis();
+ method public androidx.car.app.model.CarIcon? getIcon();
+ method public int getId();
+ method public androidx.car.app.model.CarText? getSubtitle();
+ method public androidx.car.app.model.CarText getTitle();
+ }
+
+ public static final class Alert.Builder {
+ ctor public Alert.Builder(int, androidx.car.app.model.CarText, long);
+ method public androidx.car.app.model.Alert.Builder addAction(androidx.car.app.model.Action);
+ method public androidx.car.app.model.Alert build();
+ method public androidx.car.app.model.Alert.Builder setCallback(androidx.car.app.model.AlertCallback);
+ method public androidx.car.app.model.Alert.Builder setIcon(androidx.car.app.model.CarIcon);
+ method public androidx.car.app.model.Alert.Builder setSubtitle(androidx.car.app.model.CarText);
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(5) public interface AlertCallback {
+ method public void onCancel(int);
+ method public void onDismiss();
+ field public static final int REASON_NOT_SUPPORTED = 3; // 0x3
+ field public static final int REASON_TIMEOUT = 1; // 0x1
+ field public static final int REASON_USER_ACTION = 2; // 0x2
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(5) public interface AlertCallbackDelegate {
+ method public void sendCancel(int, androidx.car.app.OnDoneCallback);
+ method public void sendDismiss(androidx.car.app.OnDoneCallback);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public class Badge {
+ method public androidx.car.app.model.CarColor? getBackgroundColor();
+ method public androidx.car.app.model.CarIcon? getIcon();
+ method public boolean hasDot();
+ }
+
+ public static final class Badge.Builder {
+ ctor public Badge.Builder();
+ method public androidx.car.app.model.Badge build();
+ method public androidx.car.app.model.Badge.Builder setBackgroundColor(androidx.car.app.model.CarColor);
+ method public androidx.car.app.model.Badge.Builder setHasDot(boolean);
+ method public androidx.car.app.model.Badge.Builder setIcon(androidx.car.app.model.CarIcon);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class CarColor {
+ method public static androidx.car.app.model.CarColor createCustom(@ColorInt int, @ColorInt int);
+ method @ColorInt public int getColor();
+ method @ColorInt public int getColorDark();
+ method public int getType();
+ field public static final androidx.car.app.model.CarColor BLUE;
+ field public static final androidx.car.app.model.CarColor DEFAULT;
+ field public static final androidx.car.app.model.CarColor GREEN;
+ field public static final androidx.car.app.model.CarColor PRIMARY;
+ field public static final androidx.car.app.model.CarColor RED;
+ field public static final androidx.car.app.model.CarColor SECONDARY;
+ field public static final int TYPE_BLUE = 6; // 0x6
+ field public static final int TYPE_CUSTOM = 0; // 0x0
+ field public static final int TYPE_DEFAULT = 1; // 0x1
+ field public static final int TYPE_GREEN = 5; // 0x5
+ field public static final int TYPE_PRIMARY = 2; // 0x2
+ field public static final int TYPE_RED = 4; // 0x4
+ field public static final int TYPE_SECONDARY = 3; // 0x3
+ field public static final int TYPE_YELLOW = 7; // 0x7
+ field public static final androidx.car.app.model.CarColor YELLOW;
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class CarIcon {
+ method public androidx.core.graphics.drawable.IconCompat? getIcon();
+ method public androidx.car.app.model.CarColor? getTint();
+ method public int getType();
+ field public static final androidx.car.app.model.CarIcon ALERT;
+ field public static final androidx.car.app.model.CarIcon APP_ICON;
+ field public static final androidx.car.app.model.CarIcon BACK;
+ field @androidx.car.app.annotations.RequiresCarApi(7) public static final androidx.car.app.model.CarIcon COMPOSE_MESSAGE;
+ field public static final androidx.car.app.model.CarIcon ERROR;
+ field @androidx.car.app.annotations.RequiresCarApi(2) public static final androidx.car.app.model.CarIcon PAN;
+ field public static final int TYPE_ALERT = 4; // 0x4
+ field public static final int TYPE_APP_ICON = 5; // 0x5
+ field public static final int TYPE_BACK = 3; // 0x3
+ field public static final int TYPE_COMPOSE_MESSAGE = 8; // 0x8
+ field public static final int TYPE_CUSTOM = 1; // 0x1
+ field public static final int TYPE_ERROR = 6; // 0x6
+ field public static final int TYPE_PAN = 7; // 0x7
+ }
+
+ public static final class CarIcon.Builder {
+ ctor public CarIcon.Builder(androidx.car.app.model.CarIcon);
+ ctor public CarIcon.Builder(androidx.core.graphics.drawable.IconCompat);
+ method public androidx.car.app.model.CarIcon build();
+ method public androidx.car.app.model.CarIcon.Builder setTint(androidx.car.app.model.CarColor);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class CarIconSpan extends androidx.car.app.model.CarSpan {
+ method public static androidx.car.app.model.CarIconSpan create(androidx.car.app.model.CarIcon);
+ method public static androidx.car.app.model.CarIconSpan create(androidx.car.app.model.CarIcon, int);
+ method public int getAlignment();
+ method public androidx.car.app.model.CarIcon getIcon();
+ field public static final int ALIGN_BASELINE = 1; // 0x1
+ field public static final int ALIGN_BOTTOM = 0; // 0x0
+ field public static final int ALIGN_CENTER = 2; // 0x2
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class CarLocation {
+ method public static androidx.car.app.model.CarLocation create(android.location.Location);
+ method public static androidx.car.app.model.CarLocation create(double, double);
+ method public double getLatitude();
+ method public double getLongitude();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public class CarSpan extends android.text.style.CharacterStyle {
+ ctor public CarSpan();
+ method public void updateDrawState(android.text.TextPaint);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class CarText {
+ method public static androidx.car.app.model.CarText create(CharSequence);
+ method public java.util.List<java.lang.CharSequence!> getVariants();
+ method public boolean isEmpty();
+ method public static boolean isNullOrEmpty(androidx.car.app.model.CarText?);
+ method public CharSequence toCharSequence();
+ }
+
+ @SuppressCompatibility public static final class CarText.Builder {
+ ctor public CarText.Builder(CharSequence);
+ method @androidx.car.app.annotations.RequiresCarApi(2) public androidx.car.app.model.CarText.Builder addVariant(CharSequence);
+ method public androidx.car.app.model.CarText build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(2) public final class ClickableSpan extends androidx.car.app.model.CarSpan {
+ method public static androidx.car.app.model.ClickableSpan create(androidx.car.app.model.OnClickListener);
+ method public androidx.car.app.model.OnClickDelegate getOnClickDelegate();
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public interface Content {
+ method public String getContentId();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class DateTimeWithZone {
+ method @RequiresApi(26) public static androidx.car.app.model.DateTimeWithZone create(java.time.ZonedDateTime);
+ method public static androidx.car.app.model.DateTimeWithZone create(long, @IntRange(from=0xffff02e0, to=64800) int, String);
+ method public static androidx.car.app.model.DateTimeWithZone create(long, java.util.TimeZone);
+ method public long getTimeSinceEpochMillis();
+ method public int getZoneOffsetSeconds();
+ method public String? getZoneShortName();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class Distance {
+ method public static androidx.car.app.model.Distance create(double, int);
+ method public double getDisplayDistance();
+ method public int getDisplayUnit();
+ field public static final int UNIT_FEET = 6; // 0x6
+ field public static final int UNIT_KILOMETERS = 2; // 0x2
+ field public static final int UNIT_KILOMETERS_P1 = 3; // 0x3
+ field public static final int UNIT_METERS = 1; // 0x1
+ field public static final int UNIT_MILES = 4; // 0x4
+ field public static final int UNIT_MILES_P1 = 5; // 0x5
+ field public static final int UNIT_YARDS = 7; // 0x7
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class DistanceSpan extends androidx.car.app.model.CarSpan {
+ method public static androidx.car.app.model.DistanceSpan create(androidx.car.app.model.Distance);
+ method public androidx.car.app.model.Distance getDistance();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class DurationSpan extends androidx.car.app.model.CarSpan {
+ method @RequiresApi(26) public static androidx.car.app.model.DurationSpan create(java.time.Duration);
+ method public static androidx.car.app.model.DurationSpan create(long);
+ method public long getDurationSeconds();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class ForegroundCarColorSpan extends androidx.car.app.model.CarSpan {
+ method public static androidx.car.app.model.ForegroundCarColorSpan create(androidx.car.app.model.CarColor);
+ method public androidx.car.app.model.CarColor getColor();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class GridItem implements androidx.car.app.model.Item {
+ method @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public androidx.car.app.model.Badge? getBadge();
+ method public androidx.car.app.model.CarIcon? getImage();
+ method public int getImageType();
+ method public androidx.car.app.model.OnClickDelegate? getOnClickDelegate();
+ method public androidx.car.app.model.CarText? getText();
+ method public androidx.car.app.model.CarText? getTitle();
+ method public boolean isLoading();
+ field public static final int IMAGE_TYPE_ICON = 1; // 0x1
+ field public static final int IMAGE_TYPE_LARGE = 2; // 0x2
+ }
+
+ public static final class GridItem.Builder {
+ ctor public GridItem.Builder();
+ method public androidx.car.app.model.GridItem build();
+ method public androidx.car.app.model.GridItem.Builder setImage(androidx.car.app.model.CarIcon);
+ method @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public androidx.car.app.model.GridItem.Builder setImage(androidx.car.app.model.CarIcon, androidx.car.app.model.Badge);
+ method public androidx.car.app.model.GridItem.Builder setImage(androidx.car.app.model.CarIcon, int);
+ method @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public androidx.car.app.model.GridItem.Builder setImage(androidx.car.app.model.CarIcon, int, androidx.car.app.model.Badge);
+ method public androidx.car.app.model.GridItem.Builder setLoading(boolean);
+ method public androidx.car.app.model.GridItem.Builder setOnClickListener(androidx.car.app.model.OnClickListener);
+ method public androidx.car.app.model.GridItem.Builder setText(androidx.car.app.model.CarText);
+ method public androidx.car.app.model.GridItem.Builder setText(CharSequence);
+ method public androidx.car.app.model.GridItem.Builder setTitle(androidx.car.app.model.CarText);
+ method public androidx.car.app.model.GridItem.Builder setTitle(CharSequence);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class GridTemplate implements androidx.car.app.model.Template {
+ method public androidx.car.app.model.ActionStrip? getActionStrip();
+ method @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public java.util.List<androidx.car.app.model.Action!> getActions();
+ method public androidx.car.app.model.Action? getHeaderAction();
+ method @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public int getItemImageShape();
+ method @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public int getItemSize();
+ method public androidx.car.app.model.ItemList? getSingleList();
+ method public androidx.car.app.model.CarText? getTitle();
+ method public boolean isLoading();
+ field @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public static final int ITEM_IMAGE_SHAPE_CIRCLE = 2; // 0x2
+ field @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public static final int ITEM_IMAGE_SHAPE_UNSET = 1; // 0x1
+ field @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public static final int ITEM_SIZE_LARGE = 4; // 0x4
+ field @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public static final int ITEM_SIZE_MEDIUM = 2; // 0x2
+ field @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public static final int ITEM_SIZE_SMALL = 1; // 0x1
+ }
+
+ public static final class GridTemplate.Builder {
+ ctor public GridTemplate.Builder();
+ method @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public androidx.car.app.model.GridTemplate.Builder addAction(androidx.car.app.model.Action);
+ method public androidx.car.app.model.GridTemplate build();
+ method public androidx.car.app.model.GridTemplate.Builder setActionStrip(androidx.car.app.model.ActionStrip);
+ method public androidx.car.app.model.GridTemplate.Builder setHeaderAction(androidx.car.app.model.Action);
+ method @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public androidx.car.app.model.GridTemplate.Builder setItemImageShape(@SuppressCompatibility int);
+ method @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public androidx.car.app.model.GridTemplate.Builder setItemSize(@SuppressCompatibility int);
+ method public androidx.car.app.model.GridTemplate.Builder setLoading(boolean);
+ method public androidx.car.app.model.GridTemplate.Builder setSingleList(androidx.car.app.model.ItemList);
+ method public androidx.car.app.model.GridTemplate.Builder setTitle(CharSequence);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(5) public final class Header {
+ method public java.util.List<androidx.car.app.model.Action!> getEndHeaderActions();
+ method public androidx.car.app.model.Action? getStartHeaderAction();
+ method public androidx.car.app.model.CarText? getTitle();
+ }
+
+ public static final class Header.Builder {
+ ctor public Header.Builder();
+ method public androidx.car.app.model.Header.Builder addEndHeaderAction(androidx.car.app.model.Action);
+ method public androidx.car.app.model.Header build();
+ method public androidx.car.app.model.Header.Builder setStartHeaderAction(androidx.car.app.model.Action);
+ method public androidx.car.app.model.Header.Builder setTitle(androidx.car.app.model.CarText);
+ method public androidx.car.app.model.Header.Builder setTitle(CharSequence);
+ }
+
+ @androidx.car.app.annotations.RequiresCarApi(2) public interface InputCallback {
+ method public default void onInputSubmitted(String);
+ method public default void onInputTextChanged(String);
+ }
+
+ @androidx.car.app.annotations.RequiresCarApi(2) public interface InputCallbackDelegate {
+ method public void sendInputSubmitted(String, androidx.car.app.OnDoneCallback);
+ method public void sendInputTextChanged(String, androidx.car.app.OnDoneCallback);
+ }
+
+ @androidx.car.app.annotations.CarProtocol public interface Item {
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class ItemList {
+ method public java.util.List<androidx.car.app.model.Item!> getItems();
+ method public androidx.car.app.model.CarText? getNoItemsMessage();
+ method public androidx.car.app.model.OnItemVisibilityChangedDelegate? getOnItemVisibilityChangedDelegate();
+ method public androidx.car.app.model.OnSelectedDelegate? getOnSelectedDelegate();
+ method public int getSelectedIndex();
+ method @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public androidx.car.app.model.ItemList.Builder toBuilder();
+ }
+
+ public static final class ItemList.Builder {
+ ctor public ItemList.Builder();
+ method public androidx.car.app.model.ItemList.Builder addItem(androidx.car.app.model.Item);
+ method public androidx.car.app.model.ItemList build();
+ method @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public androidx.car.app.model.ItemList.Builder clearItems();
+ method public androidx.car.app.model.ItemList.Builder setNoItemsMessage(CharSequence);
+ method public androidx.car.app.model.ItemList.Builder setOnItemsVisibilityChangedListener(androidx.car.app.model.ItemList.OnItemVisibilityChangedListener);
+ method public androidx.car.app.model.ItemList.Builder setOnSelectedListener(androidx.car.app.model.ItemList.OnSelectedListener);
+ method public androidx.car.app.model.ItemList.Builder setSelectedIndex(@IntRange(from=0) int);
+ }
+
+ public static interface ItemList.OnItemVisibilityChangedListener {
+ method public void onItemVisibilityChanged(int, int);
+ }
+
+ public static interface ItemList.OnSelectedListener {
+ method public void onSelected(int);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class ListTemplate implements androidx.car.app.model.Template {
+ method public androidx.car.app.model.ActionStrip? getActionStrip();
+ method @androidx.car.app.annotations.RequiresCarApi(6) public java.util.List<androidx.car.app.model.Action!> getActions();
+ method public androidx.car.app.model.Action? getHeaderAction();
+ method public java.util.List<androidx.car.app.model.SectionedItemList!> getSectionedLists();
+ method public androidx.car.app.model.ItemList? getSingleList();
+ method public androidx.car.app.model.CarText? getTitle();
+ method public boolean isLoading();
+ method @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public androidx.car.app.model.ListTemplate.Builder toBuilder();
+ }
+
+ public static final class ListTemplate.Builder {
+ ctor public ListTemplate.Builder();
+ method @androidx.car.app.annotations.RequiresCarApi(6) public androidx.car.app.model.ListTemplate.Builder addAction(androidx.car.app.model.Action);
+ method public androidx.car.app.model.ListTemplate.Builder addSectionedList(androidx.car.app.model.SectionedItemList);
+ method public androidx.car.app.model.ListTemplate build();
+ method @SuppressCompatibility @androidx.car.app.annotations.ExperimentalCarApi public androidx.car.app.model.ListTemplate.Builder clearSectionedLists();
+ method public androidx.car.app.model.ListTemplate.Builder setActionStrip(androidx.car.app.model.ActionStrip);
+ method public androidx.car.app.model.ListTemplate.Builder setHeaderAction(androidx.car.app.model.Action);
+ method public androidx.car.app.model.ListTemplate.Builder setLoading(boolean);
+ method public androidx.car.app.model.ListTemplate.Builder setSingleList(androidx.car.app.model.ItemList);
+ method public androidx.car.app.model.ListTemplate.Builder setTitle(CharSequence);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.RequiresCarApi(2) public final class LongMessageTemplate implements androidx.car.app.model.Template {
+ method public androidx.car.app.model.ActionStrip? getActionStrip();
+ method public java.util.List<androidx.car.app.model.Action!> getActions();
+ method public androidx.car.app.model.Action? getHeaderAction();
+ method public androidx.car.app.model.CarText getMessage();
+ method public androidx.car.app.model.CarText? getTitle();
+ }
+
+ @androidx.car.app.annotations.RequiresCarApi(2) public static final class LongMessageTemplate.Builder {
+ ctor public LongMessageTemplate.Builder(CharSequence);
+ method public androidx.car.app.model.LongMessageTemplate.Builder addAction(androidx.car.app.model.Action);
+ method public androidx.car.app.model.LongMessageTemplate build();
+ method public androidx.car.app.model.LongMessageTemplate.Builder setActionStrip(androidx.car.app.model.ActionStrip);
+ method public androidx.car.app.model.LongMessageTemplate.Builder setHeaderAction(androidx.car.app.model.Action);
+ method public androidx.car.app.model.LongMessageTemplate.Builder setTitle(CharSequence);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class MessageTemplate implements androidx.car.app.model.Template {
+ method @androidx.car.app.annotations.RequiresCarApi(2) public androidx.car.app.model.ActionStrip? getActionStrip();
+ method public java.util.List<androidx.car.app.model.Action!> getActions();
+ method public androidx.car.app.model.CarText? getDebugMessage();
+ method public androidx.car.app.model.Action? getHeaderAction();
+ method public androidx.car.app.model.CarIcon? getIcon();
+ method public androidx.car.app.model.CarText getMessage();
+ method public androidx.car.app.model.CarText? getTitle();
+ method @androidx.car.app.annotations.RequiresCarApi(2) public boolean isLoading();
+ }
+
+ public static final class MessageTemplate.Builder {
+ ctor public MessageTemplate.Builder(androidx.car.app.model.CarText);
+ ctor public MessageTemplate.Builder(CharSequence);
+ method public androidx.car.app.model.MessageTemplate.Builder addAction(androidx.car.app.model.Action);
+ method public androidx.car.app.model.MessageTemplate build();
+ method @androidx.car.app.annotations.RequiresCarApi(2) public androidx.car.app.model.MessageTemplate.Builder setActionStrip(androidx.car.app.model.ActionStrip);
+ method public androidx.car.app.model.MessageTemplate.Builder setDebugMessage(String);
+ method public androidx.car.app.model.MessageTemplate.Builder setDebugMessage(Throwable);
+ method public androidx.car.app.model.MessageTemplate.Builder setHeaderAction(androidx.car.app.model.Action);
+ method public androidx.car.app.model.MessageTemplate.Builder setIcon(androidx.car.app.model.CarIcon);
+ method @androidx.car.app.annotations.RequiresCarApi(2) public androidx.car.app.model.MessageTemplate.Builder setLoading(boolean);
+ method public androidx.car.app.model.MessageTemplate.Builder setTitle(CharSequence);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class Metadata {
+ method public androidx.car.app.model.Place? getPlace();
+ field public static final androidx.car.app.model.Metadata EMPTY_METADATA;
+ }
+
+ public static final class Metadata.Builder {
+ ctor public Metadata.Builder();
+ ctor public Metadata.Builder(androidx.car.app.model.Metadata);
+ method public androidx.car.app.model.Metadata build();
+ method public androidx.car.app.model.Metadata.Builder setPlace(androidx.car.app.model.Place);
+ }
+
+ @androidx.car.app.annotations.CarProtocol public interface OnCheckedChangeDelegate {
+ method public void sendCheckedChange(boolean, androidx.car.app.OnDoneCallback);
+ }
+
+ @androidx.car.app.annotations.CarProtocol public interface OnClickDelegate {
+ method public boolean isParkedOnly();
+ method public void sendClick(androidx.car.app.OnDoneCallback);
+ }
+
+ public interface OnClickListener {
+ method public void onClick();
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(5) public interface OnContentRefreshDelegate {
+ method public void sendContentRefreshRequested(androidx.car.app.OnDoneCallback);
+ }
+
+ @androidx.car.app.annotations.RequiresCarApi(5) public interface OnContentRefreshListener {
+ method public void onContentRefreshRequested();
+ }
+
+ @androidx.car.app.annotations.CarProtocol public interface OnItemVisibilityChangedDelegate {
+ method public void sendItemVisibilityChanged(int, int, androidx.car.app.OnDoneCallback);
+ }
+
+ @androidx.car.app.annotations.CarProtocol public interface OnSelectedDelegate {
+ method public void sendSelected(int, androidx.car.app.OnDoneCallback);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class Pane {
+ method public java.util.List<androidx.car.app.model.Action!> getActions();
+ method @androidx.car.app.annotations.RequiresCarApi(4) public androidx.car.app.model.CarIcon? getImage();
+ method public java.util.List<androidx.car.app.model.Row!> getRows();
+ method public boolean isLoading();
+ }
+
+ public static final class Pane.Builder {
+ ctor public Pane.Builder();
+ method public androidx.car.app.model.Pane.Builder addAction(androidx.car.app.model.Action);
+ method public androidx.car.app.model.Pane.Builder addRow(androidx.car.app.model.Row);
+ method public androidx.car.app.model.Pane build();
+ method @androidx.car.app.annotations.RequiresCarApi(4) public androidx.car.app.model.Pane.Builder setImage(androidx.car.app.model.CarIcon);
+ method public androidx.car.app.model.Pane.Builder setLoading(boolean);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class PaneTemplate implements androidx.car.app.model.Template {
+ method public androidx.car.app.model.ActionStrip? getActionStrip();
+ method public androidx.car.app.model.Action? getHeaderAction();
+ method public androidx.car.app.model.Pane getPane();
+ method public androidx.car.app.model.CarText? getTitle();
+ }
+
+ public static final class PaneTemplate.Builder {
+ ctor public PaneTemplate.Builder(androidx.car.app.model.Pane);
+ method public androidx.car.app.model.PaneTemplate build();
+ method public androidx.car.app.model.PaneTemplate.Builder setActionStrip(androidx.car.app.model.ActionStrip);
+ method public androidx.car.app.model.PaneTemplate.Builder setHeaderAction(androidx.car.app.model.Action);
+ method public androidx.car.app.model.PaneTemplate.Builder setTitle(CharSequence);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class ParkedOnlyOnClickListener implements androidx.car.app.model.OnClickListener {
+ method public static androidx.car.app.model.ParkedOnlyOnClickListener create(androidx.car.app.model.OnClickListener);
+ method public void onClick();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class Place {
+ method public androidx.car.app.model.CarLocation getLocation();
+ method public androidx.car.app.model.PlaceMarker? getMarker();
+ }
+
+ public static final class Place.Builder {
+ ctor public Place.Builder(androidx.car.app.model.CarLocation);
+ ctor public Place.Builder(androidx.car.app.model.Place);
+ method public androidx.car.app.model.Place build();
+ method public androidx.car.app.model.Place.Builder setMarker(androidx.car.app.model.PlaceMarker);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class PlaceListMapTemplate implements androidx.car.app.model.Template {
+ method public androidx.car.app.model.ActionStrip? getActionStrip();
+ method public androidx.car.app.model.Place? getAnchor();
+ method public androidx.car.app.model.Action? getHeaderAction();
+ method public androidx.car.app.model.ItemList? getItemList();
+ method @androidx.car.app.annotations.RequiresCarApi(5) public androidx.car.app.model.OnContentRefreshDelegate? getOnContentRefreshDelegate();
+ method public androidx.car.app.model.CarText? getTitle();
+ method public boolean isCurrentLocationEnabled();
+ method public boolean isLoading();
+ }
+
+ public static final class PlaceListMapTemplate.Builder {
+ ctor public PlaceListMapTemplate.Builder();
+ method public androidx.car.app.model.PlaceListMapTemplate build();
+ method public androidx.car.app.model.PlaceListMapTemplate.Builder setActionStrip(androidx.car.app.model.ActionStrip);
+ method public androidx.car.app.model.PlaceListMapTemplate.Builder setAnchor(androidx.car.app.model.Place);
+ method public androidx.car.app.model.PlaceListMapTemplate.Builder setCurrentLocationEnabled(boolean);
+ method public androidx.car.app.model.PlaceListMapTemplate.Builder setHeaderAction(androidx.car.app.model.Action);
+ method public androidx.car.app.model.PlaceListMapTemplate.Builder setItemList(androidx.car.app.model.ItemList);
+ method public androidx.car.app.model.PlaceListMapTemplate.Builder setLoading(boolean);
+ method @androidx.car.app.annotations.RequiresCarApi(5) public androidx.car.app.model.PlaceListMapTemplate.Builder setOnContentRefreshListener(androidx.car.app.model.OnContentRefreshListener);
+ method public androidx.car.app.model.PlaceListMapTemplate.Builder setTitle(androidx.car.app.model.CarText);
+ method public androidx.car.app.model.PlaceListMapTemplate.Builder setTitle(CharSequence);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class PlaceMarker {
+ method public androidx.car.app.model.CarColor? getColor();
+ method public androidx.car.app.model.CarIcon? getIcon();
+ method public int getIconType();
+ method public androidx.car.app.model.CarText? getLabel();
+ field public static final int TYPE_ICON = 0; // 0x0
+ field public static final int TYPE_IMAGE = 1; // 0x1
+ }
+
+ public static final class PlaceMarker.Builder {
+ ctor public PlaceMarker.Builder();
+ method public androidx.car.app.model.PlaceMarker build();
+ method public androidx.car.app.model.PlaceMarker.Builder setColor(androidx.car.app.model.CarColor);
+ method public androidx.car.app.model.PlaceMarker.Builder setIcon(androidx.car.app.model.CarIcon, int);
+ method public androidx.car.app.model.PlaceMarker.Builder setLabel(CharSequence);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class Row implements androidx.car.app.model.Item {
+ method @androidx.car.app.annotations.RequiresCarApi(6) public java.util.List<androidx.car.app.model.Action!> getActions();
+ method public androidx.car.app.model.CarIcon? getImage();
+ method public androidx.car.app.model.Metadata? getMetadata();
+ method @androidx.car.app.annotations.RequiresCarApi(6) public int getNumericDecoration();
+ method public androidx.car.app.model.OnClickDelegate? getOnClickDelegate();
+ method public int getRowImageType();
+ method public java.util.List<androidx.car.app.model.CarText!> getTexts();
+ method public androidx.car.app.model.CarText? getTitle();
+ method public androidx.car.app.model.Toggle? getToggle();
+ method public boolean isBrowsable();
+ method @androidx.car.app.annotations.RequiresCarApi(5) public boolean isEnabled();
+ method public androidx.car.app.model.Row row();
+ method public CharSequence yourBoat();
+ field public static final int IMAGE_TYPE_ICON = 4; // 0x4
+ field public static final int IMAGE_TYPE_LARGE = 2; // 0x2
+ field public static final int IMAGE_TYPE_SMALL = 1; // 0x1
+ field public static final int NO_DECORATION = -1; // 0xffffffff
+ }
+
+ public static final class Row.Builder {
+ ctor public Row.Builder();
+ method @androidx.car.app.annotations.RequiresCarApi(6) public androidx.car.app.model.Row.Builder addAction(androidx.car.app.model.Action);
+ method public androidx.car.app.model.Row.Builder addText(androidx.car.app.model.CarText);
+ method public androidx.car.app.model.Row.Builder addText(CharSequence);
+ method public androidx.car.app.model.Row build();
+ method public androidx.car.app.model.Row.Builder setBrowsable(boolean);
+ method @androidx.car.app.annotations.RequiresCarApi(5) public androidx.car.app.model.Row.Builder setEnabled(boolean);
+ method public androidx.car.app.model.Row.Builder setImage(androidx.car.app.model.CarIcon);
+ method public androidx.car.app.model.Row.Builder setImage(androidx.car.app.model.CarIcon, int);
+ method public androidx.car.app.model.Row.Builder setMetadata(androidx.car.app.model.Metadata);
+ method @IntRange(from=0) @androidx.car.app.annotations.RequiresCarApi(6) public androidx.car.app.model.Row.Builder setNumericDecoration(int);
+ method public androidx.car.app.model.Row.Builder setOnClickListener(androidx.car.app.model.OnClickListener);
+ method public androidx.car.app.model.Row.Builder setTitle(androidx.car.app.model.CarText);
+ method public androidx.car.app.model.Row.Builder setTitle(CharSequence);
+ method public androidx.car.app.model.Row.Builder setToggle(androidx.car.app.model.Toggle);
+ }
+
+ @androidx.car.app.annotations.CarProtocol public interface SearchCallbackDelegate {
+ method public void sendSearchSubmitted(String, androidx.car.app.OnDoneCallback);
+ method public void sendSearchTextChanged(String, androidx.car.app.OnDoneCallback);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class SearchTemplate implements androidx.car.app.model.Template {
+ method public androidx.car.app.model.ActionStrip? getActionStrip();
+ method public androidx.car.app.model.Action? getHeaderAction();
+ method public String? getInitialSearchText();
+ method public androidx.car.app.model.ItemList? getItemList();
+ method public androidx.car.app.model.SearchCallbackDelegate getSearchCallbackDelegate();
+ method public String? getSearchHint();
+ method public boolean isLoading();
+ method public boolean isShowKeyboardByDefault();
+ }
+
+ public static final class SearchTemplate.Builder {
+ ctor public SearchTemplate.Builder(androidx.car.app.model.SearchTemplate.SearchCallback);
+ method public androidx.car.app.model.SearchTemplate build();
+ method public androidx.car.app.model.SearchTemplate.Builder setActionStrip(androidx.car.app.model.ActionStrip);
+ method public androidx.car.app.model.SearchTemplate.Builder setHeaderAction(androidx.car.app.model.Action);
+ method public androidx.car.app.model.SearchTemplate.Builder setInitialSearchText(String);
+ method public androidx.car.app.model.SearchTemplate.Builder setItemList(androidx.car.app.model.ItemList);
+ method public androidx.car.app.model.SearchTemplate.Builder setLoading(boolean);
+ method public androidx.car.app.model.SearchTemplate.Builder setSearchHint(String);
+ method public androidx.car.app.model.SearchTemplate.Builder setShowKeyboardByDefault(boolean);
+ }
+
+ public static interface SearchTemplate.SearchCallback {
+ method public default void onSearchSubmitted(String);
+ method public default void onSearchTextChanged(String);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class SectionedItemList {
+ method public static androidx.car.app.model.SectionedItemList create(androidx.car.app.model.ItemList, CharSequence);
+ method public androidx.car.app.model.CarText getHeader();
+ method public androidx.car.app.model.ItemList getItemList();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public final class Tab implements androidx.car.app.model.Content {
+ method public String getContentId();
+ method public androidx.car.app.model.CarIcon getIcon();
+ method public androidx.car.app.model.CarText getTitle();
+ }
+
+ public static final class Tab.Builder {
+ ctor public Tab.Builder();
+ ctor public Tab.Builder(androidx.car.app.model.Tab);
+ method public androidx.car.app.model.Tab build();
+ method public androidx.car.app.model.Tab.Builder setContentId(String);
+ method public androidx.car.app.model.Tab.Builder setIcon(androidx.car.app.model.CarIcon);
+ method public androidx.car.app.model.Tab.Builder setTitle(CharSequence);
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public interface TabCallbackDelegate {
+ method public void sendTabSelected(String, androidx.car.app.OnDoneCallback);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public class TabContents implements androidx.car.app.model.Content {
+ method public String getContentId();
+ method public androidx.car.app.model.Template getTemplate();
+ field public static final String CONTENT_ID = "TAB_CONTENTS_CONTENT_ID";
+ }
+
+ public static final class TabContents.Builder {
+ ctor public TabContents.Builder(androidx.car.app.model.Template);
+ method public androidx.car.app.model.TabContents build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(6) public class TabTemplate implements androidx.car.app.model.Template {
+ method public String getActiveTabContentId();
+ method public androidx.car.app.model.Action getHeaderAction();
+ method public androidx.car.app.model.TabCallbackDelegate getTabCallbackDelegate();
+ method public androidx.car.app.model.TabContents getTabContents();
+ method public java.util.List<androidx.car.app.model.Tab!> getTabs();
+ method public boolean isLoading();
+ }
+
+ public static final class TabTemplate.Builder {
+ ctor public TabTemplate.Builder(androidx.car.app.model.TabTemplate);
+ ctor public TabTemplate.Builder(androidx.car.app.model.TabTemplate.TabCallback);
+ method public androidx.car.app.model.TabTemplate.Builder addTab(androidx.car.app.model.Tab);
+ method public androidx.car.app.model.TabTemplate build();
+ method public androidx.car.app.model.TabTemplate.Builder setActiveTabContentId(String);
+ method public androidx.car.app.model.TabTemplate.Builder setHeaderAction(androidx.car.app.model.Action);
+ method public androidx.car.app.model.TabTemplate.Builder setLoading(boolean);
+ method public androidx.car.app.model.TabTemplate.Builder setTabContents(androidx.car.app.model.TabContents);
+ }
+
+ public static interface TabTemplate.TabCallback {
+ method public default void onTabSelected(String);
+ }
+
+ @androidx.car.app.annotations.CarProtocol public interface Template {
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class TemplateInfo {
+ ctor public TemplateInfo(Class<? extends androidx.car.app.model.Template>, String);
+ method public Class<? extends androidx.car.app.model.Template> getTemplateClass();
+ method public String getTemplateId();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class TemplateWrapper {
+ method public static androidx.car.app.model.TemplateWrapper copyOf(androidx.car.app.model.TemplateWrapper);
+ method public int getCurrentTaskStep();
+ method public String getId();
+ method public androidx.car.app.model.Template getTemplate();
+ method public java.util.List<androidx.car.app.model.TemplateInfo!> getTemplateInfosForScreenStack();
+ method public boolean isRefresh();
+ method public void setCurrentTaskStep(int);
+ method public void setId(String);
+ method public void setRefresh(boolean);
+ method public void setTemplate(androidx.car.app.model.Template);
+ method public static androidx.car.app.model.TemplateWrapper wrap(androidx.car.app.model.Template);
+ method public static androidx.car.app.model.TemplateWrapper wrap(androidx.car.app.model.Template, String);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class Toggle {
+ method public androidx.car.app.model.OnCheckedChangeDelegate getOnCheckedChangeDelegate();
+ method public boolean isChecked();
+ method @androidx.car.app.annotations.RequiresCarApi(5) public boolean isEnabled();
+ }
+
+ public static final class Toggle.Builder {
+ ctor public Toggle.Builder(androidx.car.app.model.Toggle.OnCheckedChangeListener);
+ method public androidx.car.app.model.Toggle build();
+ method public androidx.car.app.model.Toggle.Builder setChecked(boolean);
+ method @androidx.car.app.annotations.RequiresCarApi(5) public androidx.car.app.model.Toggle.Builder setEnabled(boolean);
+ }
+
+ public static interface Toggle.OnCheckedChangeListener {
+ method public void onCheckedChange(boolean);
+ }
+
+}
+
+package androidx.car.app.model.signin {
+
+ @SuppressCompatibility @androidx.car.app.annotations.RequiresCarApi(2) public final class InputSignInMethod implements androidx.car.app.model.signin.SignInTemplate.SignInMethod {
+ method public androidx.car.app.model.CarText? getDefaultValue();
+ method public androidx.car.app.model.CarText? getErrorMessage();
+ method public androidx.car.app.model.CarText? getHint();
+ method public androidx.car.app.model.InputCallbackDelegate getInputCallbackDelegate();
+ method public int getInputType();
+ method public int getKeyboardType();
+ method public boolean isShowKeyboardByDefault();
+ field public static final int INPUT_TYPE_DEFAULT = 1; // 0x1
+ field public static final int INPUT_TYPE_PASSWORD = 2; // 0x2
+ field public static final int KEYBOARD_DEFAULT = 1; // 0x1
+ field public static final int KEYBOARD_EMAIL = 2; // 0x2
+ field public static final int KEYBOARD_NUMBER = 4; // 0x4
+ field public static final int KEYBOARD_PHONE = 3; // 0x3
+ }
+
+ public static final class InputSignInMethod.Builder {
+ ctor public InputSignInMethod.Builder(androidx.car.app.model.InputCallback);
+ method public androidx.car.app.model.signin.InputSignInMethod build();
+ method public androidx.car.app.model.signin.InputSignInMethod.Builder setDefaultValue(String);
+ method public androidx.car.app.model.signin.InputSignInMethod.Builder setErrorMessage(CharSequence);
+ method public androidx.car.app.model.signin.InputSignInMethod.Builder setHint(CharSequence);
+ method public androidx.car.app.model.signin.InputSignInMethod.Builder setInputType(int);
+ method public androidx.car.app.model.signin.InputSignInMethod.Builder setKeyboardType(int);
+ method public androidx.car.app.model.signin.InputSignInMethod.Builder setShowKeyboardByDefault(boolean);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.RequiresCarApi(2) public final class PinSignInMethod implements androidx.car.app.model.signin.SignInTemplate.SignInMethod {
+ ctor public PinSignInMethod(CharSequence);
+ method public androidx.car.app.model.CarText getPinCode();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.RequiresCarApi(2) public final class ProviderSignInMethod implements androidx.car.app.model.signin.SignInTemplate.SignInMethod {
+ ctor public ProviderSignInMethod(androidx.car.app.model.Action);
+ method public androidx.car.app.model.Action getAction();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.RequiresCarApi(4) public final class QRCodeSignInMethod implements androidx.car.app.model.signin.SignInTemplate.SignInMethod {
+ ctor public QRCodeSignInMethod(android.net.Uri);
+ method public android.net.Uri getUri();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.RequiresCarApi(2) public final class SignInTemplate implements androidx.car.app.model.Template {
+ method public androidx.car.app.model.ActionStrip? getActionStrip();
+ method public java.util.List<androidx.car.app.model.Action!> getActions();
+ method public androidx.car.app.model.CarText? getAdditionalText();
+ method public androidx.car.app.model.Action? getHeaderAction();
+ method public androidx.car.app.model.CarText? getInstructions();
+ method public androidx.car.app.model.signin.SignInTemplate.SignInMethod getSignInMethod();
+ method public androidx.car.app.model.CarText? getTitle();
+ method public boolean isLoading();
+ }
+
+ @androidx.car.app.annotations.RequiresCarApi(2) public static final class SignInTemplate.Builder {
+ ctor public SignInTemplate.Builder(androidx.car.app.model.signin.SignInTemplate.SignInMethod);
+ method public androidx.car.app.model.signin.SignInTemplate.Builder addAction(androidx.car.app.model.Action);
+ method public androidx.car.app.model.signin.SignInTemplate build();
+ method public androidx.car.app.model.signin.SignInTemplate.Builder setActionStrip(androidx.car.app.model.ActionStrip);
+ method public androidx.car.app.model.signin.SignInTemplate.Builder setAdditionalText(CharSequence);
+ method public androidx.car.app.model.signin.SignInTemplate.Builder setHeaderAction(androidx.car.app.model.Action);
+ method public androidx.car.app.model.signin.SignInTemplate.Builder setInstructions(CharSequence);
+ method public androidx.car.app.model.signin.SignInTemplate.Builder setLoading(boolean);
+ method public androidx.car.app.model.signin.SignInTemplate.Builder setTitle(CharSequence);
+ }
+
+ public static interface SignInTemplate.SignInMethod {
+ }
+
+}
+
+package androidx.car.app.navigation {
+
+ public class NavigationManager implements androidx.car.app.managers.Manager {
+ method @MainThread public void clearNavigationManagerCallback();
+ method @MainThread public void navigationEnded();
+ method @MainThread public void navigationStarted();
+ method @MainThread public void setNavigationManagerCallback(androidx.car.app.navigation.NavigationManagerCallback);
+ method @MainThread public void setNavigationManagerCallback(java.util.concurrent.Executor, androidx.car.app.navigation.NavigationManagerCallback);
+ method @MainThread public void updateTrip(androidx.car.app.navigation.model.Trip);
+ }
+
+ public interface NavigationManagerCallback {
+ method public default void onAutoDriveEnabled();
+ method public default void onStopNavigation();
+ }
+
+}
+
+package androidx.car.app.navigation.model {
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class Destination {
+ method public androidx.car.app.model.CarText? getAddress();
+ method public androidx.car.app.model.CarIcon? getImage();
+ method public androidx.car.app.model.CarText? getName();
+ }
+
+ public static final class Destination.Builder {
+ ctor public Destination.Builder();
+ method public androidx.car.app.navigation.model.Destination build();
+ method public androidx.car.app.navigation.model.Destination.Builder setAddress(CharSequence);
+ method public androidx.car.app.navigation.model.Destination.Builder setImage(androidx.car.app.model.CarIcon);
+ method public androidx.car.app.navigation.model.Destination.Builder setName(CharSequence);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class Lane {
+ method public java.util.List<androidx.car.app.navigation.model.LaneDirection!> getDirections();
+ }
+
+ public static final class Lane.Builder {
+ ctor public Lane.Builder();
+ method public androidx.car.app.navigation.model.Lane.Builder addDirection(androidx.car.app.navigation.model.LaneDirection);
+ method public androidx.car.app.navigation.model.Lane build();
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class LaneDirection {
+ method public static androidx.car.app.navigation.model.LaneDirection create(int, boolean);
+ method public int getShape();
+ method public boolean isRecommended();
+ field public static final int SHAPE_NORMAL_LEFT = 5; // 0x5
+ field public static final int SHAPE_NORMAL_RIGHT = 6; // 0x6
+ field public static final int SHAPE_SHARP_LEFT = 7; // 0x7
+ field public static final int SHAPE_SHARP_RIGHT = 8; // 0x8
+ field public static final int SHAPE_SLIGHT_LEFT = 3; // 0x3
+ field public static final int SHAPE_SLIGHT_RIGHT = 4; // 0x4
+ field public static final int SHAPE_STRAIGHT = 2; // 0x2
+ field public static final int SHAPE_UNKNOWN = 1; // 0x1
+ field public static final int SHAPE_U_TURN_LEFT = 9; // 0x9
+ field public static final int SHAPE_U_TURN_RIGHT = 10; // 0xa
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class Maneuver {
+ method public androidx.car.app.model.CarIcon? getIcon();
+ method public int getRoundaboutExitAngle();
+ method public int getRoundaboutExitNumber();
+ method public int getType();
+ field public static final int TYPE_DEPART = 1; // 0x1
+ field public static final int TYPE_DESTINATION = 39; // 0x27
+ field public static final int TYPE_DESTINATION_LEFT = 41; // 0x29
+ field public static final int TYPE_DESTINATION_RIGHT = 42; // 0x2a
+ field public static final int TYPE_DESTINATION_STRAIGHT = 40; // 0x28
+ field public static final int TYPE_FERRY_BOAT = 37; // 0x25
+ field public static final int TYPE_FERRY_BOAT_LEFT = 47; // 0x2f
+ field public static final int TYPE_FERRY_BOAT_RIGHT = 48; // 0x30
+ field public static final int TYPE_FERRY_TRAIN = 38; // 0x26
+ field public static final int TYPE_FERRY_TRAIN_LEFT = 49; // 0x31
+ field public static final int TYPE_FERRY_TRAIN_RIGHT = 50; // 0x32
+ field public static final int TYPE_FORK_LEFT = 25; // 0x19
+ field public static final int TYPE_FORK_RIGHT = 26; // 0x1a
+ field public static final int TYPE_KEEP_LEFT = 3; // 0x3
+ field public static final int TYPE_KEEP_RIGHT = 4; // 0x4
+ field public static final int TYPE_MERGE_LEFT = 27; // 0x1b
+ field public static final int TYPE_MERGE_RIGHT = 28; // 0x1c
+ field public static final int TYPE_MERGE_SIDE_UNSPECIFIED = 29; // 0x1d
+ field public static final int TYPE_NAME_CHANGE = 2; // 0x2
+ field public static final int TYPE_OFF_RAMP_NORMAL_LEFT = 23; // 0x17
+ field public static final int TYPE_OFF_RAMP_NORMAL_RIGHT = 24; // 0x18
+ field public static final int TYPE_OFF_RAMP_SLIGHT_LEFT = 21; // 0x15
+ field public static final int TYPE_OFF_RAMP_SLIGHT_RIGHT = 22; // 0x16
+ field public static final int TYPE_ON_RAMP_NORMAL_LEFT = 15; // 0xf
+ field public static final int TYPE_ON_RAMP_NORMAL_RIGHT = 16; // 0x10
+ field public static final int TYPE_ON_RAMP_SHARP_LEFT = 17; // 0x11
+ field public static final int TYPE_ON_RAMP_SHARP_RIGHT = 18; // 0x12
+ field public static final int TYPE_ON_RAMP_SLIGHT_LEFT = 13; // 0xd
+ field public static final int TYPE_ON_RAMP_SLIGHT_RIGHT = 14; // 0xe
+ field public static final int TYPE_ON_RAMP_U_TURN_LEFT = 19; // 0x13
+ field public static final int TYPE_ON_RAMP_U_TURN_RIGHT = 20; // 0x14
+ field public static final int TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW = 34; // 0x22
+ field public static final int TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW_WITH_ANGLE = 35; // 0x23
+ field public static final int TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW = 32; // 0x20
+ field public static final int TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW_WITH_ANGLE = 33; // 0x21
+ field public static final int TYPE_ROUNDABOUT_ENTER_CCW = 45; // 0x2d
+ field public static final int TYPE_ROUNDABOUT_ENTER_CW = 43; // 0x2b
+ field public static final int TYPE_ROUNDABOUT_EXIT_CCW = 46; // 0x2e
+ field public static final int TYPE_ROUNDABOUT_EXIT_CW = 44; // 0x2c
+ field public static final int TYPE_STRAIGHT = 36; // 0x24
+ field public static final int TYPE_TURN_NORMAL_LEFT = 7; // 0x7
+ field public static final int TYPE_TURN_NORMAL_RIGHT = 8; // 0x8
+ field public static final int TYPE_TURN_SHARP_LEFT = 9; // 0x9
+ field public static final int TYPE_TURN_SHARP_RIGHT = 10; // 0xa
+ field public static final int TYPE_TURN_SLIGHT_LEFT = 5; // 0x5
+ field public static final int TYPE_TURN_SLIGHT_RIGHT = 6; // 0x6
+ field public static final int TYPE_UNKNOWN = 0; // 0x0
+ field public static final int TYPE_U_TURN_LEFT = 11; // 0xb
+ field public static final int TYPE_U_TURN_RIGHT = 12; // 0xc
+ }
+
+ public static final class Maneuver.Builder {
+ ctor public Maneuver.Builder(int);
+ method public androidx.car.app.navigation.model.Maneuver build();
+ method public androidx.car.app.navigation.model.Maneuver.Builder setIcon(androidx.car.app.model.CarIcon);
+ method public androidx.car.app.navigation.model.Maneuver.Builder setRoundaboutExitAngle(@IntRange(from=1, to=360) int);
+ method public androidx.car.app.navigation.model.Maneuver.Builder setRoundaboutExitNumber(@IntRange(from=1) int);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(5) public final class MapController {
+ method public androidx.car.app.model.ActionStrip? getMapActionStrip();
+ method public androidx.car.app.navigation.model.PanModeDelegate? getPanModeDelegate();
+ }
+
+ public static final class MapController.Builder {
+ ctor public MapController.Builder();
+ method public androidx.car.app.navigation.model.MapController build();
+ method public androidx.car.app.navigation.model.MapController.Builder setMapActionStrip(androidx.car.app.model.ActionStrip);
+ method public androidx.car.app.navigation.model.MapController.Builder setPanModeListener(androidx.car.app.navigation.model.PanModeListener);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(5) public final class MapTemplate implements androidx.car.app.model.Template {
+ method public androidx.car.app.model.ActionStrip? getActionStrip();
+ method public androidx.car.app.model.Header? getHeader();
+ method public androidx.car.app.model.ItemList? getItemList();
+ method public androidx.car.app.navigation.model.MapController? getMapController();
+ method public androidx.car.app.model.Pane? getPane();
+ }
+
+ public static final class MapTemplate.Builder {
+ ctor public MapTemplate.Builder();
+ method public androidx.car.app.navigation.model.MapTemplate build();
+ method public androidx.car.app.navigation.model.MapTemplate.Builder setActionStrip(androidx.car.app.model.ActionStrip);
+ method public androidx.car.app.navigation.model.MapTemplate.Builder setHeader(androidx.car.app.model.Header);
+ method public androidx.car.app.navigation.model.MapTemplate.Builder setItemList(androidx.car.app.model.ItemList);
+ method public androidx.car.app.navigation.model.MapTemplate.Builder setMapController(androidx.car.app.navigation.model.MapController);
+ method public androidx.car.app.navigation.model.MapTemplate.Builder setPane(androidx.car.app.model.Pane);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.ExperimentalCarApi @androidx.car.app.annotations.RequiresCarApi(7) public final class MapWithContentTemplate implements androidx.car.app.model.Template {
+ method public androidx.car.app.model.ActionStrip? getActionStrip();
+ method public androidx.car.app.model.Template? getContentTemplate();
+ method public androidx.car.app.navigation.model.MapController? getMapController();
+ method public boolean isLoading();
+ }
+
+ public static final class MapWithContentTemplate.Builder {
+ ctor public MapWithContentTemplate.Builder();
+ method public androidx.car.app.navigation.model.MapWithContentTemplate build();
+ method public androidx.car.app.navigation.model.MapWithContentTemplate.Builder setActionStrip(androidx.car.app.model.ActionStrip);
+ method public androidx.car.app.navigation.model.MapWithContentTemplate.Builder setContentTemplate(androidx.car.app.model.Template);
+ method public androidx.car.app.navigation.model.MapWithContentTemplate.Builder setLoading(boolean);
+ method public androidx.car.app.navigation.model.MapWithContentTemplate.Builder setMapController(androidx.car.app.navigation.model.MapController);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class MessageInfo implements androidx.car.app.navigation.model.NavigationTemplate.NavigationInfo {
+ method public androidx.car.app.model.CarIcon? getImage();
+ method public androidx.car.app.model.CarText? getText();
+ method public androidx.car.app.model.CarText? getTitle();
+ }
+
+ public static final class MessageInfo.Builder {
+ ctor public MessageInfo.Builder(androidx.car.app.model.CarText);
+ ctor public MessageInfo.Builder(CharSequence);
+ method public androidx.car.app.navigation.model.MessageInfo build();
+ method public androidx.car.app.navigation.model.MessageInfo.Builder setImage(androidx.car.app.model.CarIcon);
+ method public androidx.car.app.navigation.model.MessageInfo.Builder setText(androidx.car.app.model.CarText);
+ method public androidx.car.app.navigation.model.MessageInfo.Builder setText(CharSequence);
+ method public androidx.car.app.navigation.model.MessageInfo.Builder setTitle(CharSequence);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class NavigationTemplate implements androidx.car.app.model.Template {
+ method public androidx.car.app.model.ActionStrip? getActionStrip();
+ method public androidx.car.app.model.CarColor? getBackgroundColor();
+ method public androidx.car.app.navigation.model.TravelEstimate? getDestinationTravelEstimate();
+ method @androidx.car.app.annotations.RequiresCarApi(2) public androidx.car.app.model.ActionStrip? getMapActionStrip();
+ method public androidx.car.app.navigation.model.NavigationTemplate.NavigationInfo? getNavigationInfo();
+ method @androidx.car.app.annotations.RequiresCarApi(2) public androidx.car.app.navigation.model.PanModeDelegate? getPanModeDelegate();
+ method @Deprecated @androidx.car.app.annotations.RequiresCarApi(2) public androidx.car.app.model.Toggle? getPanModeToggle();
+ }
+
+ public static final class NavigationTemplate.Builder {
+ ctor public NavigationTemplate.Builder();
+ method public androidx.car.app.navigation.model.NavigationTemplate build();
+ method public androidx.car.app.navigation.model.NavigationTemplate.Builder setActionStrip(androidx.car.app.model.ActionStrip);
+ method public androidx.car.app.navigation.model.NavigationTemplate.Builder setBackgroundColor(androidx.car.app.model.CarColor);
+ method public androidx.car.app.navigation.model.NavigationTemplate.Builder setDestinationTravelEstimate(androidx.car.app.navigation.model.TravelEstimate);
+ method @androidx.car.app.annotations.RequiresCarApi(2) public androidx.car.app.navigation.model.NavigationTemplate.Builder setMapActionStrip(androidx.car.app.model.ActionStrip);
+ method public androidx.car.app.navigation.model.NavigationTemplate.Builder setNavigationInfo(androidx.car.app.navigation.model.NavigationTemplate.NavigationInfo);
+ method @androidx.car.app.annotations.RequiresCarApi(2) public androidx.car.app.navigation.model.NavigationTemplate.Builder setPanModeListener(androidx.car.app.navigation.model.PanModeListener);
+ }
+
+ public static interface NavigationTemplate.NavigationInfo {
+ }
+
+ @androidx.car.app.annotations.CarProtocol @androidx.car.app.annotations.RequiresCarApi(2) public interface PanModeDelegate {
+ method public void sendPanModeChanged(boolean, androidx.car.app.OnDoneCallback);
+ }
+
+ public interface PanModeListener {
+ method public void onPanModeChanged(boolean);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class PlaceListNavigationTemplate implements androidx.car.app.model.Template {
+ method public androidx.car.app.model.ActionStrip? getActionStrip();
+ method @androidx.car.app.annotations.RequiresCarApi(5) public androidx.car.app.model.Header? getHeader();
+ method @Deprecated public androidx.car.app.model.Action? getHeaderAction();
+ method public androidx.car.app.model.ItemList? getItemList();
+ method @androidx.car.app.annotations.RequiresCarApi(4) public androidx.car.app.model.ActionStrip? getMapActionStrip();
+ method public androidx.car.app.model.OnContentRefreshDelegate? getOnContentRefreshDelegate();
+ method @androidx.car.app.annotations.RequiresCarApi(4) public androidx.car.app.navigation.model.PanModeDelegate? getPanModeDelegate();
+ method @Deprecated public androidx.car.app.model.CarText? getTitle();
+ method public boolean isLoading();
+ }
+
+ public static final class PlaceListNavigationTemplate.Builder {
+ ctor public PlaceListNavigationTemplate.Builder();
+ method public androidx.car.app.navigation.model.PlaceListNavigationTemplate build();
+ method public androidx.car.app.navigation.model.PlaceListNavigationTemplate.Builder setActionStrip(androidx.car.app.model.ActionStrip);
+ method @androidx.car.app.annotations.RequiresCarApi(5) public androidx.car.app.navigation.model.PlaceListNavigationTemplate.Builder setHeader(androidx.car.app.model.Header);
+ method @Deprecated public androidx.car.app.navigation.model.PlaceListNavigationTemplate.Builder setHeaderAction(androidx.car.app.model.Action);
+ method public androidx.car.app.navigation.model.PlaceListNavigationTemplate.Builder setItemList(androidx.car.app.model.ItemList);
+ method public androidx.car.app.navigation.model.PlaceListNavigationTemplate.Builder setLoading(boolean);
+ method @androidx.car.app.annotations.RequiresCarApi(4) public androidx.car.app.navigation.model.PlaceListNavigationTemplate.Builder setMapActionStrip(androidx.car.app.model.ActionStrip);
+ method public androidx.car.app.navigation.model.PlaceListNavigationTemplate.Builder setOnContentRefreshListener(androidx.car.app.model.OnContentRefreshListener);
+ method @androidx.car.app.annotations.RequiresCarApi(4) public androidx.car.app.navigation.model.PlaceListNavigationTemplate.Builder setPanModeListener(androidx.car.app.navigation.model.PanModeListener);
+ method @Deprecated public androidx.car.app.navigation.model.PlaceListNavigationTemplate.Builder setTitle(androidx.car.app.model.CarText);
+ method @Deprecated public androidx.car.app.navigation.model.PlaceListNavigationTemplate.Builder setTitle(CharSequence);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class RoutePreviewNavigationTemplate implements androidx.car.app.model.Template {
+ method public androidx.car.app.model.ActionStrip? getActionStrip();
+ method @androidx.car.app.annotations.RequiresCarApi(5) public androidx.car.app.model.Header? getHeader();
+ method @Deprecated public androidx.car.app.model.Action? getHeaderAction();
+ method public androidx.car.app.model.ItemList? getItemList();
+ method @androidx.car.app.annotations.RequiresCarApi(4) public androidx.car.app.model.ActionStrip? getMapActionStrip();
+ method public androidx.car.app.model.Action? getNavigateAction();
+ method @androidx.car.app.annotations.RequiresCarApi(4) public androidx.car.app.navigation.model.PanModeDelegate? getPanModeDelegate();
+ method @Deprecated public androidx.car.app.model.CarText? getTitle();
+ method public boolean isLoading();
+ }
+
+ public static final class RoutePreviewNavigationTemplate.Builder {
+ ctor public RoutePreviewNavigationTemplate.Builder();
+ method public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate build();
+ method public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setActionStrip(androidx.car.app.model.ActionStrip);
+ method @androidx.car.app.annotations.RequiresCarApi(5) public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setHeader(androidx.car.app.model.Header);
+ method @Deprecated public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setHeaderAction(androidx.car.app.model.Action);
+ method public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setItemList(androidx.car.app.model.ItemList);
+ method public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setLoading(boolean);
+ method @androidx.car.app.annotations.RequiresCarApi(4) public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setMapActionStrip(androidx.car.app.model.ActionStrip);
+ method public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setNavigateAction(androidx.car.app.model.Action);
+ method @androidx.car.app.annotations.RequiresCarApi(4) public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setPanModeListener(androidx.car.app.navigation.model.PanModeListener);
+ method @Deprecated public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setTitle(androidx.car.app.model.CarText);
+ method @Deprecated public androidx.car.app.navigation.model.RoutePreviewNavigationTemplate.Builder setTitle(CharSequence);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class RoutingInfo implements androidx.car.app.navigation.model.NavigationTemplate.NavigationInfo {
+ method public androidx.car.app.model.Distance? getCurrentDistance();
+ method public androidx.car.app.navigation.model.Step? getCurrentStep();
+ method public androidx.car.app.model.CarIcon? getJunctionImage();
+ method public androidx.car.app.navigation.model.Step? getNextStep();
+ method public boolean isLoading();
+ }
+
+ public static final class RoutingInfo.Builder {
+ ctor public RoutingInfo.Builder();
+ method public androidx.car.app.navigation.model.RoutingInfo build();
+ method public androidx.car.app.navigation.model.RoutingInfo.Builder setCurrentStep(androidx.car.app.navigation.model.Step, androidx.car.app.model.Distance);
+ method public androidx.car.app.navigation.model.RoutingInfo.Builder setJunctionImage(androidx.car.app.model.CarIcon);
+ method public androidx.car.app.navigation.model.RoutingInfo.Builder setLoading(boolean);
+ method public androidx.car.app.navigation.model.RoutingInfo.Builder setNextStep(androidx.car.app.navigation.model.Step);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class Step {
+ method public androidx.car.app.model.CarText? getCue();
+ method public java.util.List<androidx.car.app.navigation.model.Lane!> getLanes();
+ method public androidx.car.app.model.CarIcon? getLanesImage();
+ method public androidx.car.app.navigation.model.Maneuver? getManeuver();
+ method public androidx.car.app.model.CarText? getRoad();
+ }
+
+ public static final class Step.Builder {
+ ctor public Step.Builder();
+ ctor public Step.Builder(androidx.car.app.model.CarText);
+ ctor public Step.Builder(CharSequence);
+ method public androidx.car.app.navigation.model.Step.Builder addLane(androidx.car.app.navigation.model.Lane);
+ method public androidx.car.app.navigation.model.Step build();
+ method public androidx.car.app.navigation.model.Step.Builder setCue(CharSequence);
+ method public androidx.car.app.navigation.model.Step.Builder setLanesImage(androidx.car.app.model.CarIcon);
+ method public androidx.car.app.navigation.model.Step.Builder setManeuver(androidx.car.app.navigation.model.Maneuver);
+ method public androidx.car.app.navigation.model.Step.Builder setRoad(CharSequence);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class TravelEstimate {
+ method public androidx.car.app.model.DateTimeWithZone? getArrivalTimeAtDestination();
+ method public androidx.car.app.model.Distance? getRemainingDistance();
+ method public androidx.car.app.model.CarColor? getRemainingDistanceColor();
+ method public androidx.car.app.model.CarColor? getRemainingTimeColor();
+ method public long getRemainingTimeSeconds();
+ method @androidx.car.app.annotations.RequiresCarApi(5) public androidx.car.app.model.CarIcon? getTripIcon();
+ method @androidx.car.app.annotations.RequiresCarApi(5) public androidx.car.app.model.CarText? getTripText();
+ field public static final long REMAINING_TIME_UNKNOWN = -1L; // 0xffffffffffffffffL
+ }
+
+ public static final class TravelEstimate.Builder {
+ ctor public TravelEstimate.Builder(androidx.car.app.model.Distance, androidx.car.app.model.DateTimeWithZone);
+ ctor @RequiresApi(26) public TravelEstimate.Builder(androidx.car.app.model.Distance, java.time.ZonedDateTime);
+ method public androidx.car.app.navigation.model.TravelEstimate build();
+ method public androidx.car.app.navigation.model.TravelEstimate.Builder setRemainingDistanceColor(androidx.car.app.model.CarColor);
+ method @RequiresApi(26) public androidx.car.app.navigation.model.TravelEstimate.Builder setRemainingTime(java.time.Duration);
+ method public androidx.car.app.navigation.model.TravelEstimate.Builder setRemainingTimeColor(androidx.car.app.model.CarColor);
+ method public androidx.car.app.navigation.model.TravelEstimate.Builder setRemainingTimeSeconds(@IntRange(from=0xffffffff) long);
+ method @androidx.car.app.annotations.RequiresCarApi(5) public androidx.car.app.navigation.model.TravelEstimate.Builder setTripIcon(androidx.car.app.model.CarIcon);
+ method @androidx.car.app.annotations.RequiresCarApi(5) public androidx.car.app.navigation.model.TravelEstimate.Builder setTripText(androidx.car.app.model.CarText);
+ }
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class Trip {
+ method public androidx.car.app.model.CarText? getCurrentRoad();
+ method public java.util.List<androidx.car.app.navigation.model.TravelEstimate!> getDestinationTravelEstimates();
+ method public java.util.List<androidx.car.app.navigation.model.Destination!> getDestinations();
+ method public java.util.List<androidx.car.app.navigation.model.TravelEstimate!> getStepTravelEstimates();
+ method public java.util.List<androidx.car.app.navigation.model.Step!> getSteps();
+ method public boolean isLoading();
+ }
+
+ public static final class Trip.Builder {
+ ctor public Trip.Builder();
+ method public androidx.car.app.navigation.model.Trip.Builder addDestination(androidx.car.app.navigation.model.Destination, androidx.car.app.navigation.model.TravelEstimate);
+ method public androidx.car.app.navigation.model.Trip.Builder addStep(androidx.car.app.navigation.model.Step, androidx.car.app.navigation.model.TravelEstimate);
+ method public androidx.car.app.navigation.model.Trip build();
+ method public androidx.car.app.navigation.model.Trip.Builder setCurrentRoad(CharSequence);
+ method public androidx.car.app.navigation.model.Trip.Builder setLoading(boolean);
+ }
+
+}
+
+package androidx.car.app.notification {
+
+ public final class CarAppExtender implements androidx.core.app.NotificationCompat.Extender {
+ ctor public CarAppExtender(android.app.Notification);
+ method public androidx.core.app.NotificationCompat.Builder extend(androidx.core.app.NotificationCompat.Builder);
+ method public java.util.List<android.app.Notification.Action!> getActions();
+ method public String? getChannelId();
+ method public androidx.car.app.model.CarColor? getColor();
+ method public android.app.PendingIntent? getContentIntent();
+ method public CharSequence? getContentText();
+ method public CharSequence? getContentTitle();
+ method public android.app.PendingIntent? getDeleteIntent();
+ method public int getImportance();
+ method public android.graphics.Bitmap? getLargeIcon();
+ method @DrawableRes public int getSmallIcon();
+ method public static boolean isExtended(android.app.Notification);
+ }
+
+ public static final class CarAppExtender.Builder {
+ ctor public CarAppExtender.Builder();
+ method public androidx.car.app.notification.CarAppExtender.Builder addAction(@DrawableRes int, CharSequence, android.app.PendingIntent);
+ method public androidx.car.app.notification.CarAppExtender build();
+ method public androidx.car.app.notification.CarAppExtender.Builder setChannelId(String);
+ method public androidx.car.app.notification.CarAppExtender.Builder setColor(androidx.car.app.model.CarColor);
+ method public androidx.car.app.notification.CarAppExtender.Builder setContentIntent(android.app.PendingIntent);
+ method public androidx.car.app.notification.CarAppExtender.Builder setContentText(CharSequence);
+ method public androidx.car.app.notification.CarAppExtender.Builder setContentTitle(CharSequence);
+ method public androidx.car.app.notification.CarAppExtender.Builder setDeleteIntent(android.app.PendingIntent);
+ method public androidx.car.app.notification.CarAppExtender.Builder setImportance(int);
+ method public androidx.car.app.notification.CarAppExtender.Builder setLargeIcon(android.graphics.Bitmap);
+ method public androidx.car.app.notification.CarAppExtender.Builder setSmallIcon(int);
+ }
+
+ public final class CarNotificationManager {
+ method public boolean areNotificationsEnabled();
+ method public void cancel(int);
+ method public void cancel(String?, int);
+ method public void cancelAll();
+ method public void createNotificationChannel(androidx.core.app.NotificationChannelCompat);
+ method public void createNotificationChannelGroup(androidx.core.app.NotificationChannelGroupCompat);
+ method public void createNotificationChannelGroups(java.util.List<androidx.core.app.NotificationChannelGroupCompat!>);
+ method public void createNotificationChannels(java.util.List<androidx.core.app.NotificationChannelCompat!>);
+ method public void deleteNotificationChannel(String);
+ method public void deleteNotificationChannelGroup(String);
+ method public void deleteUnlistedNotificationChannels(java.util.Collection<java.lang.String!>);
+ method public static androidx.car.app.notification.CarNotificationManager from(android.content.Context);
+ method public static java.util.Set<java.lang.String!> getEnabledListenerPackages(android.content.Context);
+ method public int getImportance();
+ method public androidx.core.app.NotificationChannelCompat? getNotificationChannel(String);
+ method public androidx.core.app.NotificationChannelCompat? getNotificationChannel(String, String);
+ method public androidx.core.app.NotificationChannelGroupCompat? getNotificationChannelGroup(String);
+ method public java.util.List<androidx.core.app.NotificationChannelGroupCompat!> getNotificationChannelGroups();
+ method public java.util.List<androidx.core.app.NotificationChannelCompat!> getNotificationChannels();
+ method public void notify(int, androidx.core.app.NotificationCompat.Builder);
+ method public void notify(String?, int, androidx.core.app.NotificationCompat.Builder);
+ }
+
+ public final class CarPendingIntent {
+ method public static android.app.PendingIntent getCarApp(android.content.Context, int, android.content.Intent, int);
+ }
+
+}
+
+package androidx.car.app.serialization {
+
+ public final class Bundleable implements android.os.Parcelable {
+ method public static androidx.car.app.serialization.Bundleable create(Object) throws androidx.car.app.serialization.BundlerException;
+ method public int describeContents();
+ method public Object get() throws androidx.car.app.serialization.BundlerException;
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<androidx.car.app.serialization.Bundleable!> CREATOR;
+ }
+
+ public class BundlerException extends java.lang.Exception {
+ ctor public BundlerException(String?);
+ ctor public BundlerException(String?, Throwable);
+ }
+
+}
+
+package androidx.car.app.suggestion {
+
+ @androidx.car.app.annotations.RequiresCarApi(5) public class SuggestionManager implements androidx.car.app.managers.Manager {
+ method @MainThread public void updateSuggestions(java.util.List<androidx.car.app.suggestion.model.Suggestion!>);
+ }
+
+}
+
+package androidx.car.app.suggestion.model {
+
+ @SuppressCompatibility @androidx.car.app.annotations.CarProtocol public final class Suggestion {
+ method public android.app.PendingIntent? getAction();
+ method public androidx.car.app.model.CarIcon? getIcon();
+ method public String getIdentifier();
+ method public androidx.car.app.model.CarText? getSubtitle();
+ method public androidx.car.app.model.CarText getTitle();
+ }
+
+ public static final class Suggestion.Builder {
+ ctor public Suggestion.Builder();
+ method public androidx.car.app.suggestion.model.Suggestion build();
+ method public androidx.car.app.suggestion.model.Suggestion.Builder setAction(android.app.PendingIntent);
+ method public androidx.car.app.suggestion.model.Suggestion.Builder setIcon(androidx.car.app.model.CarIcon);
+ method public androidx.car.app.suggestion.model.Suggestion.Builder setIdentifier(String);
+ method public androidx.car.app.suggestion.model.Suggestion.Builder setSubtitle(CharSequence);
+ method public androidx.car.app.suggestion.model.Suggestion.Builder setTitle(CharSequence);
+ }
+
+}
+
+package androidx.car.app.validation {
+
+ public final class HostValidator {
+ method public java.util.Map<java.lang.String!,java.util.List<java.lang.String!>!> getAllowedHosts();
+ method public boolean isValidHost(androidx.car.app.HostInfo);
+ field public static final androidx.car.app.validation.HostValidator ALLOW_ALL_HOSTS_VALIDATOR;
+ field public static final String TEMPLATE_RENDERER_PERMISSION = "android.car.permission.TEMPLATE_RENDERER";
+ }
+
+ public static final class HostValidator.Builder {
+ ctor public HostValidator.Builder(android.content.Context);
+ method public androidx.car.app.validation.HostValidator.Builder addAllowedHost(String, String);
+ method public androidx.car.app.validation.HostValidator.Builder addAllowedHosts(@ArrayRes int);
+ method public androidx.car.app.validation.HostValidator build();
+ }
+
+}
+
+package androidx.car.app.versioning {
+
+ public final class CarAppApiLevels {
+ method public static int getLatest();
+ method public static int getOldest();
+ field public static final int LEVEL_1 = 1; // 0x1
+ field public static final int LEVEL_2 = 2; // 0x2
+ field public static final int LEVEL_3 = 3; // 0x3
+ field public static final int LEVEL_4 = 4; // 0x4
+ field public static final int LEVEL_5 = 5; // 0x5
+ field public static final int LEVEL_6 = 6; // 0x6
+ field public static final int LEVEL_7 = 7; // 0x7
+ }
+
+}
+
diff --git a/compose/animation/animation/api/current.ignore b/compose/animation/animation/api/current.ignore
new file mode 100644
index 0000000..721c745
--- /dev/null
+++ b/compose/animation/animation/api/current.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+AddedAbstractMethod: androidx.compose.animation.AnimatedContentTransitionScope#getContentAlignment():
+ Added method androidx.compose.animation.AnimatedContentTransitionScope.getContentAlignment()
diff --git a/compose/animation/animation/api/current.txt b/compose/animation/animation/api/current.txt
index f66083e..1d1e078 100644
--- a/compose/animation/animation/api/current.txt
+++ b/compose/animation/animation/api/current.txt
@@ -17,10 +17,14 @@
}
public sealed interface AnimatedContentTransitionScope<S> extends androidx.compose.animation.core.Transition.Segment<S> {
+ method public androidx.compose.ui.Alignment getContentAlignment();
method public default androidx.compose.animation.ExitTransition getHold(androidx.compose.animation.ExitTransition.Companion);
+ method @SuppressCompatibility @androidx.compose.animation.ExperimentalAnimationApi public androidx.compose.animation.EnterTransition scaleInToFitContainer(optional androidx.compose.ui.Alignment alignment, optional androidx.compose.ui.layout.ContentScale contentScale);
+ method @SuppressCompatibility @androidx.compose.animation.ExperimentalAnimationApi public androidx.compose.animation.ExitTransition scaleOutToFitContainer(optional androidx.compose.ui.Alignment alignment, optional androidx.compose.ui.layout.ContentScale contentScale);
method public androidx.compose.animation.EnterTransition slideIntoContainer(int towards, optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> animationSpec, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Integer> initialOffset);
method public androidx.compose.animation.ExitTransition slideOutOfContainer(int towards, optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> animationSpec, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Integer> targetOffset);
method public infix androidx.compose.animation.ContentTransform using(androidx.compose.animation.ContentTransform, androidx.compose.animation.SizeTransform? sizeTransform);
+ property public abstract androidx.compose.ui.Alignment contentAlignment;
}
@androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public static final value class AnimatedContentTransitionScope.SlideDirection {
diff --git a/compose/animation/animation/api/restricted_current.ignore b/compose/animation/animation/api/restricted_current.ignore
new file mode 100644
index 0000000..721c745
--- /dev/null
+++ b/compose/animation/animation/api/restricted_current.ignore
@@ -0,0 +1,3 @@
+// Baseline format: 1.0
+AddedAbstractMethod: androidx.compose.animation.AnimatedContentTransitionScope#getContentAlignment():
+ Added method androidx.compose.animation.AnimatedContentTransitionScope.getContentAlignment()
diff --git a/compose/animation/animation/api/restricted_current.txt b/compose/animation/animation/api/restricted_current.txt
index f66083e..1d1e078 100644
--- a/compose/animation/animation/api/restricted_current.txt
+++ b/compose/animation/animation/api/restricted_current.txt
@@ -17,10 +17,14 @@
}
public sealed interface AnimatedContentTransitionScope<S> extends androidx.compose.animation.core.Transition.Segment<S> {
+ method public androidx.compose.ui.Alignment getContentAlignment();
method public default androidx.compose.animation.ExitTransition getHold(androidx.compose.animation.ExitTransition.Companion);
+ method @SuppressCompatibility @androidx.compose.animation.ExperimentalAnimationApi public androidx.compose.animation.EnterTransition scaleInToFitContainer(optional androidx.compose.ui.Alignment alignment, optional androidx.compose.ui.layout.ContentScale contentScale);
+ method @SuppressCompatibility @androidx.compose.animation.ExperimentalAnimationApi public androidx.compose.animation.ExitTransition scaleOutToFitContainer(optional androidx.compose.ui.Alignment alignment, optional androidx.compose.ui.layout.ContentScale contentScale);
method public androidx.compose.animation.EnterTransition slideIntoContainer(int towards, optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> animationSpec, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Integer> initialOffset);
method public androidx.compose.animation.ExitTransition slideOutOfContainer(int towards, optional androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntOffset> animationSpec, optional kotlin.jvm.functions.Function1<? super java.lang.Integer,java.lang.Integer> targetOffset);
method public infix androidx.compose.animation.ContentTransform using(androidx.compose.animation.ContentTransform, androidx.compose.animation.SizeTransform? sizeTransform);
+ property public abstract androidx.compose.ui.Alignment contentAlignment;
}
@androidx.compose.runtime.Immutable @kotlin.jvm.JvmInline public static final value class AnimatedContentTransitionScope.SlideDirection {
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/layoutanimation/ContainerTransform.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/layoutanimation/ContainerTransform.kt
new file mode 100644
index 0000000..e2bbcbd
--- /dev/null
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/layoutanimation/ContainerTransform.kt
@@ -0,0 +1,246 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.animation.demos.layoutanimation
+
+import androidx.compose.animation.AnimatedContent
+import androidx.compose.animation.ExperimentalAnimationApi
+import androidx.compose.animation.core.animateDpAsState
+import androidx.compose.animation.core.animateIntAsState
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+import androidx.compose.animation.togetherWith
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.Icon
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.RadioButton
+import androidx.compose.material.Text
+import androidx.compose.material.TextField
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.AccountCircle
+import androidx.compose.material.icons.filled.Add
+import androidx.compose.material.icons.filled.ArrowBack
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.vector.rememberVectorPainter
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+
+@OptIn(ExperimentalAnimationApi::class)
+@Preview
+@Composable
+fun LocalContainerTransformDemo() {
+ Box(Modifier.fillMaxSize()) {
+ LazyColumn {
+ items(20) {
+ Box(
+ Modifier
+ .fillMaxWidth()
+ .height(150.dp)
+ .padding(15.dp)
+ .background(MaterialTheme.colors.primary)
+ )
+ }
+ }
+ }
+ var selectedAlignment by remember { mutableStateOf(Alignment.Center) }
+ var contentScale by remember { mutableStateOf(ContentScale.FillWidth) }
+ Column(
+ Modifier.padding(top = 100.dp)
+ ) {
+ Column(
+ Modifier
+ .background(Color.LightGray, RoundedCornerShape(10.dp)),
+ ) {
+ Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
+ RadioButton(
+ selected = selectedAlignment == Alignment.TopStart,
+ onClick = { selectedAlignment = Alignment.TopStart }
+ )
+ Text("TopStart", Modifier.padding(5.dp))
+ RadioButton(
+ selected = selectedAlignment == Alignment.TopCenter,
+ onClick = { selectedAlignment = Alignment.TopCenter }
+ )
+ Text("TopCenter", Modifier.padding(5.dp))
+ RadioButton(
+ selected = selectedAlignment == Alignment.TopEnd,
+ onClick = { selectedAlignment = Alignment.TopEnd }
+ )
+ Text("TopEnd", Modifier.padding(5.dp))
+ }
+ Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
+ RadioButton(
+ selected = selectedAlignment == Alignment.CenterStart,
+ onClick = { selectedAlignment = Alignment.CenterStart }
+ )
+ Text("CenterStart", Modifier.padding(5.dp))
+ RadioButton(
+ selected = selectedAlignment == Alignment.Center,
+ onClick = { selectedAlignment = Alignment.Center }
+ )
+ Text("Center", Modifier.padding(5.dp))
+ RadioButton(
+ selected = selectedAlignment == Alignment.CenterEnd,
+ onClick = { selectedAlignment = Alignment.CenterEnd }
+ )
+ Text("CenterEnd", Modifier.padding(5.dp))
+ }
+ Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
+ RadioButton(
+ selected = selectedAlignment == Alignment.BottomStart,
+ onClick = { selectedAlignment = Alignment.BottomStart }
+ )
+ Text("BottomStart", Modifier.padding(5.dp))
+ RadioButton(
+ selected = selectedAlignment == Alignment.BottomCenter,
+ onClick = { selectedAlignment = Alignment.BottomCenter }
+ )
+ Text("BottomCenter", Modifier.padding(5.dp))
+ RadioButton(
+ selected = selectedAlignment == Alignment.BottomEnd,
+ onClick = { selectedAlignment = Alignment.BottomEnd }
+ )
+ Text("BottomEnd", Modifier.padding(5.dp))
+ }
+ }
+ Column(
+ Modifier
+ .background(Color.Gray, RoundedCornerShape(10.dp)),
+ ) {
+ Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
+ RadioButton(
+ selected = contentScale == ContentScale.FillWidth,
+ onClick = { contentScale = ContentScale.FillWidth }
+ )
+ Text("FillWidth", Modifier.padding(5.dp))
+ RadioButton(
+ selected = contentScale == ContentScale.FillHeight,
+ onClick = { contentScale = ContentScale.FillHeight }
+ )
+ Text("FillHeight", Modifier.padding(5.dp))
+ RadioButton(
+ selected = contentScale == ContentScale.FillBounds,
+ onClick = { contentScale = ContentScale.FillBounds }
+ )
+ Text("FillBounds", Modifier.padding(5.dp))
+ }
+ Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
+ RadioButton(
+ selected = contentScale == ContentScale.Crop,
+ onClick = { contentScale = ContentScale.Crop }
+ )
+ Text("Crop", Modifier.padding(5.dp))
+ RadioButton(
+ selected = contentScale == ContentScale.Fit,
+ onClick = { contentScale = ContentScale.Fit }
+ )
+ Text("Fit", Modifier.padding(5.dp))
+ RadioButton(
+ selected = contentScale == ContentScale.Inside,
+ onClick = { contentScale = ContentScale.Inside }
+ )
+ Text("Inside", Modifier.padding(5.dp))
+ }
+ }
+ }
+ Box(Modifier.fillMaxSize()) {
+ var target by remember { mutableStateOf(ContainerState.FAB) }
+ // Corner radius
+ val cr by animateIntAsState(if (target == ContainerState.FAB) 50 else 0)
+ val padding by animateDpAsState(if (target == ContainerState.FAB) 10.dp else 0.dp)
+ AnimatedContent(
+ target,
+ label = "",
+ transitionSpec = {
+ fadeIn(tween(200, delayMillis = 100)) +
+ scaleInToFitContainer(selectedAlignment, contentScale) togetherWith
+ fadeOut(tween(100)) + scaleOutToFitContainer(selectedAlignment, contentScale)
+ },
+ modifier = Modifier
+ .align(Alignment.BottomEnd)
+ .padding(padding)
+ .clip(RoundedCornerShape(cr))
+ .background(Color.White)
+ ) {
+ if (it == ContainerState.FAB) {
+ Icon(
+ rememberVectorPainter(image = Icons.Default.Add),
+ null,
+ modifier = Modifier
+ .clickable {
+ target = ContainerState.FullScreen
+ }
+ .padding(20.dp))
+ } else {
+ Column(Modifier.fillMaxSize()) {
+ Icon(
+ rememberVectorPainter(image = Icons.Default.ArrowBack),
+ null,
+ modifier = Modifier
+ .clickable {
+ target = ContainerState.FAB
+ }
+ .padding(20.dp))
+ Spacer(Modifier.height(60.dp))
+ Text("Page Title", fontSize = 20.sp, modifier = Modifier.padding(20.dp))
+ Spacer(
+ Modifier
+ .fillMaxWidth()
+ .height(2.dp)
+ .background(Color.LightGray)
+ )
+ Row(
+ Modifier
+ .fillMaxWidth()
+ .padding(20.dp)
+ ) {
+ Icon(rememberVectorPainter(image = Icons.Default.AccountCircle), null)
+ Spacer(Modifier.width(20.dp))
+ TextField(value = "Account Name", onValueChange = {})
+ }
+ }
+ }
+ }
+ }
+}
+
+private enum class ContainerState {
+ FAB,
+ FullScreen
+}
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/AnimateBoundsModifier.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/AnimateBoundsModifier.kt
index 96a707a..707240c 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/AnimateBoundsModifier.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/AnimateBoundsModifier.kt
@@ -33,7 +33,11 @@
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
+import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.drawscope.Stroke
+import androidx.compose.ui.graphics.drawscope.translate
import androidx.compose.ui.layout.LookaheadScope
import androidx.compose.ui.layout.Placeable
import androidx.compose.ui.layout.intermediateLayout
@@ -54,6 +58,7 @@
Spring.DampingRatioNoBouncy,
Spring.StiffnessMediumLow
),
+ debug: Boolean = false,
lookaheadScope: (closestLookaheadScope: LookaheadScope) -> LookaheadScope = { it }
) = composed {
@@ -68,6 +73,17 @@
// When the measure block is invoked after lookahead pass, the lookahead size of the
// child will be accessible as a parameter to the measure block.
this
+ .drawWithContent {
+ drawContent()
+ if (debug) {
+ val offset = outerOffsetAnimation.target!! - outerOffsetAnimation.value!!
+ translate(
+ offset.x.toFloat(), offset.y.toFloat()
+ ) {
+ drawRect(Color.Black.copy(alpha = 0.5f), style = Stroke(10f))
+ }
+ }
+ }
.intermediateLayout { measurable, constraints ->
val (w, h) = outerSizeAnimation.updateTarget(
lookaheadSize,
@@ -85,6 +101,17 @@
}
}
.then(modifier)
+ .drawWithContent {
+ drawContent()
+ if (debug) {
+ val offset = offsetAnimation.target!! - offsetAnimation.value!!
+ translate(
+ offset.x.toFloat(), offset.y.toFloat()
+ ) {
+ drawRect(Color.Green.copy(alpha = 0.5f), style = Stroke(10f))
+ }
+ }
+ }
.intermediateLayout { measurable, _ ->
// When layout changes, the lookahead pass will calculate a new final size for the
// child modifier. This lookahead size can be used to animate the size
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithFlowRowDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithFlowRowDemo.kt
index adba4fa..35058df 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithFlowRowDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/lookahead/LookaheadWithFlowRowDemo.kt
@@ -16,18 +16,26 @@
package androidx.compose.animation.demos.lookahead
+import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
+import androidx.compose.foundation.layout.FlowColumn
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Button
import androidx.compose.material.Text
@@ -41,6 +49,8 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.LookaheadScope
+import androidx.compose.ui.layout.layout
+import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@OptIn(ExperimentalComposeUiApi::class, ExperimentalLayoutApi::class)
@@ -135,6 +145,77 @@
}
}
+@OptIn(ExperimentalComposeUiApi::class, ExperimentalLayoutApi::class)
+@Preview
+@Composable
+fun NestedFlowRowDemo() {
+ LookaheadScope {
+ FlowRow(
+ modifier = Modifier.fillMaxSize(),
+ horizontalArrangement = Arrangement.Center,
+ verticalArrangement = Arrangement.Center,
+ maxItemsInEachRow = 3
+ ) {
+ var expanded by remember {
+ mutableStateOf(false)
+ }
+ Box(
+ modifier = Modifier
+ .animateBounds(Modifier.widthIn(max = 600.dp))
+ .background(Color.Red)
+ ) {
+ val height = animateDpAsState(targetValue = if (expanded) 500.dp else 300.dp)
+ Box(
+ modifier = Modifier
+ .animateBounds(
+ Modifier
+ .fillMaxWidth()
+ .height(height.value)
+ )
+ .clickable {
+ expanded = !expanded
+ })
+ }
+
+ FlowColumn(Modifier.layout { measurable, constraints ->
+ measurable.measure(constraints).run {
+ layout(width, height) {
+ place(0, 0)
+ }
+ }
+ }) {
+ Box(
+ modifier = Modifier
+ .size(200.dp)
+ .animateBounds(
+ Modifier
+ .wrapContentWidth()
+ .heightIn(min = 156.dp),
+ debug = true
+
+ )
+ .background(Color.Blue)
+ ) {
+ Box(modifier = Modifier.size(200.dp))
+ }
+ Box(
+ modifier = Modifier
+ .size(200.dp)
+ .animateBounds(
+ Modifier
+ .wrapContentWidth()
+ .heightIn(min = 156.dp),
+ debug = true
+ )
+ .background(Color.Yellow)
+ ) {
+ Box(modifier = Modifier.size(200.dp))
+ }
+ }
+ }
+ }
+}
+
private val colors = listOf(
Color(0xffff6f69),
Color(0xffffcc5c),
diff --git a/compose/animation/animation/samples/src/main/java/androidx/compose/animation/samples/AnimatedContentSamples.kt b/compose/animation/animation/samples/src/main/java/androidx/compose/animation/samples/AnimatedContentSamples.kt
index f21c93f..f0e325b 100644
--- a/compose/animation/animation/samples/src/main/java/androidx/compose/animation/samples/AnimatedContentSamples.kt
+++ b/compose/animation/animation/samples/src/main/java/androidx/compose/animation/samples/AnimatedContentSamples.kt
@@ -22,6 +22,7 @@
import androidx.compose.animation.AnimatedContentTransitionScope.SlideDirection
import androidx.compose.animation.ContentTransform
import androidx.compose.animation.ExitTransition
+import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.SizeTransform
import androidx.compose.animation.core.animateDp
import androidx.compose.animation.core.keyframes
@@ -54,6 +55,7 @@
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@@ -116,7 +118,8 @@
Box(
Modifier
.size(200.dp)
- .background(Color(0xffffdb00)))
+ .background(Color(0xffffdb00))
+ )
}
@Composable
@@ -124,7 +127,8 @@
Box(
Modifier
.size(40.dp)
- .background(Color(0xffff8100)))
+ .background(Color(0xffff8100))
+ )
}
@Composable
@@ -132,7 +136,8 @@
Box(
Modifier
.size(80.dp, 20.dp)
- .background(Color(0xffff4400)))
+ .background(Color(0xffff4400))
+ )
}
var contentState: ContentState by remember { mutableStateOf(ContentState.Foo) }
@@ -304,6 +309,30 @@
}
}
+@OptIn(ExperimentalAnimationApi::class)
+@Suppress("UNUSED_VARIABLE")
+@Sampled
+@Composable
+fun ScaleInToFitContainerSample() {
+ // enum class CartState { Expanded, Collapsed }
+ // This is an example of scaling both the incoming content and outgoing content to fit in the
+ // animating container size while animating alpha.
+ val transitionSpec: AnimatedContentTransitionScope<CartState>.() -> ContentTransform = {
+ // Fade in while scaling the content.
+ fadeIn() + scaleInToFitContainer() togetherWith
+ // Fade out outgoing content while scaling it. It is important
+ // to combine `scaleOutToFitContainer` with another ExitTransition that defines
+ // a timeframe for the exit (such as fade/shrink/slide/Hold).
+ fadeOut() + scaleOutToFitContainer(
+ // Default alignment is the content alignment defined in AnimatedContent
+ Alignment.Center,
+ // Content will be scaled based on the height of the content. Default content
+ // scale is ContentScale.FillWidth.
+ ContentScale.FillHeight
+ )
+ }
+}
+
private enum class CartState {
Expanded,
Collapsed
diff --git a/compose/animation/animation/src/androidAndroidTest/kotlin/androidx/compose/animation/AnimatedContentTest.kt b/compose/animation/animation/src/androidAndroidTest/kotlin/androidx/compose/animation/AnimatedContentTest.kt
index 9540bd4..c6925f9 100644
--- a/compose/animation/animation/src/androidAndroidTest/kotlin/androidx/compose/animation/AnimatedContentTest.kt
+++ b/compose/animation/animation/src/androidAndroidTest/kotlin/androidx/compose/animation/AnimatedContentTest.kt
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalAnimationApi::class)
+
package androidx.compose.animation
import androidx.compose.animation.core.InternalAnimationApi
@@ -41,11 +43,16 @@
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.layout.boundsInRoot
import androidx.compose.ui.layout.onGloballyPositioned
+import androidx.compose.ui.layout.onPlaced
import androidx.compose.ui.layout.positionInRoot
import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
@@ -54,6 +61,7 @@
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.round
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import com.google.common.truth.Truth.assertThat
@@ -656,6 +664,400 @@
assertTrue(box2EnterFinished)
}
+ @Test
+ fun testScaleToFitDefault() {
+ var target by mutableStateOf(1)
+ var box1Coords: LayoutCoordinates? = null
+ var box2Coords: LayoutCoordinates? = null
+ var box1Disposed = true
+ var box2Disposed = true
+ rule.setContent {
+ CompositionLocalProvider(LocalDensity provides Density(1f)) {
+ AnimatedContent(
+ targetState = target,
+ transitionSpec = {
+ if (1 isTransitioningTo 2) {
+ fadeIn(tween(300)) + scaleInToFitContainer() togetherWith
+ scaleOutToFitContainer()
+ } else {
+ fadeIn() + scaleInToFitContainer() togetherWith
+ fadeOut(tween(150))
+ } using SizeTransform { initialSize, targetSize ->
+ keyframes {
+ durationMillis = 300
+ initialSize at 100 with LinearEasing
+ targetSize at 200 with LinearEasing
+ }
+ }
+ }) {
+ if (it == 1) {
+ Box(
+ Modifier
+ .onPlaced {
+ box1Coords = it
+ }
+ .size(200.dp, 400.dp)) {
+ DisposableEffect(key1 = Unit) {
+ box1Disposed = false
+ onDispose {
+ box1Disposed = true
+ }
+ }
+ }
+ } else {
+ Box(
+ Modifier
+ .onPlaced { box2Coords = it }
+ .size(100.dp, 50.dp)) {
+
+ DisposableEffect(key1 = Unit) {
+ box2Disposed = false
+ onDispose {
+ box2Disposed = true
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ rule.waitForIdle()
+ rule.mainClock.autoAdvance = false
+ assertEquals(IntSize(200, 400), box1Coords?.size)
+ assertNull(box2Coords)
+
+ assertFalse(box1Disposed)
+ assertTrue(box2Disposed)
+
+ rule.runOnIdle {
+ // Start transition from 1 -> 2, size 200,400 -> 100,50
+ target = 2
+ }
+ rule.mainClock.advanceTimeByFrame()
+ rule.waitForIdle()
+
+ // Box1 doesn't have any other ExitTransition than scale, so it'll be disposed
+ // after a couple of frames
+ assertFalse(box1Disposed)
+ assertFalse(box2Disposed)
+
+ repeat(20) {
+ rule.mainClock.advanceTimeByFrame()
+
+ val playTime = 16 * it
+ val bounds2 = box2Coords?.boundsInRoot()
+ if (playTime <= 100) {
+ assertEquals(Rect(0f, 0f, 200f, 100f), bounds2)
+ } else if (playTime <= 200) {
+ val fraction = (playTime - 100) / 100f
+ val width = 200 * (1 - fraction) + 100 * fraction
+ // Since we are testing default behavior, the scaling is based on width.
+ val height = width / 100f * 50
+ assertEquals(Offset.Zero, bounds2?.topLeft)
+ assertEquals(width, bounds2?.width)
+ assertEquals(height, bounds2?.height)
+ } else {
+ assertEquals(Rect(0f, 0f, 100f, 50f), bounds2)
+ }
+ }
+
+ rule.runOnIdle {
+ // Start transition from false -> true, size 100, 50 -> 200,400
+ target = 1
+ }
+ rule.mainClock.advanceTimeByFrame()
+ rule.waitForIdle()
+
+ assertFalse(box1Disposed)
+ assertFalse(box2Disposed)
+
+ repeat(20) {
+ rule.mainClock.advanceTimeByFrame()
+ val playTime = 16 * it
+ val bounds = box1Coords?.boundsInRoot()
+ if (playTime <= 100) {
+ assertEquals(100f, bounds?.width)
+ assertFalse(box2Disposed)
+ } else if (playTime <= 150) {
+ val fraction = (playTime - 100) / 100f
+ val width = 100 * (1 - fraction) + 200 * fraction
+ // Since we are testing default behavior, the scaling is based on width.
+ assertEquals(Offset.Zero, bounds?.topLeft)
+ assertEquals(width, bounds?.width)
+ } else {
+ rule.waitForIdle()
+ assertThat(box2Disposed)
+ }
+ }
+ }
+
+ @Test
+ fun testScaleToFitCenterAlignment() {
+ var target by mutableStateOf(true)
+ var box1Coords: LayoutCoordinates? = null
+ var box2Coords: LayoutCoordinates? = null
+ var layoutDirection: LayoutDirection? = null
+ rule.setContent {
+ CompositionLocalProvider(LocalDensity provides Density(1f)) {
+ layoutDirection = LocalLayoutDirection.current
+ AnimatedContent(
+ targetState = target,
+ transitionSpec = {
+ fadeIn() + scaleInToFitContainer(Alignment.Center) togetherWith
+ fadeOut(tween(100)) using
+ SizeTransform { _, _ ->
+ tween(100, easing = LinearEasing)
+ }
+ }) {
+ if (target) {
+ Box(
+ Modifier
+ .onPlaced {
+ box1Coords = it
+ }
+ .size(200.dp, 400.dp))
+ } else {
+ Box(
+ Modifier
+ .onPlaced { box2Coords = it }
+ .size(100.dp, 50.dp))
+ }
+ }
+ }
+ }
+
+ rule.waitForIdle()
+ assertEquals(IntSize(200, 400), box1Coords?.size)
+ assertNull(box2Coords)
+
+ rule.runOnIdle {
+ // Start transition from true -> false, size 200,400 -> 100,50
+ target = false
+ }
+ rule.mainClock.advanceTimeByFrame()
+ repeat(10) {
+ rule.mainClock.advanceTimeByFrame()
+ val playTime = 16 * it
+ val bounds = box2Coords?.boundsInRoot()
+ assertNotNull(bounds)
+ val fraction = (playTime / 100f).coerceAtMost(1f)
+ val width = 200 * (1 - fraction) + 100 * fraction
+ val containerHeight = 400 * (1 - fraction) + 50 * fraction
+ // Since we are testing default behavior, the scaling is based on width.
+ val height = width / 100f * 50
+ assertEquals(width, bounds!!.width, 0.01f)
+ assertEquals(height, bounds.height, 0.01f)
+ val offset = Alignment.Center.align(
+ IntSize(width.roundToInt(), height.roundToInt()),
+ IntSize(width.roundToInt(), containerHeight.roundToInt()), layoutDirection!!
+ )
+ assertEquals(offset, bounds.topLeft.round())
+ }
+ }
+
+ @Test
+ fun testScaleToFitBottomCenterAlignment() {
+ var target by mutableStateOf(true)
+ var box1Coords: LayoutCoordinates? = null
+ var box2Coords: LayoutCoordinates? = null
+ var layoutDirection: LayoutDirection? = null
+ rule.setContent {
+ CompositionLocalProvider(LocalDensity provides Density(1f)) {
+ layoutDirection = LocalLayoutDirection.current
+ AnimatedContent(
+ targetState = target,
+ transitionSpec = {
+ fadeIn() + scaleInToFitContainer(
+ Alignment.BottomCenter
+ ) togetherWith
+ fadeOut(tween(100)) using
+ SizeTransform { _, _ ->
+ tween(100, easing = LinearEasing)
+ }
+ }) {
+ if (target) {
+ Box(
+ Modifier
+ .onPlaced {
+ box1Coords = it
+ }
+ .size(200.dp, 400.dp))
+ } else {
+ Box(
+ Modifier
+ .onPlaced { box2Coords = it }
+ .size(100.dp, 50.dp))
+ }
+ }
+ }
+ }
+
+ rule.waitForIdle()
+ rule.mainClock.autoAdvance = false
+ assertEquals(IntSize(200, 400), box1Coords?.size)
+ assertNull(box2Coords)
+
+ rule.runOnIdle {
+ // Start transition from true -> false, size 200,400 -> 100,50
+ target = false
+ }
+ rule.mainClock.advanceTimeByFrame()
+ repeat(10) {
+ rule.mainClock.advanceTimeByFrame()
+ val playTime = 16 * it
+ val bounds = box2Coords?.boundsInRoot()
+ assertNotNull(bounds)
+ val fraction = (playTime / 100f).coerceAtMost(1f)
+ val width = 200 * (1 - fraction) + 100 * fraction
+ val containerHeight = 400 * (1 - fraction) + 50 * fraction
+ // Since we are testing default behavior, the scaling is based on width.
+ val height = width / 100f * 50
+ assertEquals(width, bounds!!.width, 0.01f)
+ assertEquals(height, bounds.height, 0.01f)
+ val offset = Alignment.BottomCenter.align(
+ IntSize(width.roundToInt(), height.roundToInt()),
+ IntSize(width.roundToInt(), containerHeight.roundToInt()), layoutDirection!!
+ )
+ assertEquals(offset, bounds.topLeft.round())
+ }
+ }
+
+ @Test
+ fun testScaleToFitInsideBottomEndAlignment() {
+ var target by mutableStateOf(true)
+ var box1Coords: LayoutCoordinates? = null
+ var box2Coords: LayoutCoordinates? = null
+ var layoutDirection: LayoutDirection? = null
+ rule.setContent {
+ CompositionLocalProvider(LocalDensity provides Density(1f)) {
+ layoutDirection = LocalLayoutDirection.current
+ AnimatedContent(
+ targetState = target,
+ transitionSpec = {
+ fadeIn() + scaleInToFitContainer(
+ Alignment.BottomEnd, ContentScale.Inside
+ ) togetherWith
+ fadeOut(tween(100)) using
+ SizeTransform { _, _ ->
+ tween(100, easing = LinearEasing)
+ }
+ }) {
+ if (target) {
+ Box(
+ Modifier
+ .onPlaced {
+ box1Coords = it
+ }
+ .size(200.dp, 400.dp))
+ } else {
+ Box(
+ Modifier
+ .onPlaced { box2Coords = it }
+ .size(100.dp, 50.dp))
+ }
+ }
+ }
+ }
+
+ rule.waitForIdle()
+ rule.mainClock.autoAdvance = false
+ assertEquals(IntSize(200, 400), box1Coords?.size)
+ assertNull(box2Coords)
+
+ rule.runOnIdle {
+ // Start transition from true -> false, size 200,400 -> 100,50
+ target = false
+ }
+ rule.mainClock.advanceTimeByFrame()
+ repeat(10) {
+ rule.mainClock.advanceTimeByFrame()
+ val playTime = 16 * it
+ val bounds = box2Coords?.boundsInRoot()
+ assertNotNull(bounds)
+ val fraction = (playTime / 100f).coerceAtMost(1f)
+ val width = 100f
+ val containerWidth = 200 * (1 - fraction) + 100 * fraction
+ val containerHeight = 400 * (1 - fraction) + 50 * fraction
+ // Since we are testing default behavior, the scaling is based on width.
+ val height = 50f
+ assertEquals(width, bounds!!.width, 0.01f)
+ assertEquals(height, bounds.height, 0.01f)
+ val offset = Alignment.BottomEnd.align(
+ IntSize(width.roundToInt(), height.roundToInt()),
+ IntSize(containerWidth.roundToInt(), containerHeight.roundToInt()),
+ layoutDirection!!
+ )
+ assertEquals(offset, bounds.topLeft.round())
+ }
+ }
+
+ @Test
+ fun testScaleToFitWithFitHeight() {
+ var target by mutableStateOf(true)
+ var box1Coords: LayoutCoordinates? = null
+ var box2Coords: LayoutCoordinates? = null
+ var layoutDirection: LayoutDirection? = null
+ rule.setContent {
+ CompositionLocalProvider(LocalDensity provides Density(1f)) {
+ layoutDirection = LocalLayoutDirection.current
+ AnimatedContent(
+ targetState = target,
+ transitionSpec = {
+ fadeIn() + scaleInToFitContainer(
+ Alignment.Center, ContentScale.FillHeight
+ ) togetherWith fadeOut(tween(100)) using
+ SizeTransform { _, _ ->
+ tween(100, easing = LinearEasing)
+ }
+ }) {
+ if (target) {
+ Box(
+ Modifier
+ .onPlaced {
+ box1Coords = it
+ }
+ .size(200.dp, 400.dp))
+ } else {
+ Box(
+ Modifier
+ .onPlaced { box2Coords = it }
+ .size(100.dp, 250.dp))
+ }
+ }
+ }
+ }
+
+ rule.waitForIdle()
+ rule.mainClock.autoAdvance = false
+ assertEquals(IntSize(200, 400), box1Coords?.size)
+ assertNull(box2Coords)
+
+ rule.runOnIdle {
+ // Start transition from true -> false, size 200,400 -> 100,250
+ target = false
+ }
+ rule.mainClock.advanceTimeByFrame()
+ repeat(10) {
+ rule.mainClock.advanceTimeByFrame()
+ val playTime = 16 * it
+ val bounds = box2Coords?.boundsInRoot()
+ assertNotNull(bounds)
+ val fraction = (playTime / 100f).coerceAtMost(1f)
+ val height = 400 * (1 - fraction) + 250 * fraction
+ val containerWidth = 200 * (1 - fraction) + 100 * fraction
+ // Since we are testing default behavior, the scaling is based on width.
+ val width = height / 250f * 100
+ assertEquals(width, bounds!!.width, 0.01f)
+ assertEquals(height, bounds.height, 0.01f)
+ val offset = Alignment.Center.align(
+ IntSize(width.roundToInt(), height.roundToInt()),
+ IntSize(containerWidth.roundToInt(), height.roundToInt()), layoutDirection!!
+ )
+ assertEquals(offset, bounds.topLeft.round())
+ }
+ }
+
@OptIn(ExperimentalAnimationApi::class)
@Test
fun testExitHoldDefersUntilAllFinished() {
diff --git a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedContent.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedContent.kt
index 4449080..da44816 100644
--- a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedContent.kt
+++ b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedContent.kt
@@ -13,8 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-@file:OptIn(InternalAnimationApi::class)
+@file:OptIn(InternalAnimationApi::class, ExperimentalAnimationApi::class)
package androidx.compose.animation
@@ -38,6 +37,7 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.MutableState
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
@@ -45,14 +45,20 @@
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.setValue
+import androidx.compose.runtime.snapshots.SnapshotStateList
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clipToBounds
+import androidx.compose.ui.graphics.TransformOrigin
+import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.IntrinsicMeasurable
import androidx.compose.ui.layout.IntrinsicMeasureScope
import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.layout.LookaheadScope
import androidx.compose.ui.layout.Measurable
import androidx.compose.ui.layout.MeasurePolicy
import androidx.compose.ui.layout.MeasureResult
@@ -60,14 +66,20 @@
import androidx.compose.ui.layout.ParentDataModifier
import androidx.compose.ui.layout.Placeable
import androidx.compose.ui.layout.layout
+import androidx.compose.ui.node.LayoutModifierNode
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.platform.InspectorInfo
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.toSize
import androidx.compose.ui.util.fastForEach
import androidx.compose.ui.util.fastForEachIndexed
+import kotlin.math.roundToInt
+import kotlinx.coroutines.CoroutineScope
/**
* [AnimatedContent] is a container that automatically animates its content when [targetState]
@@ -283,9 +295,7 @@
* [AnimatedContentTransitionScope] provides functions that are convenient and only applicable in the
* context of [AnimatedContent], such as [slideIntoContainer] and [slideOutOfContainer].
*/
-
sealed interface AnimatedContentTransitionScope<S> : Transition.Segment<S> {
-
/**
* Customizes the [SizeTransform] of a given [ContentTransform]. For example:
*
@@ -395,13 +405,67 @@
* @sample androidx.compose.animation.samples.SlideIntoContainerSample
*/
val ExitTransition.Companion.Hold: ExitTransition get() = Hold
+
+ /**
+ * This returns the [Alignment] specified on [AnimatedContent].
+ */
+ val contentAlignment: Alignment
+
+ /**
+ * [scaleInToFitContainer] defines an [EnterTransition] that scales the incoming content
+ * based on the (potentially animating) container (i.e. [AnimatedContent]) size. [contentScale]
+ * defines the scaling function. By default, the incoming content will be scaled based on its
+ * width (i.e. [ContentScale.FillWidth]), so that the content fills the container's width.
+ * [alignment] can be used to specify the alignment of the scaled content
+ * within the container of AnimatedContent.
+ *
+ * [scaleInToFitContainer] will measure the content using the final (i.e. lookahead)
+ * constraints, in order to obtain the final layout and apply scaling to that final layout
+ * while the container is resizing.
+ *
+ * @sample androidx.compose.animation.samples.ScaleInToFitContainerSample
+ */
+ @ExperimentalAnimationApi
+ fun scaleInToFitContainer(
+ alignment: Alignment = contentAlignment,
+ contentScale: ContentScale = ContentScale.FillWidth
+ ): EnterTransition
+
+ /**
+ * [scaleOutToFitContainer] defines an [ExitTransition] that scales the outgoing content
+ * based on the (potentially animating) container (i.e. [AnimatedContent]) size.
+ * [contentScale] defines the scaling function. By default, the outgoing content will be scaled
+ * using [ContentScale.FillWidth], so that it fits the container's width.
+ * [alignment] can be used to specify the alignment of the scaled content
+ * within the container of AnimatedContent.
+ *
+ * [scaleOutToFitContainer] will measure the content using the constraints cached
+ * at the beginning of the exit animation so that the content does not get re-laid out during
+ * the exit animation, and instead only scaling will be applied as the container resizes.
+ *
+ * **IMPORTANT**: [scaleOutToFitContainer] does NOT keep the exiting content from being
+ * disposed. Therefore it relies on other ExitTransitions such as [fadeOut] to define a
+ * timeframe for when should be active.
+ *
+ * @sample androidx.compose.animation.samples.ScaleInToFitContainerSample
+ */
+ @ExperimentalAnimationApi
+ fun scaleOutToFitContainer(
+ alignment: Alignment = contentAlignment,
+ contentScale: ContentScale = ContentScale.FillWidth,
+ ): ExitTransition
}
-internal class AnimatedContentTransitionScopeImpl<S> internal constructor(
+internal class AnimatedContentRootScope<S> internal constructor(
internal val transition: Transition<S>,
- internal var contentAlignment: Alignment,
+ lookaheadScope: LookaheadScope,
+ internal val coroutineScope: CoroutineScope,
+ override var contentAlignment: Alignment,
internal var layoutDirection: LayoutDirection
-) : AnimatedContentTransitionScope<S> {
+) : AnimatedContentTransitionScope<S>, LookaheadScope by lookaheadScope {
+ lateinit var rootCoords: LayoutCoordinates
+ lateinit var rootLookaheadCoords: LayoutCoordinates
+
/**
* Initial state of a Transition Segment. This is the state that transition starts from.
*/
@@ -484,7 +548,6 @@
return this == Left || this == Start && layoutDirection == LayoutDirection.Ltr ||
this == End && layoutDirection == LayoutDirection.Rtl
}
-
private val AnimatedContentTransitionScope.SlideDirection.isRight: Boolean
get() {
return this == Right || this == Start && layoutDirection == LayoutDirection.Rtl ||
@@ -532,7 +595,6 @@
}
towards.isRight -> slideOutHorizontally(animationSpec) {
-
val targetSize = targetSizeMap[transition.targetState]?.value ?: IntSize.Zero
targetOffset.invoke(
-calculateOffset(IntSize(it, it), targetSize).x + targetSize.width
@@ -540,7 +602,6 @@
}
towards == Up -> slideOutVertically(animationSpec) {
-
val targetSize = targetSizeMap[transition.targetState]?.value ?: IntSize.Zero
targetOffset.invoke(-calculateOffset(IntSize(it, it), targetSize).y - it)
}
@@ -556,15 +617,45 @@
}
}
+ @ExperimentalAnimationApi
+ override fun scaleInToFitContainer(
+ alignment: Alignment,
+ contentScale: ContentScale
+ ): EnterTransition = EnterTransition(
+ ScaleToFitTransitionKey, ScaleToFitInLookaheadElement(
+ this@AnimatedContentRootScope,
+ contentScale,
+ alignment
+ )
+ )
+
+ @ExperimentalAnimationApi
+ override fun scaleOutToFitContainer(
+ alignment: Alignment,
+ contentScale: ContentScale
+ ): ExitTransition = ExitTransition(
+ ScaleToFitTransitionKey,
+ ScaleToFitInLookaheadElement(
+ this@AnimatedContentRootScope,
+ contentScale,
+ alignment
+ )
+ )
+
internal var measuredSize: IntSize by mutableStateOf(IntSize.Zero)
- internal val targetSizeMap = mutableMapOf<S, State<IntSize>>()
+ internal val targetSizeMap = mutableMapOf<S, MutableState<IntSize>>()
internal var animatedSize: State<IntSize>? = null
// Current size of the container. If there's any size animation, the current size will be
// read from the animation value, otherwise we'll use the current
- private val currentSize: IntSize
+ internal val currentSize: IntSize
get() = animatedSize?.value ?: measuredSize
+ internal val targetSize: IntSize
+ get() = requireNotNull(targetSizeMap[targetState]) {
+ "Error: Target size for AnimatedContent has not been set."
+ }.value
+
@Suppress("ComposableModifierFactory", "ModifierFactoryExtensionFunction")
@Composable
internal fun createSizeAnimationModifier(
@@ -574,17 +665,20 @@
val sizeTransform = rememberUpdatedState(contentTransform.sizeTransform)
if (transition.currentState == transition.targetState) {
shouldAnimateSize = false
- } else {
- // TODO: CurrentSize is only relevant to enter/exit transition, not so much for sizeAnim
- if (sizeTransform.value != null) {
- shouldAnimateSize = true
- }
+ } else if (sizeTransform.value != null) {
+ shouldAnimateSize = true
}
+
return if (shouldAnimateSize) {
- val sizeAnimation = transition.createDeferredAnimation(IntSize.VectorConverter)
+ val sizeAnimation =
+ transition.createDeferredAnimation(IntSize.VectorConverter, "sizeTransform")
remember(sizeAnimation) {
(if (sizeTransform.value?.clip == false) Modifier else Modifier.clipToBounds())
- .then(SizeModifier(sizeAnimation, sizeTransform))
+ .then(
+ SizeModifierInLookaheadElement(
+ this, sizeAnimation, sizeTransform
+ )
+ )
}
} else {
animatedSize = null
@@ -594,42 +688,11 @@
// This helps track the target measurable without affecting the placement order. Target
// measurable needs to be measured first but placed last.
- internal data class ChildData(var isTarget: Boolean) : ParentDataModifier {
+ internal data class ChildData<T>(var targetState: T) : ParentDataModifier {
override fun Density.modifyParentData(parentData: Any?): Any {
return this@ChildData
}
}
-
- private inner class SizeModifier(
- val sizeAnimation: Transition<S>.DeferredAnimation<IntSize, AnimationVector2D>,
- val sizeTransform: State<SizeTransform?>,
- ) : LayoutModifierWithPassThroughIntrinsics() {
-
- override fun MeasureScope.measure(
- measurable: Measurable,
- constraints: Constraints
- ): MeasureResult {
-
- val placeable = measurable.measure(constraints)
- val size = sizeAnimation.animate(
- transitionSpec = {
- val initial = targetSizeMap[initialState]?.value ?: IntSize.Zero
- val target = targetSizeMap[targetState]?.value ?: IntSize.Zero
-
- sizeTransform.value?.createAnimationSpec(initial, target) ?: spring()
- }
- ) {
- targetSizeMap[it]?.value ?: IntSize.Zero
- }
- animatedSize = size
- val offset = contentAlignment.align(
- IntSize(placeable.width, placeable.height), size.value, LayoutDirection.Ltr
- )
- return layout(size.value.width, size.value.height) {
- placeable.place(offset)
- }
- }
- }
}
/**
@@ -706,153 +769,269 @@
content: @Composable() AnimatedContentScope.(targetState: S) -> Unit
) {
val layoutDirection = LocalLayoutDirection.current
- val rootScope = remember(this) {
- AnimatedContentTransitionScopeImpl(this, contentAlignment, layoutDirection)
- }
+ val coroutineScope = rememberCoroutineScope()
+ LookaheadScope {
+ val rootScope = remember(this@AnimatedContent) {
+ AnimatedContentRootScope(
+ this@AnimatedContent, this@LookaheadScope,
+ coroutineScope, contentAlignment, layoutDirection
+ )
+ }
+ val currentlyVisible = remember(this) { mutableStateListOf(currentState) }
+ val contentMap = remember(this) { mutableMapOf<S, @Composable() () -> Unit>() }
+ val constraintsMap = remember { mutableMapOf<S, Constraints>() }
- // TODO: remove screen as soon as they are animated out
- val currentlyVisible = remember(this) { mutableStateListOf(currentState) }
- val contentMap = remember(this) { mutableMapOf<S, @Composable() () -> Unit>() }
-
- // This is needed for tooling because it could change currentState directly,
- // as opposed to changing target only. When that happens we need to clear all the
- // visible content and only display the content for the new current state and target state.
- if (!currentlyVisible.contains(currentState)) {
- currentlyVisible.clear()
- currentlyVisible.add(currentState)
- }
-
- if (currentState == targetState) {
- if (currentlyVisible.size != 1 || currentlyVisible[0] != currentState) {
+ // This is needed for tooling because it could change currentState directly,
+ // as opposed to changing target only. When that happens we need to clear all the
+ // visible content and only display the content for the new current state and target state.
+ if (!currentlyVisible.contains(currentState)) {
currentlyVisible.clear()
currentlyVisible.add(currentState)
}
- if (contentMap.size != 1 || contentMap.containsKey(currentState)) {
+
+ if (currentState == targetState) {
+ if (currentlyVisible.size != 1 || currentlyVisible[0] != currentState) {
+ currentlyVisible.clear()
+ currentlyVisible.add(currentState)
+ }
+ if (contentMap.size != 1 || contentMap.containsKey(currentState)) {
+ contentMap.clear()
+ }
+ val targetConstraints = constraintsMap[targetState]
+ constraintsMap.clear()
+ targetConstraints?.let { constraintsMap[targetState] = it }
+ // TODO: Do we want to support changing contentAlignment amid animation?
+ rootScope.contentAlignment = contentAlignment
+ rootScope.layoutDirection = layoutDirection
+ } else if (!currentlyVisible.contains(targetState)) {
+ // Currently visible list always keeps the targetState at the end of the list, unless
+ // it's already in the list in the case of interruption. This makes the composable
+ // associated with the targetState get placed last, so the target composable will be
+ // displayed on top of content associated with other states, unless zIndex is specified.
+ // Replace the target with the same key if any.
+ val id = currentlyVisible.indexOfFirst { contentKey(it) == contentKey(targetState) }
+ if (id == -1) {
+ currentlyVisible.add(targetState)
+ } else {
+ currentlyVisible[id] = targetState
+ }
+ }
+ if (!contentMap.containsKey(targetState) || !contentMap.containsKey(currentState)) {
contentMap.clear()
- }
- // TODO: Do we want to support changing contentAlignment amid animation?
- rootScope.contentAlignment = contentAlignment
- rootScope.layoutDirection = layoutDirection
- }
-
- // Currently visible list always keeps the targetState at the end of the list, unless it's
- // already in the list in the case of interruption. This makes the composable associated with
- // the targetState get placed last, so the target composable will be displayed on top of
- // content associated with other states, unless zIndex is specified.
- if (currentState != targetState && !currentlyVisible.contains(targetState)) {
- // Replace the target with the same key if any
- val id = currentlyVisible.indexOfFirst { contentKey(it) == contentKey(targetState) }
- if (id == -1) {
- currentlyVisible.add(targetState)
- } else {
- currentlyVisible[id] = targetState
- }
- }
-
- if (!contentMap.containsKey(targetState) || !contentMap.containsKey(currentState)) {
- contentMap.clear()
- currentlyVisible.fastForEach { stateForContent ->
- contentMap[stateForContent] = {
- val specOnEnter = remember { transitionSpec(rootScope) }
- // NOTE: enter and exit for this AnimatedVisibility will be using different spec,
- // naturally.
- val exit =
- remember(segment.targetState == stateForContent) {
- if (segment.targetState == stateForContent) {
- ExitTransition.None
- } else {
- rootScope.transitionSpec().initialContentExit
- }
- }
- val childData = remember {
- AnimatedContentTransitionScopeImpl.ChildData(stateForContent == targetState)
+ val enter = transitionSpec(rootScope).targetContentEnter
+ val exit = rootScope.transitionSpec().initialContentExit
+ val zIndex = transitionSpec(rootScope).targetContentZIndex
+ currentlyVisible.fastForEach { stateForContent ->
+ contentMap[stateForContent] = {
+ PopulateContentFor(
+ stateForContent, rootScope, enter, exit, zIndex, currentlyVisible, content
+ )
}
- // TODO: Will need a custom impl of this to: 1) get the signal for when
- // the animation is finished, 2) get the target size properly
- AnimatedEnterExitImpl(
- this,
- { it == stateForContent },
- enter = specOnEnter.targetContentEnter,
- exit = exit,
- modifier = Modifier
- .layout { measurable, constraints ->
- val placeable = measurable.measure(constraints)
- layout(placeable.width, placeable.height) {
- placeable.place(0, 0, zIndex = specOnEnter.targetContentZIndex)
+ }
+ }
+ val contentTransform = remember(rootScope, segment) { transitionSpec(rootScope) }
+ val sizeModifier = rootScope.createSizeAnimationModifier(contentTransform)
+ Layout(
+ modifier = modifier
+ .layout { measurable, constraints ->
+ val placeable = measurable.measure(constraints)
+ layout(placeable.width, placeable.height) {
+ coordinates?.let {
+ if (isLookingAhead) {
+ rootScope.rootLookaheadCoords = it
+ } else {
+ rootScope.rootCoords = it
}
}
- .then(childData.apply { isTarget = stateForContent == targetState }),
- shouldDisposeBlock = { currentState, targetState ->
- currentState == EnterExitState.PostExit &&
- targetState == EnterExitState.PostExit &&
- !exit.data.hold
- }
- ) {
- // TODO: Should Transition.AnimatedVisibility have an end listener?
- DisposableEffect(this) {
- onDispose {
- currentlyVisible.remove(stateForContent)
- rootScope.targetSizeMap.remove(stateForContent)
- }
- }
- rootScope.targetSizeMap[stateForContent] =
- (this as AnimatedVisibilityScopeImpl).targetSize
- with(remember { AnimatedContentScopeImpl(this) }) {
- content(stateForContent)
+ placeable.place(0, 0)
}
}
+ .then(sizeModifier),
+ content = {
+ currentlyVisible.forEach {
+ key(contentKey(it)) { contentMap[it]?.invoke() }
+ }
+ },
+ measurePolicy = remember {
+ AnimatedContentMeasurePolicy(
+ rootScope, constraintsMap
+ )
}
- }
+ )
}
-
- val contentTransform = remember(rootScope, segment) { transitionSpec(rootScope) }
- val sizeModifier = rootScope.createSizeAnimationModifier(contentTransform)
- Layout(
- modifier = modifier.then(sizeModifier),
- content = {
- currentlyVisible.forEach {
- key(contentKey(it)) {
- contentMap[it]?.invoke()
- }
- }
- },
- measurePolicy = remember { AnimatedContentMeasurePolicy(rootScope) }
- )
}
-private class AnimatedContentMeasurePolicy(val rootScope: AnimatedContentTransitionScopeImpl<*>) :
- MeasurePolicy {
+/**
+ * Creates content for a specific state based on the current Transition, enter/exit and the content
+ * lookup lambda.
+ */
+@Composable
+private inline fun <S> Transition<S>.PopulateContentFor(
+ stateForContent: S,
+ rootScope: AnimatedContentRootScope<S>,
+ enter: EnterTransition,
+ exit: ExitTransition,
+ zIndex: Float,
+ currentlyVisible: SnapshotStateList<S>,
+ crossinline content: @Composable() AnimatedContentScope.(targetState: S) -> Unit
+) {
+ var activeEnter by remember { mutableStateOf(enter) }
+ var activeExit by remember { mutableStateOf(ExitTransition.None) }
+ val targetZIndex = remember { zIndex }
+
+ val isEntering = targetState == stateForContent
+ if (targetState == currentState) {
+ // Transition finished, reset active enter & exit.
+ activeEnter = androidx.compose.animation.EnterTransition.None
+ activeExit = androidx.compose.animation.ExitTransition.None
+ } else if (isEntering) {
+ // If the previous enter transition never finishes when multiple
+ // interruptions happen, avoid adding new enter transitions for simplicity.
+ if (activeEnter == androidx.compose.animation.EnterTransition.None)
+ activeEnter += enter
+ } else {
+ // If the previous exit transition never finishes when multiple
+ // interruptions happen, avoid adding new enter transitions for simplicity.
+ if (activeExit == androidx.compose.animation.ExitTransition.None) {
+ activeExit += exit
+ }
+ }
+ val childData = remember { AnimatedContentRootScope.ChildData(stateForContent) }
+ AnimatedEnterExitImpl(
+ this,
+ { it == stateForContent },
+ enter = activeEnter,
+ exit = activeExit,
+ modifier = Modifier
+ .layout { measurable, constraints ->
+ val placeable = measurable.measure(constraints)
+ layout(placeable.width, placeable.height) {
+ placeable.place(0, 0, zIndex = targetZIndex)
+ }
+ }
+ .then(childData)
+ .then(
+ if (isEntering) {
+ activeEnter[ScaleToFitTransitionKey]
+ ?: activeExit[ScaleToFitTransitionKey] ?: androidx.compose.ui.Modifier
+ } else {
+ activeExit[ScaleToFitTransitionKey]
+ ?: activeEnter[ScaleToFitTransitionKey] ?: androidx.compose.ui.Modifier
+ }
+ ),
+ shouldDisposeBlock = { currentState, targetState ->
+ currentState == androidx.compose.animation.EnterExitState.PostExit &&
+ targetState == androidx.compose.animation.EnterExitState.PostExit &&
+ !activeExit.data.hold
+ },
+ onLookaheadMeasured = {
+ if (isEntering) rootScope.targetSizeMap.getOrPut(targetState) {
+ mutableStateOf(it)
+ }.value = it
+ }
+ ) {
+ // TODO: Should Transition.AnimatedVisibility have an end listener?
+ DisposableEffect(this) {
+ onDispose {
+ currentlyVisible.remove(stateForContent)
+ rootScope.targetSizeMap.remove(stateForContent)
+ }
+ }
+ with(remember { AnimatedContentScopeImpl(this) }) {
+ content(stateForContent)
+ }
+ }
+}
+
+/**
+ * This measure policy returns the target content size in the lookahead pass, and the max width
+ * and height needed for all contents to fit during the main measure pass.
+ *
+ * The measure policy will measure all children with lookahead constraints. For outgoing content,
+ * we will use the constraints recorded before the content started to exit. This enables the
+ * outgoing content to not change constraints on its way out.
+ */
+@Suppress("UNCHECKED_CAST")
+private class AnimatedContentMeasurePolicy<S>(
+ val rootScope: AnimatedContentRootScope<S>,
+ val constraintsMap: MutableMap<S, Constraints>
+) : MeasurePolicy {
override fun MeasureScope.measure(
measurables: List<Measurable>,
constraints: Constraints
): MeasureResult {
val placeables = arrayOfNulls<Placeable>(measurables.size)
// Measure the target composable first (but place it on top unless zIndex is specified)
+ val targetState = rootScope.targetState
measurables.fastForEachIndexed { index, measurable ->
- if ((measurable.parentData as? AnimatedContentTransitionScopeImpl.ChildData)
- ?.isTarget == true
+ if ((measurable.parentData as? AnimatedContentRootScope.ChildData<*>)
+ ?.targetState == targetState
) {
- placeables[index] = measurable.measure(constraints)
+ // Record lookahead constraints and always use it to measure target content.
+ val lookaheadConstraints = if (isLookingAhead) {
+ constraintsMap[targetState] = constraints
+ constraints
+ } else {
+ requireNotNull(constraintsMap[targetState]) {
+ "Lookahead pass was never done for target content."
+ }
+ }
+ placeables[index] = measurable.measure(lookaheadConstraints)
}
}
+ // If no content is defined for target state, set the target size to zero
+ rootScope.targetSizeMap.getOrPut(targetState) { mutableStateOf(IntSize.Zero) }
+
+ val initialState = rootScope.initialState
// Measure the non-target composables after target, since these have no impact on
// container size in the size animation.
measurables.fastForEachIndexed { index, measurable ->
+ val stateForContent =
+ (measurable.parentData as? AnimatedContentRootScope.ChildData<*>)
+ ?.targetState
if (placeables[index] == null) {
- placeables[index] = measurable.measure(constraints)
+ val lookaheadConstraints =
+ constraintsMap[stateForContent] ?: if (isLookingAhead) {
+ constraintsMap[stateForContent as S] = constraints
+ constraints
+ } else {
+ requireNotNull(constraintsMap[stateForContent as S]) {
+ "Error: Lookahead pass never happened for state: $stateForContent"
+ }
+ }
+ placeables[index] = measurable.measure(lookaheadConstraints).also {
+ // If the initial state size isn't in the map, add it. This could be possible
+ // when the initial state is specified to be different than target state upon
+ // entering composition.
+ if (stateForContent == initialState &&
+ isLookingAhead &&
+ !rootScope.targetSizeMap.containsKey(initialState)
+ ) {
+ rootScope.targetSizeMap[initialState] =
+ mutableStateOf(IntSize(it.width, it.height))
+ }
+ }
}
}
-
- val maxWidth: Int = placeables.maxByOrNull { it?.width ?: 0 }?.width ?: 0
- val maxHeight = placeables.maxByOrNull { it?.height ?: 0 }?.height ?: 0
- rootScope.measuredSize = IntSize(maxWidth, maxHeight)
+ val lookaheadSize = rootScope.targetSizeMap[targetState]!!.value
+ val measuredWidth = if (isLookingAhead) {
+ lookaheadSize.width
+ } else {
+ placeables.maxByOrNull { it?.width ?: 0 }?.width ?: 0
+ }
+ val measuredHeight = if (isLookingAhead) {
+ lookaheadSize.height
+ } else {
+ placeables.maxByOrNull { it?.height ?: 0 }?.height ?: 0
+ }
+ rootScope.measuredSize = IntSize(measuredWidth, measuredHeight)
// Position the children.
- return layout(maxWidth, maxHeight) {
+ return layout(measuredWidth, measuredHeight) {
placeables.forEach { placeable ->
placeable?.let {
val offset = rootScope.contentAlignment.align(
IntSize(it.width, it.height),
- IntSize(maxWidth, maxHeight),
+ IntSize(measuredWidth, measuredHeight),
LayoutDirection.Ltr
)
it.place(offset.x, offset.y)
@@ -881,3 +1060,154 @@
width: Int
) = measurables.asSequence().map { it.maxIntrinsicHeight(width) }.maxOrNull() ?: 0
}
+
+private class SizeModifierInLookaheadNode<S>(
+ var rootScope: AnimatedContentRootScope<S>,
+ var sizeAnimation: Transition<S>.DeferredAnimation<IntSize, AnimationVector2D>,
+ var sizeTransform: State<SizeTransform?>,
+) : LayoutModifierNodeWithPassThroughIntrinsics() {
+
+ override fun MeasureScope.measure(
+ measurable: Measurable,
+ constraints: Constraints,
+ ): MeasureResult {
+ val placeable = measurable.measure(constraints)
+ val size = if (isLookingAhead) {
+ val targetSize = IntSize(placeable.width, placeable.height)
+ // lookahead pass
+ rootScope.animatedSize = sizeAnimation.animate(
+ transitionSpec = {
+ val initial = rootScope.targetSizeMap[initialState]?.value ?: IntSize.Zero
+ val target = rootScope.targetSizeMap[targetState]?.value ?: IntSize.Zero
+ sizeTransform.value?.createAnimationSpec(initial, target) ?: spring()
+ }
+ ) {
+ rootScope.targetSizeMap[it]?.value ?: IntSize.Zero
+ }
+ targetSize
+ } else {
+ rootScope.animatedSize!!.value
+ }
+ val offset = rootScope.contentAlignment.align(
+ IntSize(placeable.width, placeable.height), size, LayoutDirection.Ltr
+ )
+ return layout(size.width, size.height) {
+ placeable.place(offset)
+ }
+ }
+}
+
+private data class SizeModifierInLookaheadElement<S>(
+ val rootScope: AnimatedContentRootScope<S>,
+ val sizeAnimation: Transition<S>.DeferredAnimation<IntSize, AnimationVector2D>,
+ val sizeTransform: State<SizeTransform?>,
+) : ModifierNodeElement<SizeModifierInLookaheadNode<S>>() {
+ override fun create(): SizeModifierInLookaheadNode<S> {
+ return SizeModifierInLookaheadNode(rootScope, sizeAnimation, sizeTransform)
+ }
+
+ override fun update(node: SizeModifierInLookaheadNode<S>) {
+ node.rootScope = rootScope
+ node.sizeTransform = sizeTransform
+ node.sizeAnimation = sizeAnimation
+ }
+
+ override fun InspectorInfo.inspectableProperties() {
+ name = "sizeTransform"
+ properties["sizeTransform"] = sizeTransform
+ properties["sizeAnimation"] = sizeAnimation
+ }
+}
+
+private data class ScaleToFitInLookaheadElement(
+ val rootScope: AnimatedContentRootScope<*>,
+ val contentScale: ContentScale,
+ val alignment: Alignment
+) : ModifierNodeElement<ScaleToFitInLookaheadNode>() {
+ override fun create(): ScaleToFitInLookaheadNode =
+ ScaleToFitInLookaheadNode(rootScope, contentScale, alignment)
+
+ override fun update(node: ScaleToFitInLookaheadNode) {
+ node.rootScope = rootScope
+ node.contentScale = contentScale
+ node.alignment = alignment
+ }
+
+ override fun InspectorInfo.inspectableProperties() {
+ name = "scaleToFit"
+ properties["rootScope"] = rootScope
+ properties["scale"] = contentScale
+ properties["alignment"] = alignment
+ }
+}
+
+/**
+ * Creates a Modifier Node to: 1) measure the layout with lookahead constraints, 2) scale the
+ * resulting (potentially unfitting) layout based on the resizing container using the given
+ * [contentScale] lambda.
+ *
+ * This node is designed to work in a lookahead scope, therefore it anticipates lookahead pass
+ * before actual measure pass.
+ */
+private class ScaleToFitInLookaheadNode(
+ var rootScope: AnimatedContentRootScope<*>,
+ var contentScale: ContentScale,
+ var alignment: Alignment
+) : Modifier.Node(), LayoutModifierNode {
+ private var lookaheadConstraints: Constraints = Constraints()
+ set(value) {
+ lookaheadPassOccurred = true
+ field = value
+ }
+ get() {
+ require(lookaheadPassOccurred) {
+ "Error: Attempting to read lookahead constraints before lookahead pass."
+ }
+ return field
+ }
+ private var lookaheadPassOccurred = false
+
+ override fun onDetach() {
+ super.onDetach()
+ lookaheadPassOccurred = false
+ }
+
+ override fun MeasureScope.measure(
+ measurable: Measurable,
+ constraints: Constraints
+ ): MeasureResult {
+ if (isLookingAhead) lookaheadConstraints = constraints
+ // Measure with lookahead constraints.
+ val placeable = measurable.measure(lookaheadConstraints)
+ val contentSize = IntSize(placeable.width, placeable.height)
+ val sizeToReport = if (isLookingAhead) {
+ // report size of the target content, as that's what the content will be scaled to.
+ rootScope.targetSize
+ } else {
+ // report current animated size && scale based on that and full size
+ rootScope.currentSize
+ }
+ val resolvedScale =
+ contentScale.computeScaleFactor(contentSize.toSize(), sizeToReport.toSize())
+ return layout(sizeToReport.width, sizeToReport.height) {
+ val (x, y) = alignment.align(
+ IntSize(
+ (contentSize.width * resolvedScale.scaleX).roundToInt(),
+ (contentSize.height * resolvedScale.scaleY).roundToInt()
+ ),
+ sizeToReport,
+ layoutDirection
+ )
+ placeable.placeWithLayer(x, y) {
+ scaleX = resolvedScale.scaleX
+ scaleY = resolvedScale.scaleY
+ transformOrigin = TransformOrigin(0f, 0f)
+ }
+ }
+ }
+}
+
+/**
+ * Fixed key to read customization out of EnterTransition and ExitTransition.
+ */
+private val ScaleToFitTransitionKey = Any()
diff --git a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedVisibility.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedVisibility.kt
index 7e03d6f..367c85b 100644
--- a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedVisibility.kt
+++ b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/AnimatedVisibility.kt
@@ -49,6 +49,7 @@
import androidx.compose.ui.layout.MeasurePolicy
import androidx.compose.ui.layout.MeasureResult
import androidx.compose.ui.layout.MeasureScope
+import androidx.compose.ui.layout.layout
import androidx.compose.ui.platform.debugInspectorInfo
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.IntSize
@@ -129,7 +130,7 @@
content: @Composable() AnimatedVisibilityScope.() -> Unit
) {
val transition = updateTransition(visible, label)
- AnimatedEnterExitImpl(transition, { it }, modifier, enter, exit, content = content)
+ AnimatedVisibilityImpl(transition, { it }, modifier, enter, exit, content = content)
}
/**
@@ -204,7 +205,7 @@
content: @Composable() AnimatedVisibilityScope.() -> Unit
) {
val transition = updateTransition(visible, label)
- AnimatedEnterExitImpl(transition, { it }, modifier, enter, exit, content = content)
+ AnimatedVisibilityImpl(transition, { it }, modifier, enter, exit, content = content)
}
/**
@@ -277,7 +278,7 @@
content: @Composable AnimatedVisibilityScope.() -> Unit
) {
val transition = updateTransition(visible, label)
- AnimatedEnterExitImpl(transition, { it }, modifier, enter, exit, content = content)
+ AnimatedVisibilityImpl(transition, { it }, modifier, enter, exit, content = content)
}
/**
@@ -383,7 +384,7 @@
content: @Composable() AnimatedVisibilityScope.() -> Unit
) {
val transition = updateTransition(visibleState, label)
- AnimatedEnterExitImpl(transition, { it }, modifier, enter, exit, content = content)
+ AnimatedVisibilityImpl(transition, { it }, modifier, enter, exit, content = content)
}
/**
@@ -458,7 +459,7 @@
content: @Composable() AnimatedVisibilityScope.() -> Unit
) {
val transition = updateTransition(visibleState, label)
- AnimatedEnterExitImpl(transition, { it }, modifier, enter, exit, content = content)
+ AnimatedVisibilityImpl(transition, { it }, modifier, enter, exit, content = content)
}
/**
@@ -534,7 +535,7 @@
content: @Composable() AnimatedVisibilityScope.() -> Unit
) {
val transition = updateTransition(visibleState, label)
- AnimatedEnterExitImpl(transition, { it }, modifier, enter, exit, content = content)
+ AnimatedVisibilityImpl(transition, { it }, modifier, enter, exit, content = content)
}
/**
@@ -607,7 +608,7 @@
enter: EnterTransition = fadeIn() + expandIn(),
exit: ExitTransition = shrinkOut() + fadeOut(),
content: @Composable() AnimatedVisibilityScope.() -> Unit
-) = AnimatedEnterExitImpl(this, visible, modifier, enter, exit, content = content)
+) = AnimatedVisibilityImpl(this, visible, modifier, enter, exit, content = content)
/**
* This is the scope for the content of [AnimatedVisibility]. In this scope, direct and
@@ -719,8 +720,51 @@
content()
}
-// RowScope and ColumnScope AnimatedEnterExit extensions and AnimatedEnterExit without a receiver
-// converge here.
+/**
+ * RowScope and ColumnScope AnimatedVisibility extensions and AnimatedVisibility without a receiver
+ * converge here.
+ * AnimatedVisibilityImpl sets up 2 things: 1) It adds a modifier to report 0 size in lookahead
+ * when animating out. 2) It sets up a criteria for when content should be disposed.
+ */
+@OptIn(ExperimentalAnimationApi::class)
+@Composable
+internal fun <T> AnimatedVisibilityImpl(
+ transition: Transition<T>,
+ visible: (T) -> Boolean,
+ modifier: Modifier,
+ enter: EnterTransition,
+ exit: ExitTransition,
+ content: @Composable() AnimatedVisibilityScope.() -> Unit
+) {
+ AnimatedEnterExitImpl(
+ transition = transition,
+ visible = visible,
+ modifier = modifier.layout { measurable, constraints ->
+ val placeable = measurable.measure(constraints)
+ val (w, h) =
+ if (isLookingAhead && !visible(transition.targetState)) {
+ IntSize.Zero
+ } else {
+ IntSize(placeable.width, placeable.height)
+ }
+ layout(w, h) {
+ placeable.place(0, 0)
+ }
+ },
+ enter = enter,
+ exit = exit,
+ shouldDisposeBlock = { current, target -> current == target && target == PostExit },
+ content = content
+ )
+}
+
+/**
+ * Observes lookahead size.
+ */
+internal fun interface OnLookaheadMeasured {
+ fun invoke(size: IntSize)
+}
+
@OptIn(
ExperimentalTransitionApi::class,
InternalAnimationApi::class,
@@ -733,9 +777,8 @@
modifier: Modifier,
enter: EnterTransition,
exit: ExitTransition,
- shouldDisposeBlock: (EnterExitState, EnterExitState) -> Boolean = { current, target ->
- current == target && target == PostExit
- },
+ shouldDisposeBlock: (EnterExitState, EnterExitState) -> Boolean,
+ onLookaheadMeasured: OnLookaheadMeasured? = null,
content: @Composable() AnimatedVisibilityScope.() -> Unit
) {
if (visible(transition.targetState) || visible(transition.currentState) ||
@@ -771,7 +814,21 @@
val scope = remember(transition) { AnimatedVisibilityScopeImpl(childTransition) }
Layout(
content = { scope.content() },
- modifier = modifier.then(childTransition.createModifier(enter, exit, "Built-in")),
+ modifier = modifier
+ .then(childTransition.createModifier(enter, exit, "Built-in")
+ .then(if (onLookaheadMeasured != null) {
+ Modifier.layout { measurable, constraints ->
+ measurable.measure(constraints).run {
+ if (isLookingAhead) {
+ onLookaheadMeasured.invoke(IntSize(width, height))
+ }
+ layout(width, height) {
+ place(0, 0)
+ }
+ }
+ }
+ } else Modifier)
+ ),
measurePolicy = remember { AnimatedEnterExitMeasurePolicy(scope) }
)
}
diff --git a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/EnterExitTransition.kt b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/EnterExitTransition.kt
index e9d8ea9..f5a9d27 100644
--- a/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/EnterExitTransition.kt
+++ b/compose/animation/animation/src/commonMain/kotlin/androidx/compose/animation/EnterExitTransition.kt
@@ -113,7 +113,8 @@
fade = data.fade ?: enter.data.fade,
slide = data.slide ?: enter.data.slide,
changeSize = data.changeSize ?: enter.data.changeSize,
- scale = data.scale ?: enter.data.scale
+ scale = data.scale ?: enter.data.scale,
+ effectsMap = data.effectsMap + enter.data.effectsMap
)
)
}
@@ -198,7 +199,8 @@
slide = data.slide ?: exit.data.slide,
changeSize = data.changeSize ?: exit.data.changeSize,
scale = data.scale ?: exit.data.scale,
- hold = data.hold || exit.data.hold
+ hold = data.hold || exit.data.hold,
+ effectsMap = data.effectsMap + exit.data.effectsMap
)
)
}
@@ -796,6 +798,18 @@
val animationSpec: FiniteAnimationSpec<Float>
)
+internal fun EnterTransition(
+ key: Any,
+ node: ModifierNodeElement<out Modifier.Node>
+): EnterTransition =
+ EnterTransitionImpl(TransitionData(effectsMap = mapOf(key to node)))
+
+internal fun ExitTransition(
+ key: Any,
+ node: ModifierNodeElement<out Modifier.Node>
+): ExitTransition =
+ ExitTransitionImpl(TransitionData(effectsMap = mapOf(key to node)))
+
@Immutable
private class EnterTransitionImpl(override val data: TransitionData) : EnterTransition()
@@ -822,9 +836,18 @@
val slide: Slide? = null,
val changeSize: ChangeSize? = null,
val scale: Scale? = null,
- val hold: Boolean = false
+ val hold: Boolean = false,
+ val effectsMap: Map<Any, ModifierNodeElement<out Modifier.Node>> = emptyMap()
)
+@Suppress("ModifierFactoryExtensionFunction", "ModifierFactoryReturnType")
+internal operator fun EnterTransition.get(key: Any): ModifierNodeElement<out Modifier.Node>? =
+ data.effectsMap[key]
+
+@Suppress("ModifierFactoryExtensionFunction", "ModifierFactoryReturnType")
+internal operator fun ExitTransition.get(key: Any): ModifierNodeElement<out Modifier.Node>? =
+ data.effectsMap[key]
+
@OptIn(ExperimentalAnimationApi::class, InternalAnimationApi::class)
@Suppress("ModifierFactoryExtensionFunction", "ComposableModifierFactory")
@Composable
@@ -833,7 +856,6 @@
exit: ExitTransition,
label: String
): Modifier {
-
// Active enter & active exit reference the enter and exit transition that is currently being
// used. It is important to preserve the active enter/exit that was previously used before
// changing target state, such that if the previous enter/exit is interrupted, we still hold
@@ -879,7 +901,6 @@
activeExit.data.changeSize?.clip == false) || !shouldAnimateSizeChange
val graphicsLayerBlock = createGraphicsLayerBlock(activeEnter, activeExit, label)
-
return Modifier
.graphicsLayer(clip = !disableClip)
.then(
@@ -1026,9 +1047,6 @@
}
}
- private fun targetConstraints(default: Constraints) =
- if (lookaheadConstraintsAvailable) lookaheadConstraints else default
-
val sizeTransitionSpec: Transition.Segment<EnterExitState>.() -> FiniteAnimationSpec<IntSize> =
{
when {
@@ -1097,15 +1115,15 @@
val measuredSize = IntSize(placeable.width, placeable.height)
lookaheadSize = measuredSize
lookaheadConstraints = constraints
- val sizeToReport = if (transition.targetState == EnterExitState.Visible)
- measuredSize
- else
- IntSize.Zero
- return layout(sizeToReport.width, sizeToReport.height) {
+ return layout(measuredSize.width, measuredSize.height) {
placeable.place(0, 0)
}
} else {
- val placeable = measurable.measure(targetConstraints(constraints))
+ // Measure the content based on the current constraints passed down from parent.
+ // AnimatedContent will measure outgoing children with a cached constraints to avoid
+ // re-layout the outgoing content. At the animateEnterExit() level, it's not best not
+ // to make assumptions, which is why we use constraints from parent.
+ val placeable = measurable.measure(constraints)
val measuredSize = IntSize(placeable.width, placeable.height)
val target = if (lookaheadSize.isValid) lookaheadSize else measuredSize
val animSize = sizeAnimation?.animate(sizeTransitionSpec) { sizeByState(it, target) }
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FunctionBodySkippingTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FunctionBodySkippingTransformTests.kt
index f100c5d..e1af9da 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FunctionBodySkippingTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FunctionBodySkippingTransformTests.kt
@@ -4117,4 +4117,38 @@
}
"""
)
+
+ @Test
+ fun test_ComposableLambdaWithUnusedParameter() = verifyComposeIrTransform(
+ source = """
+ import androidx.compose.runtime.*
+
+ val layoutLambda = @Composable { _: Int ->
+ Layout()
+ }
+ """,
+ extra = """
+ import androidx.compose.runtime.*
+
+ @Composable inline fun Layout() {}
+ """,
+ expectedTransformed = """
+ val layoutLambda: Function3<Int, Composer, Int, Unit> = ComposableSingletons%TestKt.lambda-1
+ internal object ComposableSingletons%TestKt {
+ val lambda-1: Function3<Int, Composer, Int, Unit> = composableLambdaInstance(<>, false) { <unused var>: Int, %composer: Composer?, %changed: Int ->
+ if (%changed and 0b01010001 !== 0b00010000 || !%composer.skipping) {
+ if (isTraceInProgress()) {
+ traceEventStart(<>, %changed, -1, <>)
+ }
+ Layout(%composer, 0)
+ if (isTraceInProgress()) {
+ traceEventEnd()
+ }
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ }
+ }
+ """
+ )
}
diff --git a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FunctionalInterfaceTransformTests.kt b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FunctionalInterfaceTransformTests.kt
index 606cc70..98786eb 100644
--- a/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FunctionalInterfaceTransformTests.kt
+++ b/compose/compiler/compiler-hosted/integration-tests/src/test/java/androidx/compose/compiler/plugins/kotlin/FunctionalInterfaceTransformTests.kt
@@ -387,4 +387,88 @@
}
"""
)
+
+ @Test
+ fun testComposableFunInterfaceWAnonymousParam() = verifyComposeIrTransform(
+ """
+ import androidx.compose.runtime.*
+
+ fun interface Consumer {
+ @Composable operator fun invoke(t: Int)
+ }
+
+ @Composable fun Test(int: Int) {
+ Example { _ ->
+ }
+ }
+
+ @Composable fun Example(consumer: Consumer) {
+ }
+ """,
+ """
+ interface Consumer {
+ @Composable
+ abstract fun invoke(t: Int, %composer: Composer?, %changed: Int)
+ }
+ @Composable
+ fun Test(int: Int, %composer: Composer?, %changed: Int) {
+ %composer = %composer.startRestartGroup(<>)
+ sourceInformation(%composer, "C(Test)<Exampl...>:Test.kt")
+ if (%changed and 0b0001 !== 0 || !%composer.skipping) {
+ if (isTraceInProgress()) {
+ traceEventStart(<>, %changed, -1, <>)
+ }
+ Example(class <no name provided> : Consumer {
+ @Composable
+ override fun invoke(<unused var>: Int, %composer: Composer?, %changed: Int) {
+ %composer = %composer.startRestartGroup(<>)
+ sourceInformation(%composer, "C(invoke):Test.kt")
+ if (%changed and 0b0001 !== 0 || !%composer.skipping) {
+ if (isTraceInProgress()) {
+ traceEventStart(<>, %changed, -1, <>)
+ }
+ Unit
+ if (isTraceInProgress()) {
+ traceEventEnd()
+ }
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ val tmp0_rcvr = <this>
+ %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+ tmp0_rcvr.invoke(<unused var>, %composer, updateChangedFlags(%changed or 0b0001))
+ }
+ }
+ }
+ <no name provided>(), %composer, 0)
+ if (isTraceInProgress()) {
+ traceEventEnd()
+ }
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+ Test(int, %composer, updateChangedFlags(%changed or 0b0001))
+ }
+ }
+ @Composable
+ fun Example(consumer: Consumer, %composer: Composer?, %changed: Int) {
+ %composer = %composer.startRestartGroup(<>)
+ sourceInformation(%composer, "C(Example):Test.kt")
+ if (%changed and 0b0001 !== 0 || !%composer.skipping) {
+ if (isTraceInProgress()) {
+ traceEventStart(<>, %changed, -1, <>)
+ }
+ if (isTraceInProgress()) {
+ traceEventEnd()
+ }
+ } else {
+ %composer.skipToGroupEnd()
+ }
+ %composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
+ Example(consumer, %composer, updateChangedFlags(%changed or 0b0001))
+ }
+ }
+ """
+ )
}
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt
index e334859..07c13e3 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/ComposableFunctionBodyTransformer.kt
@@ -1689,12 +1689,20 @@
)
if (defaultParam == null) {
- require(parameterCount == defaultIndex) // param count is 1-based, index is 0-based
+ // param count is 1-based, index is 0-based
+ require(parameterCount == defaultIndex) {
+ "Expected $defaultIndex params for ${function.fqNameWhenAvailable}, " +
+ "found $parameterCount"
+ }
} else {
+ val expectedParamCount = defaultIndex +
+ defaultParamCount(contextParameterCount + numRealValueParameters)
require(
- parameterCount == defaultIndex +
- defaultParamCount(contextParameterCount + numRealValueParameters)
- )
+ parameterCount == expectedParamCount
+ ) {
+ "Expected $expectedParamCount params for ${function.fqNameWhenAvailable}, " +
+ "found $parameterCount"
+ }
}
val lambda = irLambdaExpression(
@@ -3875,7 +3883,6 @@
paramName.startsWith(KtxNameConventions.CHANGED_PARAMETER.identifier) ->
changedParams += param
paramName.startsWith("\$context_receiver_") ||
- paramName.startsWith("\$anonymous\$parameter") ||
paramName.startsWith("\$name\$for\$destructuring") ||
paramName.startsWith("\$noName_") ||
paramName == "\$this" -> Unit
diff --git a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/IrSourcePrinter.kt b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/IrSourcePrinter.kt
index 04c7f2c..841ab83a 100644
--- a/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/IrSourcePrinter.kt
+++ b/compose/compiler/compiler-hosted/src/main/java/androidx/compose/compiler/plugins/kotlin/lower/IrSourcePrinter.kt
@@ -1572,7 +1572,8 @@
get() = when {
// FIR generates both <iterator> and tmp0_for_iterator...
origin == IrDeclarationOrigin.FOR_LOOP_ITERATOR -> "<iterator>"
- !useFir && origin == IrDeclarationOrigin.UNDERSCORE_PARAMETER -> "<unused var>"
+ // $anonymous$parameter$x vs $unused$var$x
+ origin == IrDeclarationOrigin.UNDERSCORE_PARAMETER -> "<unused var>"
!useFir && name.asString().endsWith("_elvis_lhs") -> "<elvis>"
!useFir && name.asString() == "\$this\$null" -> "<this>"
else -> name.asString()
diff --git a/compose/foundation/foundation/api/current.txt b/compose/foundation/foundation/api/current.txt
index 5383dcd7..478059f 100644
--- a/compose/foundation/foundation/api/current.txt
+++ b/compose/foundation/foundation/api/current.txt
@@ -881,7 +881,8 @@
}
public final class LazyLayoutKt {
- method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void LazyLayout(androidx.compose.foundation.lazy.layout.LazyLayoutItemProvider itemProvider, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.layout.LazyLayoutPrefetchState? prefetchState, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.layout.LazyLayoutMeasureScope,? super androidx.compose.ui.unit.Constraints,? extends androidx.compose.ui.layout.MeasureResult> measurePolicy);
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void LazyLayout(androidx.compose.foundation.lazy.layout.LazyLayoutItemProvider itemProvider, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.layout.LazyLayoutPrefetchState? prefetchState, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.layout.LazyLayoutMeasureScope,? super androidx.compose.ui.unit.Constraints,? extends androidx.compose.ui.layout.MeasureResult> measurePolicy);
+ method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void LazyLayout(kotlin.jvm.functions.Function0<? extends androidx.compose.foundation.lazy.layout.LazyLayoutItemProvider> itemProvider, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.layout.LazyLayoutPrefetchState? prefetchState, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.layout.LazyLayoutMeasureScope,? super androidx.compose.ui.unit.Constraints,? extends androidx.compose.ui.layout.MeasureResult> measurePolicy);
}
@SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public sealed interface LazyLayoutMeasureScope extends androidx.compose.ui.layout.MeasureScope {
@@ -1380,7 +1381,9 @@
}
@androidx.compose.runtime.Immutable public final class KeyboardOptions {
+ ctor @Deprecated public KeyboardOptions(optional int capitalization, optional boolean autoCorrect, optional int keyboardType, optional int imeAction);
ctor public KeyboardOptions(optional int capitalization, optional boolean autoCorrect, optional int keyboardType, optional int imeAction, optional androidx.compose.ui.text.input.PlatformImeOptions? platformImeOptions);
+ method @Deprecated public androidx.compose.foundation.text.KeyboardOptions copy(optional int capitalization, optional boolean autoCorrect, optional int keyboardType, optional int imeAction);
method public androidx.compose.foundation.text.KeyboardOptions copy(optional int capitalization, optional boolean autoCorrect, optional int keyboardType, optional int imeAction, optional androidx.compose.ui.text.input.PlatformImeOptions? platformImeOptions);
method public boolean getAutoCorrect();
method public int getCapitalization();
diff --git a/compose/foundation/foundation/api/restricted_current.txt b/compose/foundation/foundation/api/restricted_current.txt
index 2bdd519..a608e1e 100644
--- a/compose/foundation/foundation/api/restricted_current.txt
+++ b/compose/foundation/foundation/api/restricted_current.txt
@@ -883,7 +883,8 @@
}
public final class LazyLayoutKt {
- method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void LazyLayout(androidx.compose.foundation.lazy.layout.LazyLayoutItemProvider itemProvider, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.layout.LazyLayoutPrefetchState? prefetchState, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.layout.LazyLayoutMeasureScope,? super androidx.compose.ui.unit.Constraints,? extends androidx.compose.ui.layout.MeasureResult> measurePolicy);
+ method @Deprecated @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void LazyLayout(androidx.compose.foundation.lazy.layout.LazyLayoutItemProvider itemProvider, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.layout.LazyLayoutPrefetchState? prefetchState, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.layout.LazyLayoutMeasureScope,? super androidx.compose.ui.unit.Constraints,? extends androidx.compose.ui.layout.MeasureResult> measurePolicy);
+ method @SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Composable public static void LazyLayout(kotlin.jvm.functions.Function0<? extends androidx.compose.foundation.lazy.layout.LazyLayoutItemProvider> itemProvider, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.lazy.layout.LazyLayoutPrefetchState? prefetchState, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.lazy.layout.LazyLayoutMeasureScope,? super androidx.compose.ui.unit.Constraints,? extends androidx.compose.ui.layout.MeasureResult> measurePolicy);
}
@SuppressCompatibility @androidx.compose.foundation.ExperimentalFoundationApi @androidx.compose.runtime.Stable public sealed interface LazyLayoutMeasureScope extends androidx.compose.ui.layout.MeasureScope {
@@ -1382,7 +1383,9 @@
}
@androidx.compose.runtime.Immutable public final class KeyboardOptions {
+ ctor @Deprecated public KeyboardOptions(optional int capitalization, optional boolean autoCorrect, optional int keyboardType, optional int imeAction);
ctor public KeyboardOptions(optional int capitalization, optional boolean autoCorrect, optional int keyboardType, optional int imeAction, optional androidx.compose.ui.text.input.PlatformImeOptions? platformImeOptions);
+ method @Deprecated public androidx.compose.foundation.text.KeyboardOptions copy(optional int capitalization, optional boolean autoCorrect, optional int keyboardType, optional int imeAction);
method public androidx.compose.foundation.text.KeyboardOptions copy(optional int capitalization, optional boolean autoCorrect, optional int keyboardType, optional int imeAction, optional androidx.compose.ui.text.input.PlatformImeOptions? platformImeOptions);
method public boolean getAutoCorrect();
method public int getCapitalization();
diff --git a/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/lazy/LazyGridScrollingBenchmark.kt b/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/lazy/LazyGridScrollingBenchmark.kt
index 2e190b3..bda7bb1 100644
--- a/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/lazy/LazyGridScrollingBenchmark.kt
+++ b/compose/foundation/foundation/benchmark/src/androidTest/java/androidx/compose/foundation/benchmark/lazy/LazyGridScrollingBenchmark.kt
@@ -22,9 +22,11 @@
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.scrollBy
import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.requiredHeight
import androidx.compose.foundation.layout.requiredSize
+import androidx.compose.foundation.layout.requiredWidth
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyGridState
import androidx.compose.foundation.lazy.grid.LazyHorizontalGrid
@@ -184,7 +186,7 @@
LazyHorizontalGrid(
rows = GridCells.Fixed(2),
state = state,
- modifier = Modifier.requiredHeight(400.dp).fillMaxWidth(),
+ modifier = Modifier.requiredWidth(400.dp).fillMaxHeight(),
flingBehavior = NoFlingBehavior
) {
items(2) {
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/snapping/LazyGridSnapFlingBehaviorTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/snapping/LazyGridSnapFlingBehaviorTest.kt
index 6294141..b756f09 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/snapping/LazyGridSnapFlingBehaviorTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/gesture/snapping/LazyGridSnapFlingBehaviorTest.kt
@@ -61,6 +61,7 @@
import kotlin.test.assertEquals
import kotlin.test.assertNotEquals
import kotlinx.coroutines.runBlocking
+import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
@@ -178,6 +179,7 @@
}
}
+ @Ignore // b/293513475
@Test
fun aboveThresholdVelocityBackward_notLargeEnoughScroll_shouldGoToPreviousPage() {
var lazyGridState: LazyGridState? = null
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutStateRestorationTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutStateRestorationTest.kt
index d05d1f1..eab253b 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutStateRestorationTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutStateRestorationTest.kt
@@ -299,20 +299,19 @@
indexToKey: (Int) -> Any = { getDefaultLazyLayoutKey(it) },
content: @Composable (Int) -> Unit
) {
- LazyLayout(
- itemProvider = remember(itemCount, indexToKey, content as Any) {
- object : LazyLayoutItemProvider {
- override val itemCount: Int = itemCount()
+ val provider = remember(itemCount, indexToKey, content as Any) {
+ object : LazyLayoutItemProvider {
+ override val itemCount: Int = itemCount()
- @Composable
- override fun Item(index: Int, key: Any) {
- content(index)
- }
-
- override fun getKey(index: Int) = indexToKey(index)
+ @Composable
+ override fun Item(index: Int, key: Any) {
+ content(index)
}
+
+ override fun getKey(index: Int) = indexToKey(index)
}
- ) { constraints ->
+ }
+ LazyLayout(itemProvider = { provider }) { constraints ->
val placeables = mutableListOf<Placeable>()
repeat(itemCount()) { index ->
if (itemIsVisible(index)) {
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutTest.kt
index 7cedc56..cd762bf 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/lazy/layout/LazyLayoutTest.kt
@@ -163,7 +163,7 @@
LazyLayout(itemProvider) {
val constraints = Constraints.fixed(100, 100)
val items = mutableListOf<Placeable>()
- repeat(itemProvider.itemCount) { index ->
+ repeat(itemProvider().itemCount) { index ->
items.addAll(measure(index, constraints))
}
layout(100, 100) {
@@ -204,7 +204,7 @@
LazyLayout(itemProvider) {
val constraints = Constraints.fixed(100, 100)
val items = mutableListOf<Placeable>()
- repeat(itemProvider.itemCount) { index ->
+ repeat(itemProvider().itemCount) { index ->
items.addAll(measure(index, constraints))
}
layout(100, 100) {
@@ -463,7 +463,7 @@
override fun getKey(index: Int) = stateList[index]
}
rule.setContent {
- LazyLayout(itemProvider) { constraint ->
+ LazyLayout({ itemProvider }) { constraint ->
measure(0, constraint)
layout(100, 100) {}
}
@@ -483,8 +483,8 @@
private fun itemProvider(
itemCount: () -> Int,
itemContent: @Composable (Int) -> Unit
- ): LazyLayoutItemProvider {
- return object : LazyLayoutItemProvider {
+ ): () -> LazyLayoutItemProvider {
+ val provider = object : LazyLayoutItemProvider {
@Composable
override fun Item(index: Int, key: Any) {
itemContent(index)
@@ -492,5 +492,6 @@
override val itemCount: Int get() = itemCount()
}
+ return { provider }
}
}
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/selection/AndroidSelectionHandles.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/selection/AndroidSelectionHandles.android.kt
index b4f0669..f9f10a3 100644
--- a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/selection/AndroidSelectionHandles.android.kt
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text/selection/AndroidSelectionHandles.android.kt
@@ -302,7 +302,7 @@
/**
* Computes whether the handle's appearance should be left-pointing or right-pointing.
*/
-private fun isLeft(
+internal fun isLeft(
isStartHandle: Boolean,
direction: ResolvedTextDirection,
handlesCrossed: Boolean
diff --git a/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/selection/TextFieldSelectionHandles.android.kt b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/selection/TextFieldSelectionHandles.android.kt
new file mode 100644
index 0000000..c6183a9
--- /dev/null
+++ b/compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/selection/TextFieldSelectionHandles.android.kt
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.text2.selection
+
+import androidx.compose.foundation.text.Handle
+import androidx.compose.foundation.text.selection.DefaultSelectionHandle
+import androidx.compose.foundation.text.selection.HandleReferencePoint
+import androidx.compose.foundation.text.selection.SelectionHandleAnchor
+import androidx.compose.foundation.text.selection.SelectionHandleInfo
+import androidx.compose.foundation.text.selection.SelectionHandleInfoKey
+import androidx.compose.foundation.text.selection.isLeft
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.takeOrElse
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.text.style.ResolvedTextDirection
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.IntRect
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.round
+import androidx.compose.ui.window.Popup
+import androidx.compose.ui.window.PopupPositionProvider
+import androidx.compose.ui.window.PopupProperties
+
+@Composable
+internal actual fun TextFieldSelectionHandle2(
+ positionProvider: OffsetProvider,
+ isStartHandle: Boolean,
+ direction: ResolvedTextDirection,
+ handlesCrossed: Boolean,
+ modifier: Modifier
+) {
+ val isLeft = isLeft(isStartHandle, direction, handlesCrossed)
+ // The left selection handle's top right is placed at the given position, and vice versa.
+ val handleReferencePoint = if (isLeft) {
+ HandleReferencePoint.TopRight
+ } else {
+ HandleReferencePoint.TopLeft
+ }
+
+ HandlePopup2(
+ positionProvider = positionProvider,
+ handleReferencePoint = handleReferencePoint
+ ) {
+ DefaultSelectionHandle(
+ modifier = modifier
+ .semantics {
+ this[SelectionHandleInfoKey] = SelectionHandleInfo(
+ handle = if (isStartHandle) {
+ Handle.SelectionStart
+ } else {
+ Handle.SelectionEnd
+ },
+ position = positionProvider.provide(),
+ anchor = if (isLeft) {
+ SelectionHandleAnchor.Left
+ } else {
+ SelectionHandleAnchor.Right
+ }
+ )
+ },
+ isStartHandle = isStartHandle,
+ direction = direction,
+ handlesCrossed = handlesCrossed
+ )
+ }
+}
+
+/**
+ * An alternative HandlePopup API that uses dynamic positioning. This enables us to update the
+ * handle position when onGloballyPositioned is called.
+ */
+@Composable
+internal fun HandlePopup2(
+ positionProvider: OffsetProvider,
+ handleReferencePoint: HandleReferencePoint,
+ content: @Composable () -> Unit
+) {
+ val popupPositioner = remember(handleReferencePoint) {
+ HandlePositionProvider2(handleReferencePoint, positionProvider)
+ }
+
+ Popup(
+ popupPositionProvider = popupPositioner,
+ properties = PopupProperties(
+ excludeFromSystemGesture = true,
+ clippingEnabled = false
+ ),
+ content = content
+ )
+}
+
+internal class HandlePositionProvider2(
+ private val handleReferencePoint: HandleReferencePoint,
+ private val positionProvider: OffsetProvider
+) : PopupPositionProvider {
+
+ override fun calculatePosition(
+ anchorBounds: IntRect,
+ windowSize: IntSize,
+ layoutDirection: LayoutDirection,
+ popupContentSize: IntSize
+ ): IntOffset {
+ val intOffset = positionProvider.provide().takeOrElse {
+ Offset(Float.MAX_VALUE, Float.MAX_VALUE)
+ }.round()
+
+ return when (handleReferencePoint) {
+ HandleReferencePoint.TopLeft ->
+ IntOffset(
+ x = anchorBounds.left + intOffset.x,
+ y = anchorBounds.top + intOffset.y
+ )
+ HandleReferencePoint.TopRight ->
+ IntOffset(
+ x = anchorBounds.left + intOffset.x - popupContentSize.width,
+ y = anchorBounds.top + intOffset.y
+ )
+ HandleReferencePoint.TopMiddle ->
+ IntOffset(
+ x = anchorBounds.left + intOffset.x - popupContentSize.width / 2,
+ y = anchorBounds.top + intOffset.y
+ )
+ }
+ }
+}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayout.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayout.kt
index 3ad7f4f..6c08e89 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayout.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/layout/LazyLayout.kt
@@ -37,6 +37,12 @@
* @param prefetchState allows to schedule items for prefetching
* @param measurePolicy Measure policy which allows to only compose and measure needed items.
*/
+@Deprecated(
+ message = "Use an overload accepting a lambda prodicing an item provider instead",
+ replaceWith = ReplaceWith(
+ "LazyLayout({ itemProvider }, modifier, prefetchState, measurePolicy)"
+ )
+)
@ExperimentalFoundationApi
@Composable
fun LazyLayout(
@@ -48,9 +54,19 @@
LazyLayout({ itemProvider }, modifier, prefetchState, measurePolicy)
}
+/**
+ * A layout that only composes and lays out currently needed items. Can be used to build
+ * efficient scrollable layouts.
+ *
+ * @param itemProvider lambda producing an item provider containing all the needed info about
+ * the items which could be used to compose and measure items as part of [measurePolicy].
+ * @param modifier to apply on the layout
+ * @param prefetchState allows to schedule items for prefetching
+ * @param measurePolicy Measure policy which allows to only compose and measure needed items.
+ */
@ExperimentalFoundationApi
@Composable
-internal fun LazyLayout(
+fun LazyLayout(
itemProvider: () -> LazyLayoutItemProvider,
modifier: Modifier = Modifier,
prefetchState: LazyLayoutPrefetchState? = null,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/KeyboardOptions.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/KeyboardOptions.kt
index 70a1d14..f7776ef 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/KeyboardOptions.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/KeyboardOptions.kt
@@ -61,6 +61,23 @@
val Default = KeyboardOptions()
}
+ @Deprecated(
+ "Please use the new constructor that takes optional platformImeOptions parameter.",
+ level = DeprecationLevel.HIDDEN
+ )
+ constructor(
+ capitalization: KeyboardCapitalization = KeyboardCapitalization.None,
+ autoCorrect: Boolean = true,
+ keyboardType: KeyboardType = KeyboardType.Text,
+ imeAction: ImeAction = ImeAction.Default
+ ) : this(
+ capitalization = capitalization,
+ autoCorrect = autoCorrect,
+ keyboardType = keyboardType,
+ imeAction = imeAction,
+ platformImeOptions = null
+ )
+
/**
* Returns a new [ImeOptions] with the values that are in this [KeyboardOptions] and provided
* params.
@@ -92,6 +109,25 @@
)
}
+ @Deprecated(
+ "Please use the new copy function that takes optional platformImeOptions parameter.",
+ level = DeprecationLevel.HIDDEN
+ )
+ fun copy(
+ capitalization: KeyboardCapitalization = this.capitalization,
+ autoCorrect: Boolean = this.autoCorrect,
+ keyboardType: KeyboardType = this.keyboardType,
+ imeAction: ImeAction = this.imeAction
+ ): KeyboardOptions {
+ return KeyboardOptions(
+ capitalization = capitalization,
+ autoCorrect = autoCorrect,
+ keyboardType = keyboardType,
+ imeAction = imeAction,
+ platformImeOptions = this.platformImeOptions
+ )
+ }
+
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is KeyboardOptions) return false
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/BasicTextField2.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/BasicTextField2.kt
index 3e58a9e..a574637 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/BasicTextField2.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/BasicTextField2.kt
@@ -32,7 +32,6 @@
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.text.heightInLines
-import androidx.compose.foundation.text.selection.SelectionHandle
import androidx.compose.foundation.text.selection.SelectionHandleAnchor
import androidx.compose.foundation.text.selection.SelectionHandleInfo
import androidx.compose.foundation.text.selection.SelectionHandleInfoKey
@@ -47,6 +46,7 @@
import androidx.compose.foundation.text2.input.internal.TextFieldDecoratorModifier
import androidx.compose.foundation.text2.input.internal.TextFieldTextLayoutModifier
import androidx.compose.foundation.text2.input.internal.TextLayoutState
+import androidx.compose.foundation.text2.selection.TextFieldSelectionHandle2
import androidx.compose.foundation.text2.selection.TextFieldSelectionState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
@@ -302,29 +302,27 @@
) {
val startHandleState = selectionState.startSelectionHandle
if (startHandleState.visible) {
- SelectionHandle(
- position = startHandleState.position,
+ TextFieldSelectionHandle2(
+ positionProvider = { selectionState.startSelectionHandle.position },
isStartHandle = true,
direction = startHandleState.direction,
handlesCrossed = startHandleState.handlesCrossed,
modifier = Modifier.pointerInput(selectionState) {
with(selectionState) { selectionHandleGestures(true) }
- },
- content = null
+ }
)
}
val endHandleState = selectionState.endSelectionHandle
if (endHandleState.visible) {
- SelectionHandle(
- position = endHandleState.position,
+ TextFieldSelectionHandle2(
+ positionProvider = { selectionState.endSelectionHandle.position },
isStartHandle = false,
direction = endHandleState.direction,
handlesCrossed = endHandleState.handlesCrossed,
modifier = Modifier.pointerInput(selectionState) {
with(selectionState) { selectionHandleGestures(false) }
- },
- content = null
+ }
)
}
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/selection/TextFieldSelectionHandles.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/selection/TextFieldSelectionHandles.kt
new file mode 100644
index 0000000..1273aae
--- /dev/null
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/selection/TextFieldSelectionHandles.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.text2.selection
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.text.style.ResolvedTextDirection
+
+@Composable
+internal expect fun TextFieldSelectionHandle2(
+ positionProvider: OffsetProvider,
+ isStartHandle: Boolean,
+ direction: ResolvedTextDirection,
+ handlesCrossed: Boolean,
+ modifier: Modifier
+)
+
+/**
+ * Avoids boxing of [Offset] which is an inline value class.
+ */
+internal fun interface OffsetProvider {
+ fun provide(): Offset
+}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/selection/TextFieldSelectionState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/selection/TextFieldSelectionState.kt
index c12257d..9f4b7f1 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/selection/TextFieldSelectionState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text2/selection/TextFieldSelectionState.kt
@@ -35,6 +35,7 @@
import androidx.compose.foundation.text2.input.TextFieldState
import androidx.compose.foundation.text2.input.getSelectedText
import androidx.compose.foundation.text2.input.internal.TextLayoutState
+import androidx.compose.foundation.text2.input.internal.coerceIn
import androidx.compose.foundation.text2.input.selectAll
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
@@ -726,7 +727,19 @@
val directionOffset = if (isStartHandle) selection.start else max(selection.end - 1, 0)
val direction = layoutResult.getBidiRunDirection(directionOffset)
val handlesCrossed = selection.reversed
- return TextFieldHandleState(true, position, direction, handlesCrossed)
+
+ // Handle normally is visible when it's out of bounds but when the handle is being dragged,
+ // we let it stay on the screen to maintain gesture continuation. However, we still want
+ // to coerce handle's position to visible bounds to not let it jitter while scrolling the
+ // TextField as the selection is expanding.
+ val coercedPosition = innerCoordinates?.visibleBounds()?.let { position.coerceIn(it) }
+ ?: position
+ return TextFieldHandleState(
+ visible = true,
+ position = coercedPosition,
+ direction = direction,
+ handlesCrossed = handlesCrossed
+ )
}
private fun getHandlePosition(isStartHandle: Boolean): Offset {
diff --git a/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/text2/selection/TextFieldSelectionHandles.desktop.kt b/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/text2/selection/TextFieldSelectionHandles.desktop.kt
new file mode 100644
index 0000000..8ac7119
--- /dev/null
+++ b/compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/text2/selection/TextFieldSelectionHandles.desktop.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.foundation.text2.selection
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.style.ResolvedTextDirection
+
+/**
+ * Handles are not supported on Desktop.
+ */
+@Composable
+internal actual fun TextFieldSelectionHandle2(
+ positionProvider: OffsetProvider,
+ isStartHandle: Boolean,
+ direction: ResolvedTextDirection,
+ handlesCrossed: Boolean,
+ modifier: Modifier
+) {}
diff --git a/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/TrivialTracingBenchmark.kt b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/TrivialPerfettoSdkBenchmark.kt
similarity index 97%
rename from compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/TrivialTracingBenchmark.kt
rename to compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/TrivialPerfettoSdkBenchmark.kt
index 3115e6b..002886f 100644
--- a/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/TrivialTracingBenchmark.kt
+++ b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/TrivialPerfettoSdkBenchmark.kt
@@ -41,7 +41,7 @@
*/
@OptIn(ExperimentalMetricApi::class)
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.R) // TODO(234351579): Support API < 30
-class TrivialTracingBenchmark(private val composableName: String) {
+class TrivialPerfettoSdkBenchmark(private val composableName: String) {
@get:Rule
val benchmarkRule = MacrobenchmarkRule()
diff --git a/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/TrivialStartupTracingBenchmark.kt b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/TrivialStartupPerfettoSdkBenchmark.kt
similarity index 84%
rename from compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/TrivialStartupTracingBenchmark.kt
rename to compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/TrivialStartupPerfettoSdkBenchmark.kt
index 47cbb44..f9ca454 100644
--- a/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/TrivialStartupTracingBenchmark.kt
+++ b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/TrivialStartupPerfettoSdkBenchmark.kt
@@ -26,7 +26,6 @@
import androidx.testutils.measureStartup
import org.hamcrest.CoreMatchers.`is`
import org.hamcrest.MatcherAssert.assertThat
-import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -35,7 +34,7 @@
@OptIn(ExperimentalMetricApi::class)
@LargeTest
@RunWith(Parameterized::class)
-class TrivialStartupTracingBenchmark(
+class TrivialStartupPerfettoSdkBenchmark(
private val startupMode: StartupMode,
private val compilationMode: CompilationMode,
private val isFullTracingEnabled: Boolean
@@ -43,8 +42,6 @@
@get:Rule
val benchmarkRule = MacrobenchmarkRule()
- // TODO(283953019): enable alongside StartupTracingInitializer (pending performance testing)
- @Ignore
@Test
fun startup() = try {
Arguments.fullTracingEnableOverride = isFullTracingEnabled
@@ -52,9 +49,8 @@
try {
val perfettoSdkTraceSection = TraceSectionMetric(
- "androidx.compose.integration.macrobenchmark.target." +
- "TrivialStartupTracingActivity.onCreate.<anonymous>" +
- " (TrivialStartupTracingActivity.kt:33)"
+ "%TrivialStartupTracingActivity.onCreate%" +
+ " (TrivialStartupTracingActivity.kt:%)"
)
benchmarkRule.measureStartup(
compilationMode = compilationMode,
@@ -70,8 +66,11 @@
if (!isFullTracingEnabled &&
e.message?.contains("Unable to read any metrics during benchmark") == true
) {
- // this is expected, we don't expect Perfetto SDK Tracing section present
- // when full tracing is disabled
+ // We are relying on the fact that Macrobenchmark will throw an exception when it
+ // cannot find any metrics, and given we are looking for one specific metric
+ // (a Composable function emitted by Compose Tracing), we are able to tell if
+ // Compose Tracing is working (enabled) or not, both of which we want to verify in
+ // this test.
} else throw e // this is a legitimate failure
}
} finally {
diff --git a/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/TrivialStartupPerfettoSdkOverheadBenchmark.kt b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/TrivialStartupPerfettoSdkOverheadBenchmark.kt
new file mode 100644
index 0000000..0e22752
--- /dev/null
+++ b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/TrivialStartupPerfettoSdkOverheadBenchmark.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.integration.macrobenchmark
+
+import androidx.benchmark.Arguments
+import androidx.benchmark.macro.CompilationMode
+import androidx.benchmark.macro.StartupMode
+import androidx.benchmark.macro.junit4.MacrobenchmarkRule
+import androidx.test.filters.LargeTest
+import androidx.testutils.createStartupCompilationParams
+import androidx.testutils.measureStartup
+import org.hamcrest.CoreMatchers.`is`
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@LargeTest
+@RunWith(Parameterized::class)
+class TrivialStartupPerfettoSdkOverheadBenchmark(
+ private val startupMode: StartupMode,
+ private val compilationMode: CompilationMode,
+ private val isFullTracingEnabled: Boolean
+) {
+ @get:Rule
+ val benchmarkRule = MacrobenchmarkRule()
+
+ @Test
+ fun startup() = try {
+ Arguments.fullTracingEnableOverride = isFullTracingEnabled
+ assertThat(Arguments.fullTracingEnable, `is`(isFullTracingEnabled))
+
+ benchmarkRule.measureStartup(
+ compilationMode = compilationMode,
+ startupMode = startupMode,
+ packageName = "androidx.compose.integration.macrobenchmark.target"
+ ) {
+ action = "androidx.compose.integration.macrobenchmark.target." +
+ "TRIVIAL_STARTUP_TRACING_ACTIVITY"
+ }
+ } finally {
+ Arguments.fullTracingEnableOverride = null
+ }
+
+ companion object {
+ // intended for local testing of all possible configurations
+ private const val exhaustiveMode = false
+
+ @Parameterized.Parameters(name = "startup={0},compilation={1},fullTracing={2}")
+ @JvmStatic
+ fun parameters() =
+ when {
+ exhaustiveMode ->
+ // complete set for testing locally
+ createStartupCompilationParams()
+ .flatMap { listOf(it + true, it + false) } /* full tracing enabled */
+ else ->
+ // subset for testing in CI:
+ // compilation isn't expected to affect this, so we just look at startup time
+ // for cold and not, since the behavior is very different in those scenarios
+ createStartupCompilationParams(
+ listOf(StartupMode.COLD, StartupMode.WARM),
+ listOf(CompilationMode.DEFAULT)
+ ).map { it + true } /* full tracing enabled */
+ }
+ }
+}
diff --git a/compose/material3/material3-adaptive/api/current.txt b/compose/material3/material3-adaptive/api/current.txt
index bf5b32a..af140b8 100644
--- a/compose/material3/material3-adaptive/api/current.txt
+++ b/compose/material3/material3-adaptive/api/current.txt
@@ -96,6 +96,77 @@
property public abstract androidx.compose.material3.adaptive.ThreePaneScaffoldValue layoutValue;
}
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public enum NavigationSuiteAlignment {
+ method public static androidx.compose.material3.adaptive.NavigationSuiteAlignment valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+ method public static androidx.compose.material3.adaptive.NavigationSuiteAlignment[] values();
+ enum_constant public static final androidx.compose.material3.adaptive.NavigationSuiteAlignment BottomHorizontal;
+ enum_constant public static final androidx.compose.material3.adaptive.NavigationSuiteAlignment EndVertical;
+ enum_constant public static final androidx.compose.material3.adaptive.NavigationSuiteAlignment StartVertical;
+ enum_constant public static final androidx.compose.material3.adaptive.NavigationSuiteAlignment TopHorizontal;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public final class NavigationSuiteColors {
+ method public long getNavigationBarContainerColor();
+ method public long getNavigationBarContentColor();
+ method public long getNavigationDrawerContainerColor();
+ method public long getNavigationDrawerContentColor();
+ method public long getNavigationRailContainerColor();
+ method public long getNavigationRailContentColor();
+ property public final long navigationBarContainerColor;
+ property public final long navigationBarContentColor;
+ property public final long navigationDrawerContainerColor;
+ property public final long navigationDrawerContentColor;
+ property public final long navigationRailContainerColor;
+ property public final long navigationRailContentColor;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public final class NavigationSuiteDefaults {
+ method public String calculateFromAdaptiveInfo(androidx.compose.material3.adaptive.WindowAdaptiveInfo adaptiveInfo);
+ method @androidx.compose.runtime.Composable public androidx.compose.material3.adaptive.NavigationSuiteColors colors(optional long navigationBarContainerColor, optional long navigationBarContentColor, optional long navigationRailContainerColor, optional long navigationRailContentColor, optional long navigationDrawerContainerColor, optional long navigationDrawerContentColor);
+ method public androidx.compose.material3.adaptive.NavigationSuiteAlignment getNavigationBarAlignment();
+ method public androidx.compose.material3.adaptive.NavigationSuiteAlignment getNavigationDrawerAlignment();
+ method public androidx.compose.material3.adaptive.NavigationSuiteAlignment getNavigationRailAlignment();
+ property public final androidx.compose.material3.adaptive.NavigationSuiteAlignment NavigationBarAlignment;
+ property public final androidx.compose.material3.adaptive.NavigationSuiteAlignment NavigationDrawerAlignment;
+ property public final androidx.compose.material3.adaptive.NavigationSuiteAlignment NavigationRailAlignment;
+ field public static final androidx.compose.material3.adaptive.NavigationSuiteDefaults INSTANCE;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public final class NavigationSuiteItemColors {
+ method public androidx.compose.material3.NavigationBarItemColors getNavigationBarItemColors();
+ method public androidx.compose.material3.NavigationDrawerItemColors getNavigationDrawerItemColors();
+ method public androidx.compose.material3.NavigationRailItemColors getNavigationRailItemColors();
+ property public final androidx.compose.material3.NavigationBarItemColors navigationBarItemColors;
+ property public final androidx.compose.material3.NavigationDrawerItemColors navigationDrawerItemColors;
+ property public final androidx.compose.material3.NavigationRailItemColors navigationRailItemColors;
+ }
+
+ public final class NavigationSuiteScaffoldKt {
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void NavigationSuite(androidx.compose.material3.adaptive.NavigationSuiteScaffoldScope, optional androidx.compose.ui.Modifier modifier, optional String layoutType, optional androidx.compose.material3.adaptive.NavigationSuiteColors colors, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.NavigationSuiteScope,kotlin.Unit> content);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void NavigationSuiteScaffold(kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.NavigationSuiteScaffoldScope,kotlin.Unit> navigationSuite, optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long contentColor, optional kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public interface NavigationSuiteScaffoldScope {
+ method public androidx.compose.ui.Modifier alignment(androidx.compose.ui.Modifier, androidx.compose.material3.adaptive.NavigationSuiteAlignment alignment);
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public interface NavigationSuiteScope {
+ method public void item(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> icon, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional boolean alwaysShowLabel, optional kotlin.jvm.functions.Function0<kotlin.Unit>? badge, optional androidx.compose.material3.adaptive.NavigationSuiteItemColors? colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @kotlin.jvm.JvmInline public final value class NavigationSuiteType {
+ field public static final androidx.compose.material3.adaptive.NavigationSuiteType.Companion Companion;
+ }
+
+ public static final class NavigationSuiteType.Companion {
+ method public String getNavigationBar();
+ method public String getNavigationDrawer();
+ method public String getNavigationRail();
+ property public final String NavigationBar;
+ property public final String NavigationDrawer;
+ property public final String NavigationRail;
+ }
+
@SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @kotlin.jvm.JvmInline public final value class PaneAdaptedValue {
field public static final androidx.compose.material3.adaptive.PaneAdaptedValue.Companion Companion;
}
diff --git a/compose/material3/material3-adaptive/api/restricted_current.txt b/compose/material3/material3-adaptive/api/restricted_current.txt
index bf5b32a..af140b8 100644
--- a/compose/material3/material3-adaptive/api/restricted_current.txt
+++ b/compose/material3/material3-adaptive/api/restricted_current.txt
@@ -96,6 +96,77 @@
property public abstract androidx.compose.material3.adaptive.ThreePaneScaffoldValue layoutValue;
}
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public enum NavigationSuiteAlignment {
+ method public static androidx.compose.material3.adaptive.NavigationSuiteAlignment valueOf(String value) throws java.lang.IllegalArgumentException, java.lang.NullPointerException;
+ method public static androidx.compose.material3.adaptive.NavigationSuiteAlignment[] values();
+ enum_constant public static final androidx.compose.material3.adaptive.NavigationSuiteAlignment BottomHorizontal;
+ enum_constant public static final androidx.compose.material3.adaptive.NavigationSuiteAlignment EndVertical;
+ enum_constant public static final androidx.compose.material3.adaptive.NavigationSuiteAlignment StartVertical;
+ enum_constant public static final androidx.compose.material3.adaptive.NavigationSuiteAlignment TopHorizontal;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public final class NavigationSuiteColors {
+ method public long getNavigationBarContainerColor();
+ method public long getNavigationBarContentColor();
+ method public long getNavigationDrawerContainerColor();
+ method public long getNavigationDrawerContentColor();
+ method public long getNavigationRailContainerColor();
+ method public long getNavigationRailContentColor();
+ property public final long navigationBarContainerColor;
+ property public final long navigationBarContentColor;
+ property public final long navigationDrawerContainerColor;
+ property public final long navigationDrawerContentColor;
+ property public final long navigationRailContainerColor;
+ property public final long navigationRailContentColor;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public final class NavigationSuiteDefaults {
+ method public String calculateFromAdaptiveInfo(androidx.compose.material3.adaptive.WindowAdaptiveInfo adaptiveInfo);
+ method @androidx.compose.runtime.Composable public androidx.compose.material3.adaptive.NavigationSuiteColors colors(optional long navigationBarContainerColor, optional long navigationBarContentColor, optional long navigationRailContainerColor, optional long navigationRailContentColor, optional long navigationDrawerContainerColor, optional long navigationDrawerContentColor);
+ method public androidx.compose.material3.adaptive.NavigationSuiteAlignment getNavigationBarAlignment();
+ method public androidx.compose.material3.adaptive.NavigationSuiteAlignment getNavigationDrawerAlignment();
+ method public androidx.compose.material3.adaptive.NavigationSuiteAlignment getNavigationRailAlignment();
+ property public final androidx.compose.material3.adaptive.NavigationSuiteAlignment NavigationBarAlignment;
+ property public final androidx.compose.material3.adaptive.NavigationSuiteAlignment NavigationDrawerAlignment;
+ property public final androidx.compose.material3.adaptive.NavigationSuiteAlignment NavigationRailAlignment;
+ field public static final androidx.compose.material3.adaptive.NavigationSuiteDefaults INSTANCE;
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public final class NavigationSuiteItemColors {
+ method public androidx.compose.material3.NavigationBarItemColors getNavigationBarItemColors();
+ method public androidx.compose.material3.NavigationDrawerItemColors getNavigationDrawerItemColors();
+ method public androidx.compose.material3.NavigationRailItemColors getNavigationRailItemColors();
+ property public final androidx.compose.material3.NavigationBarItemColors navigationBarItemColors;
+ property public final androidx.compose.material3.NavigationDrawerItemColors navigationDrawerItemColors;
+ property public final androidx.compose.material3.NavigationRailItemColors navigationRailItemColors;
+ }
+
+ public final class NavigationSuiteScaffoldKt {
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void NavigationSuite(androidx.compose.material3.adaptive.NavigationSuiteScaffoldScope, optional androidx.compose.ui.Modifier modifier, optional String layoutType, optional androidx.compose.material3.adaptive.NavigationSuiteColors colors, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.NavigationSuiteScope,kotlin.Unit> content);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void NavigationSuiteScaffold(kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.NavigationSuiteScaffoldScope,kotlin.Unit> navigationSuite, optional androidx.compose.ui.Modifier modifier, optional long containerColor, optional long contentColor, optional kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public interface NavigationSuiteScaffoldScope {
+ method public androidx.compose.ui.Modifier alignment(androidx.compose.ui.Modifier, androidx.compose.material3.adaptive.NavigationSuiteAlignment alignment);
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public interface NavigationSuiteScope {
+ method public void item(boolean selected, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, kotlin.jvm.functions.Function0<kotlin.Unit> icon, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional kotlin.jvm.functions.Function0<kotlin.Unit>? label, optional boolean alwaysShowLabel, optional kotlin.jvm.functions.Function0<kotlin.Unit>? badge, optional androidx.compose.material3.adaptive.NavigationSuiteItemColors? colors, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @kotlin.jvm.JvmInline public final value class NavigationSuiteType {
+ field public static final androidx.compose.material3.adaptive.NavigationSuiteType.Companion Companion;
+ }
+
+ public static final class NavigationSuiteType.Companion {
+ method public String getNavigationBar();
+ method public String getNavigationDrawer();
+ method public String getNavigationRail();
+ property public final String NavigationBar;
+ property public final String NavigationDrawer;
+ property public final String NavigationRail;
+ }
+
@SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @kotlin.jvm.JvmInline public final value class PaneAdaptedValue {
field public static final androidx.compose.material3.adaptive.PaneAdaptedValue.Companion Companion;
}
diff --git a/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/NavigationSuiteScaffold.kt b/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/NavigationSuiteScaffold.kt
index 956eb20..9620e34 100644
--- a/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/NavigationSuiteScaffold.kt
+++ b/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/NavigationSuiteScaffold.kt
@@ -65,22 +65,20 @@
* The Navigation Suite Scaffold wraps the provided content and places the adequate provided
* navigation component on the screen according to the current [NavigationSuiteType].
*
- * @param modifier the [Modifier] to be applied to the navigation suite scaffold
* @param navigationSuite the navigation component to be displayed, typically [NavigationSuite]
+ * @param modifier the [Modifier] to be applied to the navigation suite scaffold
* @param containerColor the color used for the background of the navigation suite scaffold. Use
* [Color.Transparent] to have no color
* @param contentColor the preferred color for content inside the navigation suite scaffold.
* Defaults to either the matching content color for [containerColor], or to the current
* [LocalContentColor] if [containerColor] is not a color from the theme
* @param content the content of your screen
- *
- * TODO: Remove "internal".
*/
@ExperimentalMaterial3AdaptiveApi
@Composable
-internal fun NavigationSuiteScaffold(
- modifier: Modifier = Modifier,
+fun NavigationSuiteScaffold(
navigationSuite: @Composable NavigationSuiteScaffoldScope.() -> Unit,
+ modifier: Modifier = Modifier,
containerColor: Color = MaterialTheme.colorScheme.background,
contentColor: Color = contentColorFor(containerColor),
content: @Composable () -> Unit = {},
@@ -201,17 +199,15 @@
* @param modifier the [Modifier] to be applied to the navigation component
* @param layoutType the current [NavigationSuiteType] of the [NavigationSuiteScaffold]. Defaults to
* [NavigationSuiteDefaults.calculateFromAdaptiveInfo]
- * @params colors [NavigationSuiteColors] that will be used to determine the container (background)
+ * @param colors [NavigationSuiteColors] that will be used to determine the container (background)
* color of the navigation component and the preferred color for content inside the navigation
* component
* @param content the content inside the current navigation component, typically
* [NavigationSuiteScope.item]s
- *
- * TODO: Remove "internal".
*/
@ExperimentalMaterial3AdaptiveApi
@Composable
-internal fun NavigationSuiteScaffoldScope.NavigationSuite(
+fun NavigationSuiteScaffoldScope.NavigationSuite(
modifier: Modifier = Modifier,
layoutType: NavigationSuiteType =
NavigationSuiteDefaults.calculateFromAdaptiveInfo(WindowAdaptiveInfoDefault),
@@ -291,13 +287,9 @@
}
}
-/**
- * The scope associated with the [NavigationSuiteScaffold].
- *
- * TODO: Remove "internal".
- */
+/** The scope associated with the [NavigationSuiteScaffold]. */
@ExperimentalMaterial3AdaptiveApi
-internal interface NavigationSuiteScaffoldScope {
+interface NavigationSuiteScaffoldScope {
/**
* [Modifier] that should be applied to the [NavigationSuite] of the [NavigationSuiteScaffold]
* in order to determine its alignment on the screen.
@@ -312,11 +304,9 @@
*
* The alignment informs the Navigation Suite Scaffold how to properly place the expected navigation
* component on the screen in relation to the Navigation Suite Scaffold's content.
- *
- * TODO: Remove "internal".
*/
@ExperimentalMaterial3AdaptiveApi
-internal enum class NavigationSuiteAlignment {
+enum class NavigationSuiteAlignment {
/** The navigation component is vertical and positioned at the start of the screen. */
StartVertical,
/** The navigation component is vertical and positioned at the end of the screen. */
@@ -327,13 +317,9 @@
BottomHorizontal
}
-/**
- * The scope associated with the [NavigationSuite].
- *
- * TODO: Remove "internal".
- */
+/** The scope associated with the [NavigationSuite]. */
@ExperimentalMaterial3AdaptiveApi
-internal interface NavigationSuiteScope {
+interface NavigationSuiteScope {
/**
* This function sets the parameters of the default Material navigation item to be used with the
@@ -377,16 +363,11 @@
/**
* Class that describes the different navigation suite types of the [NavigationSuiteScaffold].
*
- * The [NavigationSuiteType] informs the [NavigationSuite] of what navigation component to
- * expect.
- *
- * @param description the description of the [NavigationSuiteType]
- *
- * TODO: remove "internal"
+ * The [NavigationSuiteType] informs the [NavigationSuite] of what navigation component to expect.
*/
@JvmInline
@ExperimentalMaterial3AdaptiveApi
-internal value class NavigationSuiteType private constructor(private val description: String) {
+value class NavigationSuiteType private constructor(private val description: String) {
override fun toString(): String {
return description
}
@@ -417,13 +398,9 @@
}
}
-/**
- * Contains the default values used by the [NavigationSuite].
- *
- * TODO: Remove "internal".
- */
+/** Contains the default values used by the [NavigationSuite]. */
@ExperimentalMaterial3AdaptiveApi
-internal object NavigationSuiteDefaults {
+object NavigationSuiteDefaults {
/**
* Returns the expected [NavigationSuiteType] according to the provided [WindowAdaptiveInfo].
* Usually used with the [NavigationSuite].
@@ -506,11 +483,9 @@
* [NavigationSuite]
* @param navigationDrawerContentColor the content color for the [PermanentDrawerSheet] of the
* [NavigationSuite]
- *
- * TODO: Remove "internal".
*/
@ExperimentalMaterial3AdaptiveApi
-internal class NavigationSuiteColors
+class NavigationSuiteColors
internal constructor(
val navigationBarContainerColor: Color,
val navigationBarContentColor: Color,
@@ -532,11 +507,9 @@
* [NavigationRailItem] of the [NavigationSuiteScope.item]
* @param navigationDrawerItemColors the [NavigationDrawerItemColors] associated with the
* [NavigationDrawerItem] of the [NavigationSuiteScope.item]
- *
- * TODO: Remove "internal".
*/
@ExperimentalMaterial3AdaptiveApi
-internal class NavigationSuiteItemColors
+class NavigationSuiteItemColors
internal constructor(
val navigationBarItemColors: NavigationBarItemColors,
val navigationRailItemColors: NavigationRailItemColors,
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composer.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composer.kt
index 31650af..fc64f3a 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composer.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composer.kt
@@ -2006,18 +2006,14 @@
if (inserting) {
providers = parentScope.putValue(local, state)
invalid = false
+ writerHasAProvider = true
} else {
val oldScope = reader.groupAux(reader.currentGroup) as PersistentCompositionLocalMap
providers =
if ((!skipping || change) && (value.canOverride || !parentScope.contains(local)))
parentScope.putValue(local, state)
else oldScope
- if (oldScope !== providers) {
- invalid = true
- writerHasAProvider = true
- } else {
- invalid = false
- }
+ invalid = reusing || oldScope !== providers
}
if (invalid && !inserting) {
providerUpdates[reader.currentGroup] = providers
@@ -3333,6 +3329,7 @@
private fun reportFreeMovableContent(groupBeingRemoved: Int) {
fun reportGroup(group: Int, needsNodeDelete: Boolean, nodeIndex: Int): Int {
+ val reader = reader
return if (reader.hasMark(group)) {
// If the group has a mark then it is either a movable content group or a
// composition context group
@@ -3390,7 +3387,7 @@
}
}
reader.nodeCount(group)
- } else reader.nodeCount(group)
+ } else if (reader.isNode(group)) 1 else reader.nodeCount(group)
} else if (reader.containsMark(group)) {
// Traverse the group freeing the child movable content. This group is known to
// have at least one child that contains movable content because the group is
@@ -3423,8 +3420,8 @@
}
current += reader.groupSize(current)
}
- runningNodeCount
- } else reader.nodeCount(group)
+ if (reader.isNode(group)) 1 else runningNodeCount
+ } else if (reader.isNode(group)) 1 else reader.nodeCount(group)
}
reportGroup(groupBeingRemoved, needsNodeDelete = false, nodeIndex = 0)
changeListWriter.endNodeMovement()
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/CompositionLocal.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/CompositionLocal.kt
index cde9d7c..1073e39 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/CompositionLocal.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/CompositionLocal.kt
@@ -244,10 +244,9 @@
@Composable
@OptIn(InternalComposeApi::class)
fun CompositionLocalProvider(value: ProvidedValue<*>, content: @Composable () -> Unit) {
- // TODO(b/292224893): Switch this to the higher-performance startProvider()
- currentComposer.startProviders(arrayOf(value))
+ currentComposer.startProvider(value)
content()
- currentComposer.endProviders()
+ currentComposer.endProvider()
}
/**
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/CompositionLocalTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/CompositionLocalTests.kt
index 1289e49..4144ccd 100644
--- a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/CompositionLocalTests.kt
+++ b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/CompositionLocalTests.kt
@@ -568,6 +568,44 @@
}
}
+ @Suppress("UNUSED_EXPRESSION")
+ @Test // Regression for b/292224893
+ fun testSingleInvalidatedProvider() = compositionTest {
+ val local1 = compositionLocalOf { 10 }
+ val local2 = compositionLocalOf { 20 }
+ val local3 = compositionLocalOf { 30 }
+ var state by mutableStateOf(0)
+
+ compose {
+ state
+ CompositionLocalProvider(local1 provides 11) {
+ state
+ CompositionLocalProvider(local2 provides 22) {
+ state
+ CompositionLocalProvider(local3 provides 33) {
+ state
+ assertEquals(11, local1.current)
+ assertEquals(22, local2.current)
+ assertEquals(33, local3.current)
+ }
+ assertEquals(11, local1.current)
+ assertEquals(22, local2.current)
+ assertEquals(30, local3.current)
+ }
+ assertEquals(11, local1.current)
+ assertEquals(20, local2.current)
+ assertEquals(30, local3.current)
+ }
+ assertEquals(10, local1.current)
+ assertEquals(20, local2.current)
+ assertEquals(30, local3.current)
+ }
+
+ state++
+
+ advance()
+ }
+
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun testProvideAllLocals() = compositionTest {
diff --git a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/MovableContentTests.kt b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/MovableContentTests.kt
index d3d1a87..dcda328 100644
--- a/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/MovableContentTests.kt
+++ b/compose/runtime/runtime/src/nonEmulatorCommonTest/kotlin/androidx/compose/runtime/MovableContentTests.kt
@@ -16,6 +16,7 @@
package androidx.compose.runtime
+import androidx.compose.runtime.mock.Linear
import androidx.compose.runtime.mock.MockViewValidator
import androidx.compose.runtime.mock.View
import androidx.compose.runtime.mock.ViewApplier
@@ -1541,6 +1542,50 @@
assertEquals(state, lastSeen)
}
+
+ @Test
+ fun movableContent_moveRow() = compositionTest {
+ var condition by mutableStateOf(true)
+
+ val movableContent1 = movableContentOf {
+ Text("First")
+ }
+ val movableContent2 = movableContentOf {
+ Text("Second")
+ }
+
+ compose {
+ if (condition) {
+ Linear {
+ Linear {
+ movableContent1()
+ }
+ movableContent2()
+ }
+ } else {
+ Linear {
+ Linear {
+ movableContent1()
+ }
+ movableContent2()
+ }
+ }
+ }
+
+ validate {
+ Linear {
+ Linear {
+ Text("First")
+ }
+ Text("Second")
+ }
+ }
+
+ condition = false
+ expectChanges()
+
+ revalidate()
+ }
}
@Composable
diff --git a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/TestOwner.kt b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/TestOwner.kt
index 49bc878..d34d4bc 100644
--- a/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/TestOwner.kt
+++ b/compose/ui/ui-test/src/commonMain/kotlin/androidx/compose/ui/test/TestOwner.kt
@@ -93,8 +93,10 @@
}
}
- return roots.flatMap {
- it.semanticsOwner.getAllSemanticsNodes(mergingEnabled = !useUnmergedTree)
+ return testOwner.runOnUiThread {
+ roots.flatMap {
+ it.semanticsOwner.getAllSemanticsNodes(mergingEnabled = !useUnmergedTree)
+ }
}
}
}
diff --git a/compose/ui/ui-text/api/current.txt b/compose/ui/ui-text/api/current.txt
index 91a2b5d1..f6bb7575a 100644
--- a/compose/ui/ui-text/api/current.txt
+++ b/compose/ui/ui-text/api/current.txt
@@ -1001,7 +1001,9 @@
}
@androidx.compose.runtime.Immutable public final class ImeOptions {
+ ctor @Deprecated public ImeOptions(optional boolean singleLine, optional int capitalization, optional boolean autoCorrect, optional int keyboardType, optional int imeAction);
ctor public ImeOptions(optional boolean singleLine, optional int capitalization, optional boolean autoCorrect, optional int keyboardType, optional int imeAction, optional androidx.compose.ui.text.input.PlatformImeOptions? platformImeOptions);
+ method @Deprecated public androidx.compose.ui.text.input.ImeOptions copy(optional boolean singleLine, optional int capitalization, optional boolean autoCorrect, optional int keyboardType, optional int imeAction);
method public androidx.compose.ui.text.input.ImeOptions copy(optional boolean singleLine, optional int capitalization, optional boolean autoCorrect, optional int keyboardType, optional int imeAction, optional androidx.compose.ui.text.input.PlatformImeOptions? platformImeOptions);
method public boolean getAutoCorrect();
method public int getCapitalization();
diff --git a/compose/ui/ui-text/api/restricted_current.txt b/compose/ui/ui-text/api/restricted_current.txt
index 91a2b5d1..f6bb7575a 100644
--- a/compose/ui/ui-text/api/restricted_current.txt
+++ b/compose/ui/ui-text/api/restricted_current.txt
@@ -1001,7 +1001,9 @@
}
@androidx.compose.runtime.Immutable public final class ImeOptions {
+ ctor @Deprecated public ImeOptions(optional boolean singleLine, optional int capitalization, optional boolean autoCorrect, optional int keyboardType, optional int imeAction);
ctor public ImeOptions(optional boolean singleLine, optional int capitalization, optional boolean autoCorrect, optional int keyboardType, optional int imeAction, optional androidx.compose.ui.text.input.PlatformImeOptions? platformImeOptions);
+ method @Deprecated public androidx.compose.ui.text.input.ImeOptions copy(optional boolean singleLine, optional int capitalization, optional boolean autoCorrect, optional int keyboardType, optional int imeAction);
method public androidx.compose.ui.text.input.ImeOptions copy(optional boolean singleLine, optional int capitalization, optional boolean autoCorrect, optional int keyboardType, optional int imeAction, optional androidx.compose.ui.text.input.PlatformImeOptions? platformImeOptions);
method public boolean getAutoCorrect();
method public int getCapitalization();
diff --git a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/ImeOptions.kt b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/ImeOptions.kt
index 487b949..8a45b44 100644
--- a/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/ImeOptions.kt
+++ b/compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/input/ImeOptions.kt
@@ -56,6 +56,25 @@
val Default = ImeOptions()
}
+ @Deprecated(
+ "Please use the new constructor that takes optional platformImeOptions parameter.",
+ level = DeprecationLevel.HIDDEN
+ )
+ constructor(
+ singleLine: Boolean = false,
+ capitalization: KeyboardCapitalization = KeyboardCapitalization.None,
+ autoCorrect: Boolean = true,
+ keyboardType: KeyboardType = KeyboardType.Text,
+ imeAction: ImeAction = ImeAction.Default,
+ ) : this(
+ singleLine = singleLine,
+ capitalization = capitalization,
+ autoCorrect = autoCorrect,
+ keyboardType = keyboardType,
+ imeAction = imeAction,
+ platformImeOptions = null
+ )
+
fun copy(
singleLine: Boolean = this.singleLine,
capitalization: KeyboardCapitalization = this.capitalization,
@@ -74,6 +93,27 @@
)
}
+ @Deprecated(
+ "Please use the new copy function that takes optional platformImeOptions parameter.",
+ level = DeprecationLevel.HIDDEN
+ )
+ fun copy(
+ singleLine: Boolean = this.singleLine,
+ capitalization: KeyboardCapitalization = this.capitalization,
+ autoCorrect: Boolean = this.autoCorrect,
+ keyboardType: KeyboardType = this.keyboardType,
+ imeAction: ImeAction = this.imeAction
+ ): ImeOptions {
+ return ImeOptions(
+ singleLine = singleLine,
+ capitalization = capitalization,
+ autoCorrect = autoCorrect,
+ keyboardType = keyboardType,
+ imeAction = imeAction,
+ platformImeOptions = this.platformImeOptions
+ )
+ }
+
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is ImeOptions) return false
diff --git a/compose/ui/ui-tooling/src/androidAndroidTest/kotlin/androidx/compose/ui/tooling/animation/clock/TransitionClockTest.kt b/compose/ui/ui-tooling/src/androidAndroidTest/kotlin/androidx/compose/ui/tooling/animation/clock/TransitionClockTest.kt
index 48b3e9c..5377348 100644
--- a/compose/ui/ui-tooling/src/androidAndroidTest/kotlin/androidx/compose/ui/tooling/animation/clock/TransitionClockTest.kt
+++ b/compose/ui/ui-tooling/src/androidAndroidTest/kotlin/androidx/compose/ui/tooling/animation/clock/TransitionClockTest.kt
@@ -599,7 +599,8 @@
clock.setStateParameters(10.dp, 10.dp)
}
rule.runOnIdle {
- assertEquals(2, clock.getAnimatedProperties().size)
+ // When initial == target state, no animation is active.
+ assertEquals(0, clock.getAnimatedProperties().size)
clock.setStateParameters(20.dp, 40.dp)
}
rule.runOnIdle {
@@ -619,19 +620,8 @@
rule.runOnIdle {
// Default clock state.
clock.getTransitions(100).let {
- assertEquals(2, it.size)
- it[0].let { info ->
- assertEquals(0, info.startTimeMillis)
- assertEquals(0, info.endTimeMillis)
- assertEquals(1, info.values.size)
- assertNotNull(info.specType)
- }
- it[1].let { info ->
- assertEquals(0, info.startTimeMillis)
- assertEquals(0, info.endTimeMillis)
- assertEquals(1, info.values.size)
- assertNotNull(info.specType)
- }
+ // When initial == target state, no animation is active.
+ assertEquals(0, it.size)
}
// Change state
clock.setStateParameters(20.dp, 40.dp)
diff --git a/compose/ui/ui/api/current.txt b/compose/ui/ui/api/current.txt
index 5ac963d..07a53a6 100644
--- a/compose/ui/ui/api/current.txt
+++ b/compose/ui/ui/api/current.txt
@@ -2175,14 +2175,14 @@
@Deprecated @SuppressCompatibility @androidx.compose.ui.ExperimentalComposeUiApi public sealed interface LookaheadLayoutCoordinates extends androidx.compose.ui.layout.LayoutCoordinates {
}
- @SuppressCompatibility @androidx.compose.ui.ExperimentalComposeUiApi public interface LookaheadScope {
+ public interface LookaheadScope {
method public androidx.compose.ui.layout.LayoutCoordinates getLookaheadScopeCoordinates(androidx.compose.ui.layout.Placeable.PlacementScope);
- method public default long localLookaheadPositionOf(androidx.compose.ui.layout.LayoutCoordinates, androidx.compose.ui.layout.LayoutCoordinates coordinates);
- method public androidx.compose.ui.layout.LayoutCoordinates toLookaheadCoordinates(androidx.compose.ui.layout.LayoutCoordinates);
+ method @SuppressCompatibility @androidx.compose.ui.ExperimentalComposeUiApi public default long localLookaheadPositionOf(androidx.compose.ui.layout.LayoutCoordinates, androidx.compose.ui.layout.LayoutCoordinates coordinates);
+ method @SuppressCompatibility @androidx.compose.ui.ExperimentalComposeUiApi public androidx.compose.ui.layout.LayoutCoordinates toLookaheadCoordinates(androidx.compose.ui.layout.LayoutCoordinates);
}
public final class LookaheadScopeKt {
- method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.compose.ui.ExperimentalComposeUiApi @androidx.compose.ui.UiComposable public static void LookaheadScope(kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.LookaheadScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable @androidx.compose.ui.UiComposable public static void LookaheadScope(kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.LookaheadScope,kotlin.Unit> content);
method @SuppressCompatibility @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier intermediateLayout(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function3<? super androidx.compose.ui.layout.IntermediateMeasureScope,? super androidx.compose.ui.layout.Measurable,? super androidx.compose.ui.unit.Constraints,? extends androidx.compose.ui.layout.MeasureResult> measure);
}
diff --git a/compose/ui/ui/api/restricted_current.txt b/compose/ui/ui/api/restricted_current.txt
index f3078e1..ec0b3c9 100644
--- a/compose/ui/ui/api/restricted_current.txt
+++ b/compose/ui/ui/api/restricted_current.txt
@@ -2178,14 +2178,14 @@
@Deprecated @SuppressCompatibility @androidx.compose.ui.ExperimentalComposeUiApi public sealed interface LookaheadLayoutCoordinates extends androidx.compose.ui.layout.LayoutCoordinates {
}
- @SuppressCompatibility @androidx.compose.ui.ExperimentalComposeUiApi public interface LookaheadScope {
+ public interface LookaheadScope {
method public androidx.compose.ui.layout.LayoutCoordinates getLookaheadScopeCoordinates(androidx.compose.ui.layout.Placeable.PlacementScope);
- method public default long localLookaheadPositionOf(androidx.compose.ui.layout.LayoutCoordinates, androidx.compose.ui.layout.LayoutCoordinates coordinates);
- method public androidx.compose.ui.layout.LayoutCoordinates toLookaheadCoordinates(androidx.compose.ui.layout.LayoutCoordinates);
+ method @SuppressCompatibility @androidx.compose.ui.ExperimentalComposeUiApi public default long localLookaheadPositionOf(androidx.compose.ui.layout.LayoutCoordinates, androidx.compose.ui.layout.LayoutCoordinates coordinates);
+ method @SuppressCompatibility @androidx.compose.ui.ExperimentalComposeUiApi public androidx.compose.ui.layout.LayoutCoordinates toLookaheadCoordinates(androidx.compose.ui.layout.LayoutCoordinates);
}
public final class LookaheadScopeKt {
- method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.compose.ui.ExperimentalComposeUiApi @androidx.compose.ui.UiComposable public static void LookaheadScope(kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.LookaheadScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable @androidx.compose.ui.UiComposable public static void LookaheadScope(kotlin.jvm.functions.Function1<? super androidx.compose.ui.layout.LookaheadScope,kotlin.Unit> content);
method @SuppressCompatibility @androidx.compose.ui.ExperimentalComposeUiApi public static androidx.compose.ui.Modifier intermediateLayout(androidx.compose.ui.Modifier, kotlin.jvm.functions.Function3<? super androidx.compose.ui.layout.IntermediateMeasureScope,? super androidx.compose.ui.layout.Measurable,? super androidx.compose.ui.unit.Constraints,? extends androidx.compose.ui.layout.MeasureResult> measure);
}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt
index 93604d6..e905ca7 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/AndroidLayoutDrawTest.kt
@@ -1953,11 +1953,12 @@
// This simulates a child that recomposes, for example due to a transition.
content(offset.value)
}
- val assumeLayoutBeforeDraw = @Composable { _: Int ->
+ val assumeLayoutBeforeDraw = @Composable { value: Int ->
// This assumes a layout was done before the draw pass.
Layout(
content = {},
modifier = Modifier.drawBehind {
+ assertEquals(offset.value, value)
assertTrue(laidOut)
latch.countDown()
}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/LookaheadScopeTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/LookaheadScopeTest.kt
index 7f3e41e..1725987 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/LookaheadScopeTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/layout/LookaheadScopeTest.kt
@@ -19,6 +19,7 @@
package androidx.compose.ui.layout
import androidx.activity.ComponentActivity
+import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.AnimationVector2D
import androidx.compose.animation.core.VectorConverter
@@ -28,6 +29,9 @@
import androidx.compose.foundation.layout.Arrangement.Absolute.SpaceAround
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.ExperimentalLayoutApi
+import androidx.compose.foundation.layout.FlowColumn
+import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
@@ -36,6 +40,7 @@
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredHeight
@@ -46,6 +51,7 @@
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.layout.wrapContentHeight
+import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
@@ -91,6 +97,7 @@
import junit.framework.TestCase.assertTrue
import kotlin.math.roundToInt
import kotlin.random.Random
+import kotlin.test.assertNotNull
import kotlinx.coroutines.launch
import org.junit.Ignore
import org.junit.Rule
@@ -1264,6 +1271,83 @@
}
}
+ @OptIn(ExperimentalLayoutApi::class)
+ @Test
+ fun testNestedLookaheadPlacement() {
+ var expanded by mutableStateOf(true)
+ var actualOffset: Offset? = null
+ var lookaheadOffset: Offset? = null
+ rule.setContent {
+ LookaheadScope {
+ FlowRow(
+ modifier = Modifier.fillMaxSize(),
+ horizontalArrangement = Arrangement.Center,
+ verticalArrangement = Arrangement.Center,
+ maxItemsInEachRow = 3
+ ) {
+ Box(
+ modifier = Modifier
+ .animateContentSize()
+ .widthIn(max = 600.dp)
+ .background(Color.Red)
+ ) {
+ val height = if (expanded) 500.dp else 300.dp
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(height)
+ )
+ }
+
+ FlowColumn {
+ Box(
+ modifier = Modifier
+ .size(200.dp)
+ .layout { measurable, constraints ->
+ val placeable = measurable.measure(constraints)
+ layout(placeable.width, placeable.height) {
+ val coords = coordinates
+ if (coords != null) {
+ if (isLookingAhead) {
+ lookaheadOffset = coords
+ .findRootCoordinates()
+ .localLookaheadPositionOf(coords)
+ } else {
+ actualOffset = coords
+ .findRootCoordinates()
+ .localPositionOf(coords, Offset.Zero)
+ }
+ }
+ placeable.place(0, 0)
+ }
+ }
+ .wrapContentWidth()
+ .heightIn(min = 156.dp)
+ .background(Color.Blue)
+ ) {
+ Box(modifier = Modifier.size(200.dp))
+ }
+ }
+ }
+ }
+ }
+ rule.runOnIdle {
+ expanded = !expanded
+ }
+ rule.runOnIdle {
+ assertNotNull(actualOffset)
+ assertEquals(actualOffset, lookaheadOffset)
+ actualOffset = null
+ lookaheadOffset = null
+
+ expanded = !expanded
+ }
+ rule.runOnIdle {
+ assertNotNull(actualOffset)
+ assertEquals(actualOffset, lookaheadOffset)
+ }
+ }
+
@Test
fun grandparentQueryBaseline() {
assertSameLayoutWithAndWithoutLookahead { modifier ->
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LookaheadScope.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LookaheadScope.kt
index ba4ee3b..1d331e9 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LookaheadScope.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/LookaheadScope.kt
@@ -44,7 +44,6 @@
*
* @param content The child composable to be laid out.
*/
-@ExperimentalComposeUiApi
@UiComposable
@Composable
fun LookaheadScope(content: @Composable @UiComposable LookaheadScope.() -> Unit) {
@@ -145,18 +144,19 @@
*
* @sample androidx.compose.ui.samples.LookaheadLayoutCoordinatesSample
*/
-@ExperimentalComposeUiApi
interface LookaheadScope {
/**
* Converts a [LayoutCoordinates] into a [LayoutCoordinates] in the Lookahead coordinates space.
* This is only applicable to child layouts within [LookaheadScope].
*/
+ @ExperimentalComposeUiApi
fun LayoutCoordinates.toLookaheadCoordinates(): LayoutCoordinates
/**
* Returns the [LayoutCoordinates] of the [LookaheadScope]. This is
* only accessible from [Placeable.PlacementScope] (i.e. during placement time).
*/
+ @ExperimentalComposeUiApi
val Placeable.PlacementScope.lookaheadScopeCoordinates: LayoutCoordinates
/**
@@ -165,6 +165,7 @@
* [toLookaheadCoordinates], and 2) invoking [LayoutCoordinates.localPositionOf] with the
* converted coordinates.
*/
+ @ExperimentalComposeUiApi
fun LayoutCoordinates.localLookaheadPositionOf(coordinates: LayoutCoordinates) =
this.toLookaheadCoordinates().localPositionOf(
coordinates.toLookaheadCoordinates(),
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeLayoutDelegate.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeLayoutDelegate.kt
index 4a1772b..3abc4d9 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeLayoutDelegate.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LayoutNodeLayoutDelegate.kt
@@ -1274,6 +1274,7 @@
val owner = layoutNode.requireOwner()
if (!lookaheadLayoutPending && isPlaced) {
+ outerCoordinator.lookaheadDelegate!!.placeSelfApparentToRealOffset(position)
onNodePlaced()
} else {
coordinatesAccessedDuringModifierPlacement = false
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LookaheadDelegate.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LookaheadDelegate.kt
index ba59c04..42c7d6b5 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LookaheadDelegate.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/LookaheadDelegate.kt
@@ -151,14 +151,22 @@
zIndex: Float,
layerBlock: (GraphicsLayerScope.() -> Unit)?
) {
+ placeSelf(position)
+ if (isShallowPlacing) return
+ placeChildren()
+ }
+
+ private fun placeSelf(position: IntOffset) {
if (this.position != position) {
this.position = position
layoutNode.layoutDelegate.lookaheadPassDelegate
?.notifyChildrenUsingCoordinatesWhilePlacing()
coordinator.invalidateAlignmentLinesFromPositionChange()
}
- if (isShallowPlacing) return
- placeChildren()
+ }
+
+ internal fun placeSelfApparentToRealOffset(position: IntOffset) {
+ placeSelf(position + apparentToRealOffset)
}
protected open fun placeChildren() {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/MeasureAndLayoutDelegate.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/MeasureAndLayoutDelegate.kt
index 120e248..9c53aa8 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/MeasureAndLayoutDelegate.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/node/MeasureAndLayoutDelegate.kt
@@ -154,12 +154,16 @@
} else {
layoutNode.markLookaheadMeasurePending()
layoutNode.markMeasurePending()
- if (layoutNode.isPlacedInLookahead == true ||
- layoutNode.canAffectParentInLookahead
+ if ((layoutNode.isPlacedInLookahead == true ||
+ layoutNode.canAffectParentInLookahead) &&
+ layoutNode.parent?.lookaheadMeasurePending != true
) {
- if (layoutNode.parent?.lookaheadMeasurePending != true) {
- relayoutNodes.add(layoutNode, true)
- }
+ relayoutNodes.add(layoutNode, true)
+ } else if (
+ (layoutNode.isPlaced || layoutNode.canAffectParent) &&
+ layoutNode.parent?.measurePending != true
+ ) {
+ relayoutNodes.add(layoutNode, false)
}
!duringMeasureLayout
}
@@ -238,13 +242,17 @@
// dependency on lookahead layout.
layoutNode.markLookaheadLayoutPending()
layoutNode.markLayoutPending()
- if (layoutNode.isPlacedInLookahead == true) {
- val parent = layoutNode.parent
- if (parent?.lookaheadMeasurePending != true &&
- parent?.lookaheadLayoutPending != true
- ) {
- relayoutNodes.add(layoutNode, true)
- }
+
+ val parent = layoutNode.parent
+ if (layoutNode.isPlacedInLookahead == true &&
+ parent?.lookaheadMeasurePending != true &&
+ parent?.lookaheadLayoutPending != true
+ ) {
+ relayoutNodes.add(layoutNode, true)
+ } else if (layoutNode.isPlaced &&
+ parent?.layoutPending != true && parent?.measurePending != true
+ ) {
+ relayoutNodes.add(layoutNode, false)
}
!duringMeasureLayout
}
diff --git a/core/core-performance/samples/build.gradle b/core/core-performance/samples/build.gradle
index 0087990..ef28e3c 100644
--- a/core/core-performance/samples/build.gradle
+++ b/core/core-performance/samples/build.gradle
@@ -30,7 +30,7 @@
}
androidx {
- name = "cSamples"
+ name = "Core Performance Samples"
type = LibraryType.SAMPLES
mavenVersion = LibraryVersions.CORE_PERFORMANCE
inceptionYear = "2021"
diff --git a/core/core-performance/samples/src/main/java/androidx/core/performance/samples/Usage.kt b/core/core-performance/samples/src/main/java/androidx/core/performance/samples/Usage.kt
index ac4214b..62b5e9e 100644
--- a/core/core-performance/samples/src/main/java/androidx/core/performance/samples/Usage.kt
+++ b/core/core-performance/samples/src/main/java/androidx/core/performance/samples/Usage.kt
@@ -19,6 +19,7 @@
package androidx.core.performance.samples
import android.app.Application
+import android.os.Build
import androidx.annotation.Sampled
import androidx.core.performance.DefaultDevicePerformance
import androidx.core.performance.DevicePerformance
@@ -36,6 +37,17 @@
}
fun doSomeThing() {
+ when {
+ devicePerformance.mediaPerformanceClass >= Build.VERSION_CODES.TIRAMISU -> {
+ // Provide the most premium experience for highest performing devices
+ }
+ devicePerformance.mediaPerformanceClass == Build.VERSION_CODES.R -> {
+ // Provide a high quality experience
+ }
+ else -> {
+ // Remove extras to keep experience functional
+ }
+ }
}
}
}
diff --git a/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/BasicCallControlCallbacksTest.kt b/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/BasicCallControlCallbacksTest.kt
index f1fff4d..302eac0 100644
--- a/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/BasicCallControlCallbacksTest.kt
+++ b/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/BasicCallControlCallbacksTest.kt
@@ -70,7 +70,7 @@
*/
@SdkSuppress(minSdkVersion = VERSION_CODES.UPSIDE_DOWN_CAKE)
@LargeTest
- @Test
+ @Test(timeout = 10000)
fun testBasicCallControlCallbackAnswerCall() {
setUpV2Test()
verifyAnswerCall()
@@ -84,7 +84,7 @@
*/
@SdkSuppress(minSdkVersion = VERSION_CODES.UPSIDE_DOWN_CAKE)
@LargeTest
- @Test
+ @Test(timeout = 10000)
fun testRejectCallControlCallbackAnswerCall() {
setUpV2Test()
verifyRejectAnswerCall(Call.STATE_ACTIVE)
@@ -96,7 +96,7 @@
*/
@SdkSuppress(minSdkVersion = VERSION_CODES.UPSIDE_DOWN_CAKE)
@LargeTest
- @Test
+ @Test(timeout = 10000)
fun testRejectCallControlCallbackHoldCall() {
setUpV2Test()
verifyRejectHoldCall()
@@ -108,7 +108,7 @@
*/
@SdkSuppress(minSdkVersion = VERSION_CODES.UPSIDE_DOWN_CAKE)
@LargeTest
- @Test
+ @Test(timeout = 10000)
fun testRejectCallControlCallbackUnholdCall() {
setUpV2Test()
verifyRejectUnholdCall()
@@ -120,7 +120,7 @@
*/
@SdkSuppress(minSdkVersion = VERSION_CODES.UPSIDE_DOWN_CAKE)
@LargeTest
- @Test
+ @Test(timeout = 10000)
fun testRejectCallControlCallbackDisconnectCall() {
setUpV2Test()
verifyRejectDisconnectCall(true)
@@ -132,7 +132,7 @@
*/
@SdkSuppress(minSdkVersion = VERSION_CODES.UPSIDE_DOWN_CAKE)
@LargeTest
- @Test
+ @Test(timeout = 10000)
fun testRejectCallControlCallbackRejectCall() {
setUpV2Test()
verifyRejectDisconnectCall(false)
@@ -145,7 +145,7 @@
*/
@SdkSuppress(minSdkVersion = VERSION_CODES.UPSIDE_DOWN_CAKE)
@LargeTest
- @Test
+ @Test(timeout = 10000)
fun testBasicCallControlCallbackDisconnectCall() {
setUpV2Test()
verifyDisconnectCall()
@@ -158,7 +158,7 @@
*/
@SdkSuppress(minSdkVersion = VERSION_CODES.UPSIDE_DOWN_CAKE)
@LargeTest
- @Test
+ @Test(timeout = 10000)
fun testBasicCallControlCallbackHoldCall() {
setUpV2Test()
verifyHoldCall()
@@ -171,7 +171,7 @@
*/
@SdkSuppress(minSdkVersion = VERSION_CODES.UPSIDE_DOWN_CAKE)
@LargeTest
- @Test
+ @Test(timeout = 10000)
fun testBasicCallControlCallbackUnholdCall() {
setUpV2Test()
verifyUnholdCall()
@@ -189,7 +189,7 @@
*/
@SdkSuppress(minSdkVersion = VERSION_CODES.O)
@LargeTest
- @Test
+ @Test(timeout = 10000)
fun testBasicCallControlCallbackAnswerCall_BackwardsCompat() {
setUpBackwardsCompatTest()
verifyAnswerCall()
@@ -204,7 +204,7 @@
*/
@SdkSuppress(minSdkVersion = VERSION_CODES.O)
@LargeTest
- @Test
+ @Test(timeout = 10000)
fun testRejectCallControlCallbackAnswerCall_BackwardsCompat() {
setUpBackwardsCompatTest()
verifyRejectAnswerCall(Call.STATE_DISCONNECTED)
@@ -217,7 +217,7 @@
*/
@SdkSuppress(minSdkVersion = VERSION_CODES.O)
@LargeTest
- @Test
+ @Test(timeout = 10000)
fun testRejectCallControlCallbackHoldCall_BackwardsCompat() {
setUpBackwardsCompatTest()
verifyRejectHoldCall()
@@ -230,7 +230,7 @@
*/
@SdkSuppress(minSdkVersion = VERSION_CODES.O)
@LargeTest
- @Test
+ @Test(timeout = 10000)
fun testRejectCallControlCallbackUnholdCall_BackwardsCompat() {
setUpBackwardsCompatTest()
verifyRejectUnholdCall()
@@ -243,7 +243,7 @@
*/
@SdkSuppress(minSdkVersion = VERSION_CODES.O)
@LargeTest
- @Test
+ @Test(timeout = 10000)
fun testRejectCallControlCallbackDisconnectCall_BackwardsCompat() {
setUpBackwardsCompatTest()
verifyRejectDisconnectCall(true)
@@ -256,7 +256,7 @@
*/
@SdkSuppress(minSdkVersion = VERSION_CODES.O)
@LargeTest
- @Test
+ @Test(timeout = 10000)
fun testRejectCallControlCallbackRejectCall_BackwardsCompat() {
setUpBackwardsCompatTest()
verifyRejectDisconnectCall(false)
@@ -270,7 +270,7 @@
*/
@SdkSuppress(minSdkVersion = VERSION_CODES.O)
@LargeTest
- @Test
+ @Test(timeout = 10000)
fun testBasicCallControlCallbackDisconnectCall_BackwardsCompat() {
setUpBackwardsCompatTest()
verifyDisconnectCall()
@@ -284,7 +284,7 @@
*/
@SdkSuppress(minSdkVersion = VERSION_CODES.O)
@LargeTest
- @Test
+ @Test(timeout = 10000)
fun testBasicCallControlCallbackHoldCall_BackwardsCompat() {
setUpBackwardsCompatTest()
verifyHoldCall()
@@ -298,7 +298,7 @@
*/
@SdkSuppress(minSdkVersion = VERSION_CODES.O)
@LargeTest
- @Test
+ @Test(timeout = 10000)
fun testBasicCallControlCallbackUnholdCall_BackwardsCompat() {
setUpBackwardsCompatTest()
verifyUnholdCall()
diff --git a/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/BasicCallControlsTest.kt b/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/BasicCallControlsTest.kt
index 5992045..8abbb88 100644
--- a/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/BasicCallControlsTest.kt
+++ b/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/BasicCallControlsTest.kt
@@ -81,7 +81,7 @@
*/
@SdkSuppress(minSdkVersion = VERSION_CODES.UPSIDE_DOWN_CAKE)
@LargeTest
- @Test
+ @Test(timeout = 10000)
fun testBasicOutgoingCall() {
setUpV2Test()
runBlocking_addCallAndSetActive(TestUtils.OUTGOING_CALL_ATTRIBUTES)
@@ -93,7 +93,7 @@
*/
@SdkSuppress(minSdkVersion = VERSION_CODES.UPSIDE_DOWN_CAKE)
@LargeTest
- @Test
+ @Test(timeout = 10000)
fun testBasicIncomingCall() {
setUpV2Test()
runBlocking_addCallAndSetActive(TestUtils.INCOMING_CALL_ATTRIBUTES)
@@ -105,7 +105,7 @@
*/
@SdkSuppress(minSdkVersion = VERSION_CODES.UPSIDE_DOWN_CAKE)
@LargeTest
- @Test
+ @Test(timeout = 10000)
fun testTogglingHoldOnActiveCall() {
setUpV2Test()
runBlocking_ToggleCallAsserts(TestUtils.OUTGOING_CALL_ATTRIBUTES)
@@ -118,7 +118,7 @@
*/
@SdkSuppress(minSdkVersion = VERSION_CODES.UPSIDE_DOWN_CAKE)
@LargeTest
- @Test
+ @Test(timeout = 10000)
fun testTogglingHoldOnActiveCall_NoHoldCapabilities() {
setUpV2Test()
assertFalse(TestUtils.OUTGOING_NO_HOLD_CAP_CALL_ATTRIBUTES
@@ -133,7 +133,7 @@
*/
@SdkSuppress(minSdkVersion = VERSION_CODES.UPSIDE_DOWN_CAKE)
@LargeTest
- @Test
+ @Test(timeout = 10000)
fun testRequestEndpointChange() {
setUpV2Test()
runBlocking_RequestEndpointChangeAsserts()
@@ -146,7 +146,7 @@
*/
@SdkSuppress(minSdkVersion = VERSION_CODES.UPSIDE_DOWN_CAKE)
@LargeTest
- @Test
+ @Test(timeout = 10000)
fun testIsMuted() {
setUpV2Test()
verifyMuteStateChange()
@@ -158,7 +158,7 @@
*/
@SdkSuppress(minSdkVersion = VERSION_CODES.UPSIDE_DOWN_CAKE)
@LargeTest
- @Test
+ @Test(timeout = 10000)
fun testBasicCallControlCallbackOperations_CallbackNotSet() {
setUpV2Test()
verifyAnswerCallFails_CallbackNotSet()
@@ -175,7 +175,7 @@
*/
@SdkSuppress(minSdkVersion = VERSION_CODES.O)
@LargeTest
- @Test
+ @Test(timeout = 10000)
fun testBasicOutgoingCall_BackwardsCompat() {
setUpBackwardsCompatTest()
runBlocking_addCallAndSetActive(TestUtils.OUTGOING_CALL_ATTRIBUTES)
@@ -188,7 +188,7 @@
*/
@SdkSuppress(minSdkVersion = VERSION_CODES.O)
@LargeTest
- @Test
+ @Test(timeout = 10000)
fun testBasicIncomingCall_BackwardsCompat() {
setUpBackwardsCompatTest()
runBlocking_addCallAndSetActive(TestUtils.INCOMING_CALL_ATTRIBUTES)
@@ -201,7 +201,7 @@
*/
@SdkSuppress(minSdkVersion = VERSION_CODES.O)
@LargeTest
- @Test
+ @Test(timeout = 10000)
fun testTogglingHoldOnActiveCall_BackwardsCompat() {
setUpBackwardsCompatTest()
runBlocking_ToggleCallAsserts(TestUtils.OUTGOING_CALL_ATTRIBUTES)
@@ -215,7 +215,7 @@
*/
@SdkSuppress(minSdkVersion = VERSION_CODES.O)
@LargeTest
- @Test
+ @Test(timeout = 10000)
fun testTogglingHoldOnActiveCall_NoHoldCapabilities_BackwardsCompat() {
setUpBackwardsCompatTest()
assertFalse(TestUtils.OUTGOING_NO_HOLD_CAP_CALL_ATTRIBUTES
@@ -231,7 +231,7 @@
*/
@SdkSuppress(minSdkVersion = VERSION_CODES.O)
@LargeTest
- @Test
+ @Test(timeout = 10000)
fun testRequestEndpointChange_BackwardsCompat() {
setUpBackwardsCompatTest()
runBlocking_RequestEndpointChangeAsserts()
@@ -246,7 +246,7 @@
*/
@SdkSuppress(minSdkVersion = VERSION_CODES.O)
@LargeTest
- @Test
+ @Test(timeout = 10000)
fun testIsMuted_BackwardsCompat() {
setUpBackwardsCompatTest()
verifyMuteStateChange()
@@ -259,7 +259,7 @@
*/
@SdkSuppress(minSdkVersion = VERSION_CODES.O)
@LargeTest
- @Test
+ @Test(timeout = 10000)
fun testBasicCallControlCallbackOperations_BackwardsCompat_CallbackNotSet() {
setUpBackwardsCompatTest()
verifyAnswerCallFails_CallbackNotSet()
diff --git a/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/InCallAudioTest.kt b/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/InCallAudioTest.kt
index 280fbdb..48bc53d 100644
--- a/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/InCallAudioTest.kt
+++ b/core/core-telecom/src/androidTest/java/androidx/core/telecom/test/InCallAudioTest.kt
@@ -78,7 +78,7 @@
*/
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@LargeTest
- @Test
+ @Test(timeout = 10000)
fun testAddCallAssertModeInCommunication() {
setUpV2Test()
runBlocking_addCall_assertAudioModeInCommunication()
@@ -95,7 +95,7 @@
*/
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
@LargeTest
- @Test
+ @Test(timeout = 10000)
fun testAddCallAssertModeInCommunication_BackwardsCompat() {
setUpBackwardsCompatTest()
runBlocking_addCall_assertAudioModeInCommunication()
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/PublicKeyCredentialControllerUtility.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/PublicKeyCredentialControllerUtility.kt
index a18deea..4032e6e 100644
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/PublicKeyCredentialControllerUtility.kt
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/PublicKeyCredentialControllerUtility.kt
@@ -74,7 +74,7 @@
private val JSON_KEY_CLIENT_DATA = "clientDataJSON"
private val JSON_KEY_ATTESTATION_OBJ = "attestationObject"
- private val JSON_KEY_AUTH_DATA = "authenticationData"
+ private val JSON_KEY_AUTH_DATA = "authenticatorData"
private val JSON_KEY_SIGNATURE = "signature"
private val JSON_KEY_USER_HANDLE = "userHandle"
private val JSON_KEY_RESPONSE = "response"
diff --git a/development/referenceDocs/switcher.py b/development/referenceDocs/switcher.py
index 562d433..e73e4c6 100755
--- a/development/referenceDocs/switcher.py
+++ b/development/referenceDocs/switcher.py
@@ -81,14 +81,14 @@
stub = doc.replace(java_source_abs_path, kotlin_source_abs_path)
# Always add the switcher for java files, switch to the package summary if
# the page itself doesn't exist in kotlin
- slug1 = "sed -i 's/<div id=\"refdoc-switcher-placeholder\">/{}/' {}".format("\\n{% setvar page_path %}_page_path_{% endsetvar %}\\n{% setvar can_switch %}1{% endsetvar %}\\n{% include \"reference\/_java_switcher2.md\" %}",doc)
+ slug1 = "sed -i 's/<div id=\"refdoc-switcher-placeholder\"><\/div>/{}/' {}".format("\\n{% setvar page_path %}_page_path_{% endsetvar %}\\n{% setvar can_switch %}1{% endsetvar %}\\n{% include \"reference\/_java_switcher2.md\" %}",doc)
else:
file_path = doc[len(kotlin_ref_root)+1:]
stub = doc.replace(kotlin_source_abs_path, java_source_abs_path)
if (both):
- slug1 = "sed -i 's/<div id=\"refdoc-switcher-placeholder\">/{}/' {}".format("\\n{% setvar page_path %}_page_path_{% endsetvar %}\\n{% setvar can_switch %}1{% endsetvar %}\\n{% include \"reference\/_kotlin_switcher2.md\" %}",doc)
+ slug1 = "sed -i 's/<div id=\"refdoc-switcher-placeholder\"><\/div>/{}/' {}".format("\\n{% setvar page_path %}_page_path_{% endsetvar %}\\n{% setvar can_switch %}1{% endsetvar %}\\n{% include \"reference\/_kotlin_switcher2.md\" %}",doc)
else:
- slug1 = "sed -i 's/<div id=\"refdoc-switcher-placeholder\">/{}/' {}".format("\\n{% include \"reference\/_kotlin_switcher2.md\" %}",doc)
+ slug1 = "sed -i 's/<div id=\"refdoc-switcher-placeholder\"><\/div>/{}/' {}".format("\\n{% include \"reference\/_kotlin_switcher2.md\" %}",doc)
os.system(slug1)
if both or java:
diff --git a/docs/api_guidelines/compat.md b/docs/api_guidelines/compat.md
index 6ec06be..a86ad2a 100644
--- a/docs/api_guidelines/compat.md
+++ b/docs/api_guidelines/compat.md
@@ -404,43 +404,75 @@
### Inter-process communication {#ipc}
Protocols and data structures used for IPC must support interoperability between
-different versions of libraries and should be treated similarly to public API;
-however, AndroidX does not currently implement compatibility tracking for IPC.
-
-We recommend the following, in order of preference:
-
-1. Stable AIDL if (1) your project lives partially in the Android platform and
- has access to Stable AIDL build rules and (2) you need to support Android's
- `Parcelable` data types. The AndroidX workflow **does not** provide Stable
- AIDL compilation or compatibility checks, so these would need to happen in
- the platform build and the resulting `.java` files would need to be copied
- out.
-2. Protobuf if (1) your project needs to persist data to disk or (2) you need
- interoperability with systems already using Protobuf. Similar to Stable
- AIDL, the AndroidX workflow **does not** provide built-in support Protobuf
- compilation or compatibility checks. It is possible to use a Proto plug-in,
- but you will be responsible for bundling the runtime and maintaining
- compatibility on your own.
-3. `Bundle` if you have a very simple data model that is unlikely to change in
- the future. `Bundle` has the weakest type safety and compatibility
- guarantees of any recommendation, and it has many caveats that make it a
- poor choice.
-4. `VersionedParcelable` if your project is already using Versioned Parcelable
- and is aware of its compatibility constraints.
-
-We are currently evaluating Square's [Wire](https://github.com/square/wire) and
-Google's [gRPC](https://grpc.io/) libraries for recommendation. If either of
-these libraries meets your team's needs based on your own research, feel free to
-use them.
+different versions of libraries and should be treated similarly to public API.
**Do not** design your own serialization mechanism or wire format for disk
storage or inter-process communication. Preserving and verifying compatibility
is difficult and error-prone.
-In all cases, **do not** expose your serialization mechanism in your API
-surface. Neither Stable AIDL nor Protobuf generate stable language APIs.
+**Do not** expose your serialization mechanism in your API surface. Neither
+Stable AIDL nor Protobuf generate stable language APIs.
-#### Annotating unstable IPC
+Generally, any communication prototcol, handshake, etc. must maintain
+compatibility consistent with SemVer guidelines. Consider how your protocol will
+handle addition and removal of operations or constants, compatibility-breaking
+changes, and other modifications without crashing either the host or client
+process.
+
+We recommend the following IPC mechanisms, in order of preference:
+
+#### Stable AIDL <a name="ipc-stableaidl"></a>
+
+Stable AIDL is used by the Android platform and AndroidX to provide a
+platform-native IPC mechanism with strong inter-process compatibility
+guarantees. It supports a subset of standard AIDL.
+
+Use Stable AIDL if your library:
+
+- Needs to send and receive Android's `Parcelable` data types
+- Communicates directly with the Android platform, System UI, or other AOSP
+ components *or* is likely to do so in the future
+
+**Do not** use Stable AIDL to persist data to disk.
+
+##### Using Stable AIDL {#ipc-stableaidl-using}
+
+To add Stable AIDL definitions to your project:
+
+1. Add the Stable AIDL plugin to `build.gradle`:
+
+ ```
+ plugins {
+ id("androidx.stableaidl")
+ }
+ ```
+
+2. Enable the AIDL build feature and specify an initial version for your Stable
+ AIDL interfaces in `build.gradle`:
+
+ ```
+ android {
+ buildFeatures {
+ aidl = true
+ }
+ buildTypes.all {
+ stableAidl {
+ version 1
+ }
+ }
+ }
+ ```
+
+3. Migrate existing AIDL files or create new AIDL files under
+ `<project>/src/main/stableAidl`
+
+4. Generate an initial set of Stable AIDL API tracking files by running
+
+ ```
+ ./gradlew :path:to:project:updateAidlApi
+ ```
+
+##### Annotating unstable AIDL {#ipc-stableaidl-unstable}
Once an API that relies on an IPC contract ships to production in an app, the
contract is locked in and must maintain compatibility to prevent crashing either
@@ -485,25 +517,29 @@
annotations are required for Stable AIDL definition files under
`src/stableAidl`.
-#### Parcelable {#ipc-parcelable}
+#### Protobuf <a name="ipc-protobuf"></a>
-**Do not** implement `Parcelable` for any class that may be used for IPC or
-otherwise exposed as public API. By default, `Parcelable` does not provide any
-compatibility guarantees and will result in crashes if fields are added or
-removed between library versions. If you are using Stable AIDL, you *may* use
-AIDL-defined parcelables for IPC but not public API.
+Protobuf is used by many Google applications and services to provide an IPC and
+disk persistence mechanism with strong inter-process compatibility guarantees.
-NOTE As of 2022/12/16, we are working on experimental support for compiling and
-tracking Stable AIDL definitions within the AndroidX workflow.
+Use Protobuf if your library:
-#### Protobuf {#ipc-protobuf}
+- Communicates directly with other applications or services already using
+ Protobuf
+- Your data structure is complex and likely to change over time - Needs to
+ persist data to disk
-Developers **should** use protocol buffers for most cases. See
-[Protobuf](#dependencies-protobuf) for more information on using protocol
-buffers in your library. **Do** use protocol buffers if your data structure is
-complex and likely to change over time. If your data includes `FileDescriptor`s,
-`Binder`s, or other platform-defined `Parcelable` data structures, they will
-need to be stored alongside the protobuf bytes in a `Bundle`.
+If your data includes `FileDescriptor`s, `Binder`s, or other platform-defined
+`Parcelable` data structures, consider using Stable AIDL instead. Protobuf
+cannot directly handle these types, and they will need to be stored alongside
+the serialized Protobuf bytes in a `Bundle`.
+
+See [Protobuf](#dependencies-protobuf) for more information on using protocol
+buffers in your library.
+
+WARNING While Protobuf is capable of maintaining inter-process compatibility,
+AndroidX does not currently provide compatibility tracking or enforcement.
+Library owners must perform their own validation.
NOTE We are currently investigating the suitability of Square's
[`wire` library](https://github.com/square/wire) for handling protocol buffers
@@ -511,12 +547,24 @@
Libraries that expose their serialization mechanism in their API surface *will
not be able to migrate*.
-#### Bundle {#ipc-bundle}
+#### Bundle <a name="ipc-bundle"></a>
-Developers **may** use `Bundle` in simple cases that require sending `Binder`s,
-`FileDescriptor`s, or platform `Parcelable`s across IPC
-([example](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:core/core/src/main/java/androidx/core/graphics/drawable/IconCompat.java;l=820)).
-Note that `Bundle` has several caveats:
+`Bundle` is used by the Android platform and AndroidX as a lightweight IPC
+mechanism. It has the weakest type safety and compatibility guarantees of any
+recommendation, and it has many caveats that make it a poor choice.
+
+In some cases, you may need to use a `Bundle` to wrap another IPC mechanism so
+that it can be passed through Android platform APIs, e.g. a `Bundle` that wraps
+a `byte[]` representing a serialized Protobuf.
+
+Use `Bundle` if your library:
+
+- Has a very simple data model that is unlikely to change in the future
+- Needs to send or receive `Binder`s, `FileDescriptor`s, or platform-defined
+ `Parcelable`s
+ ([example](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:core/core/src/main/java/androidx/core/graphics/drawable/IconCompat.java;l=820))
+
+Caveats for `Bundle` include:
- When running on Android S and below, accessing *any* entry in a `Bundle`
will result in the platform attempting to deserialize *every* entry. This
@@ -535,9 +583,39 @@
are responsible for providing their own system for guaranteeing wire format
compatibility between versions.
-#### Communication protocols {#ipc-protocol}
+#### Versioned Parcelable <a name="ipc-versionedparcelable"></a>
-Any communication prototcol, handshake, etc. must maintain compatibility
-consistent with SemVer guidelines. Consider how your protocol will handle
-addition and removal of operations or constants, compatibility-breaking changes,
-and other modifications without crashing either the host or client process.
+`VersionedParcelable` is a deprecated library that was intended to provide
+compatibility guarantees around the Android platform's `Parcelable` class;
+however, the initial version contained bugs and it was not actively maintained.
+
+Use `VersionedParcelable` if your library:
+
+- Is already using `VersionedParcelable` and you are aware of its
+ compatibility constraints
+
+**Do not** use `VersionedParcelable` in all other cases.
+
+#### Wire <a name="ipc-wire"></a>
+
+We are currently evaluating Square's [Wire](https://github.com/square/wire) as a
+front-end to Protobuf. If this library meets your team's needs based on your own
+research, feel free to use it.
+
+#### gRPC <a name="ipc-grpc"></a>
+
+Some clients have requested to use Google's [gRPC](https://grpc.io/) library to
+align with other Google products. It's okay to use gRPC for network
+communication or communication with libraries and services outside of AndroidX
+that are already using gRPC.
+
+**Do not** use gRPC to communicate between AndroidX libraries or with the
+Android platform.
+
+#### Parcelable <a name="ipc-parcelable"></a>
+
+**Do not** implement `Parcelable` for any class that may be used for IPC or
+otherwise exposed as public API. By default, `Parcelable` does not provide any
+compatibility guarantees and will result in crashes if fields are added or
+removed between library versions. If you are using Stable AIDL, you *may* use
+AIDL-defined parcelables for IPC but not public API.
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentAnimationTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentAnimationTest.kt
index c2dea61..06749ed 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentAnimationTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentAnimationTest.kt
@@ -25,6 +25,8 @@
import android.view.animation.Animation
import android.view.animation.AnimationUtils
import android.view.animation.TranslateAnimation
+import android.window.BackEvent
+import androidx.activity.BackEventCompat
import androidx.annotation.AnimRes
import androidx.annotation.LayoutRes
import androidx.core.view.ViewCompat
@@ -613,6 +615,64 @@
assertThat(fragment1.requireView().parent).isNotNull()
}
+ @Test
+ fun replaceOperationWithAnimationsThenSystemBack() {
+ waitForAnimationReady()
+ val fm1 = activityRule.activity.supportFragmentManager
+
+ val fragment1 = AnimationListenerFragment(R.layout.scene1)
+ fm1.beginTransaction()
+ .replace(R.id.fragmentContainer, fragment1, "1")
+ .addToBackStack(null)
+ .commit()
+ activityRule.waitForExecution()
+
+ val fragment2 = AnimationListenerFragment()
+
+ fm1.beginTransaction()
+ .setCustomAnimations(
+ R.anim.fade_in,
+ R.anim.fade_out,
+ R.anim.fade_in,
+ R.anim.fade_out
+ )
+ .replace(R.id.fragmentContainer, fragment2, "2")
+ .addToBackStack(null)
+ .commit()
+ activityRule.executePendingTransactions(fm1)
+
+ assertThat(fragment2.startAnimationLatch.await(1000, TimeUnit.MILLISECONDS)).isTrue()
+ // We need to wait for the exit animation to end
+ assertThat(fragment1.exitLatch.await(1000, TimeUnit.MILLISECONDS)).isTrue()
+
+ val dispatcher = activityRule.activity.onBackPressedDispatcher
+ activityRule.runOnUiThread {
+ dispatcher.dispatchOnBackStarted(BackEventCompat(0.1F, 0.1F, 0.1F, BackEvent.EDGE_LEFT))
+ }
+ activityRule.executePendingTransactions(fm1)
+ activityRule.runOnUiThread {
+ dispatcher.dispatchOnBackProgressed(
+ BackEventCompat(0.2F, 0.2F, 0.2F, BackEvent.EDGE_LEFT)
+ )
+ dispatcher.onBackPressed()
+ }
+ activityRule.executePendingTransactions(fm1)
+
+ assertThat(fragment2.startAnimationLatch.await(1000, TimeUnit.MILLISECONDS)).isTrue()
+ // Now fragment2 should be animating away
+ assertThat(fragment2.isAdded).isFalse()
+ assertThat(fm1.findFragmentByTag("2"))
+ .isEqualTo(null) // fragmentManager does not know about animating fragment
+ assertThat(fragment2.parentFragmentManager)
+ .isEqualTo(fm1) // but the animating fragment knows the fragmentManager
+
+ // We need to wait for the exit animation to end
+ assertThat(fragment2.exitLatch.await(1000, TimeUnit.MILLISECONDS)).isTrue()
+
+ // Make sure the original fragment was correctly readded to the container
+ assertThat(fragment1.requireView().parent).isNotNull()
+ }
+
// When an animation is running on a Fragment's View, the view shouldn't be
// prevented from being removed. There's no way to directly test this, so we have to
// test to see if the animation is still running.
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/DefaultSpecialEffectsController.kt b/fragment/fragment/src/main/java/androidx/fragment/app/DefaultSpecialEffectsController.kt
index 58a7345..bd08997 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/DefaultSpecialEffectsController.kt
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/DefaultSpecialEffectsController.kt
@@ -767,6 +767,14 @@
}
private class AnimationEffect(val animationInfo: AnimationInfo) : Effect() {
+ override fun onStart(container: ViewGroup) {
+ val operation: Operation = animationInfo.operation
+
+ val finalState = operation.finalState
+ if (finalState !== Operation.State.REMOVED) {
+ operation.effects.add(NoOpEffect(animationInfo))
+ }
+ }
override fun onCommit(container: ViewGroup) {
val context = container.context
val operation: Operation = animationInfo.operation
@@ -780,10 +788,6 @@
// If the operation does not remove the view, we can't use a
// AnimationSet due that causing the introduction of visual artifacts (b/163084315).
viewToAnimate.startAnimation(anim)
- // This means we can't use setAnimationListener() without overriding
- // any listener that the Fragment has set themselves, so we
- // just mark the special effect as complete immediately.
- operation.effects.add(NoOpEffect(animationInfo))
} else {
container.startViewTransition(viewToAnimate)
val animation: Animation = FragmentAnim.EndViewTransitionAnimation(anim,
@@ -968,7 +972,7 @@
"SpecialEffectsController: Container $container has not been " +
"laid out. Completing operation $operation")
}
- operation.effects.add(NoOpEffect(transitionInfo))
+ transitionInfo.completeSpecialEffect()
} else {
transitionImpl.setListenerForTransitionEnd(
transitionInfo.operation.fragment,
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index ea26f32..54778a9 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -250,7 +250,7 @@
reactiveStreams = { module = "org.reactivestreams:reactive-streams", version = "1.0.0" }
retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
retrofitConverterWire = { module = "com.squareup.retrofit2:converter-wire", version.ref = "retrofit" }
-robolectric = { module = "org.robolectric:robolectric", version = "4.9.2" }
+robolectric = { module = "org.robolectric:robolectric", version = "4.10.3" }
rxjava2 = { module = "io.reactivex.rxjava2:rxjava", version = "2.2.9" }
rxjava3 = { module = "io.reactivex.rxjava3:rxjava", version = "3.0.0" }
shadow = { module = "gradle.plugin.com.github.johnrengelman:shadow", version = "7.1.1" }
diff --git a/libraryversions.toml b/libraryversions.toml
index 4972719..cdb7b5b 100644
--- a/libraryversions.toml
+++ b/libraryversions.toml
@@ -17,11 +17,11 @@
CAMERA = "1.3.0-beta02"
CAMERA_PIPE = "1.0.0-alpha01"
CARDVIEW = "1.1.0-alpha01"
-CAR_APP = "1.4.0-alpha02"
+CAR_APP = "1.4.0-beta01"
COLLECTION = "1.3.0-alpha05"
COMPOSE = "1.6.0-alpha02"
COMPOSE_COMPILER = "1.5.1"
-COMPOSE_MATERIAL3 = "1.2.0-alpha04"
+COMPOSE_MATERIAL3 = "1.2.0-alpha05"
COMPOSE_MATERIAL3_ADAPTIVE = "1.0.0-alpha01"
COMPOSE_RUNTIME_TRACING = "1.0.0-alpha03"
CONSTRAINTLAYOUT = "2.2.0-alpha11"
@@ -29,7 +29,7 @@
CONSTRAINTLAYOUT_CORE = "1.1.0-alpha11"
CONTENTPAGER = "1.1.0-alpha01"
COORDINATORLAYOUT = "1.3.0-alpha01"
-CORE = "1.12.0-beta01"
+CORE = "1.12.0-rc01"
CORE_ANIMATION = "1.0.0-rc01"
CORE_ANIMATION_TESTING = "1.0.0-rc01"
CORE_APPDIGEST = "1.0.0-alpha01"
@@ -68,7 +68,7 @@
GRAPHICS_FILTERS = "1.0.0-alpha01"
GRAPHICS_SHAPES = "1.0.0-alpha03"
GRIDLAYOUT = "1.1.0-beta02"
-HEALTH_CONNECT = "1.1.0-alpha03"
+HEALTH_CONNECT = "1.1.0-alpha04"
HEALTH_SERVICES_CLIENT = "1.1.0-alpha01"
HEIFWRITER = "1.1.0-alpha02"
HILT = "1.1.0-alpha01"
@@ -151,11 +151,11 @@
WEAR_INPUT_TESTING = "1.2.0-alpha03"
WEAR_ONGOING = "1.1.0-alpha01"
WEAR_PHONE_INTERACTIONS = "1.1.0-alpha04"
-WEAR_PROTOLAYOUT = "1.0.0-rc01"
+WEAR_PROTOLAYOUT = "1.1.0-alpha01"
WEAR_REMOTE_INTERACTIONS = "1.1.0-alpha01"
-WEAR_TILES = "1.2.0-rc01"
+WEAR_TILES = "1.3.0-alpha01"
WEAR_WATCHFACE = "1.2.0-alpha09"
-WEBKIT = "1.8.0-beta01"
+WEBKIT = "1.8.0-rc01"
WINDOW = "1.2.0-beta01"
WINDOW_EXTENSIONS = "1.2.0-rc01"
WINDOW_EXTENSIONS_CORE = "1.1.0-alpha01"
diff --git a/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/DerivedWithOverriddenMethodsWithLfAnnotation.java b/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/DerivedWithOverriddenMethodsWithLfAnnotation.java
deleted file mode 100644
index ac0474a..0000000
--- a/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/DerivedWithOverriddenMethodsWithLfAnnotation.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.lifecycle.observers;
-
-import androidx.lifecycle.Lifecycle;
-
-@SuppressWarnings("deprecation")
-public class DerivedWithOverriddenMethodsWithLfAnnotation extends Base {
-
- @androidx.lifecycle.OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
- @Override
- public void onCreate() {
- super.onCreate();
- }
-}
diff --git a/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/Interface1.java b/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/DerivedWithOverriddenMethodsWithLfAnnotation.kt
similarity index 74%
copy from lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/Interface1.java
copy to lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/DerivedWithOverriddenMethodsWithLfAnnotation.kt
index 31d0e6f..f239dfc 100644
--- a/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/Interface1.java
+++ b/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/DerivedWithOverriddenMethodsWithLfAnnotation.kt
@@ -13,15 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package androidx.lifecycle.observers
-package androidx.lifecycle.observers;
+import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.Lifecycle;
-import androidx.lifecycle.LifecycleObserver;
-
-@SuppressWarnings("deprecation")
-public interface Interface1 extends LifecycleObserver {
-
+@Suppress("DEPRECATION")
+class DerivedWithOverriddenMethodsWithLfAnnotation : Base() {
@androidx.lifecycle.OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
- void onCreate();
+ override fun onCreate() {
+ super.onCreate()
+ }
}
diff --git a/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/Interface1.java b/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/Interface1.kt
similarity index 74%
rename from lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/Interface1.java
rename to lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/Interface1.kt
index 31d0e6f..fb49485 100644
--- a/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/Interface1.java
+++ b/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/Interface1.kt
@@ -13,15 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package androidx.lifecycle.observers
-package androidx.lifecycle.observers;
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleObserver
-import androidx.lifecycle.Lifecycle;
-import androidx.lifecycle.LifecycleObserver;
-
-@SuppressWarnings("deprecation")
-public interface Interface1 extends LifecycleObserver {
-
+@Suppress("DEPRECATION")
+interface Interface1 : LifecycleObserver {
@androidx.lifecycle.OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
- void onCreate();
+ fun onCreate()
}
diff --git a/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/Interface2.java b/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/Interface2.kt
similarity index 74%
rename from lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/Interface2.java
rename to lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/Interface2.kt
index f272601..b3faa538a 100644
--- a/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/Interface2.java
+++ b/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/Interface2.kt
@@ -13,18 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package androidx.lifecycle.observers
-package androidx.lifecycle.observers;
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleObserver
-import androidx.lifecycle.Lifecycle;
-import androidx.lifecycle.LifecycleObserver;
-
-@SuppressWarnings("deprecation")
-public interface Interface2 extends LifecycleObserver {
-
+@Suppress("DEPRECATION")
+interface Interface2 : LifecycleObserver {
@androidx.lifecycle.OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
- void onCreate();
+ fun onCreate()
@androidx.lifecycle.OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
- void onDestroy();
+ fun onDestroy()
}
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/build.gradle b/privacysandbox/sdkruntime/sdkruntime-client/build.gradle
index 704d543..3c7ef5e 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/build.gradle
+++ b/privacysandbox/sdkruntime/sdkruntime-client/build.gradle
@@ -15,6 +15,7 @@
*/
import androidx.build.LibraryType
+import javax.inject.Inject
plugins {
id("AndroidXPlugin")
@@ -22,6 +23,72 @@
id("org.jetbrains.kotlin.android")
}
+abstract class BundleTestSdkDexTask extends DefaultTask {
+
+ @InputFiles
+ @PathSensitive(PathSensitivity.NAME_ONLY)
+ abstract ConfigurableFileCollection getTestSdkApkFolders()
+
+ @OutputDirectory
+ abstract DirectoryProperty getOutputDir()
+
+ @Inject
+ abstract FileSystemOperations getFileSystemOperations()
+
+ @Inject
+ abstract ArchiveOperations getArchiveOperations()
+
+ @TaskAction
+ def exec() {
+ for (File folder : testSdkApkFolders.files) {
+ for (File file : folder.listFiles()) {
+ String fileName = file.name
+ if (!fileName.endsWith(".apk")) {
+ continue
+ }
+
+ int projectNameEnd = fileName.indexOf("-")
+ String projectName = projectNameEnd > 0 ? fileName.substring(0, projectNameEnd) : fileName
+
+ fileSystemOperations.copy {
+ from(archiveOperations.zipTree(file))
+ into(outputDir.dir("test-sdks/$projectName/"))
+ include('*.dex')
+ }
+ }
+ }
+ }
+}
+
+def bundleTestSdkDexTaskProvider = tasks.register("bundleTestSdkDexTask", BundleTestSdkDexTask) {
+ description("Bundle DEX from the androidTestBundleDex configuration into assets folder")
+
+ def configuration = configurations.getByName("androidTestBundleDex")
+ testSdkApkFolders.from(configuration.incoming.artifactView {}.files)
+}
+
+androidComponents {
+ onVariants(selector().withBuildType("debug")) {
+ androidTest.sources.assets.addGeneratedSourceDirectory(
+ bundleTestSdkDexTaskProvider,
+ BundleTestSdkDexTask::getOutputDir
+ )
+ }
+}
+
+configurations {
+ androidTestBundleDex {
+ canBeConsumed = false
+ canBeResolved = true
+ attributes {
+ attribute(
+ LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE,
+ objects.named(LibraryElements, "testSdkApk")
+ )
+ }
+ }
+}
+
dependencies {
api(libs.kotlinStdlib)
api(libs.kotlinCoroutinesCore)
@@ -48,6 +115,8 @@
androidTestImplementation(libs.mockitoCore, excludes.bytebuddy) // DexMaker has it"s own MockMaker
androidTestImplementation(libs.dexmakerMockitoInline, excludes.bytebuddy) // DexMaker has it"s own MockMaker
+
+ androidTestBundleDex(project(":privacysandbox:sdkruntime:test-sdks:current"))
}
android {
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/LocalSdkProviderTest.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/LocalSdkProviderTest.kt
index 4149ac0..09f256a 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/LocalSdkProviderTest.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/androidTest/java/androidx/privacysandbox/sdkruntime/client/loader/LocalSdkProviderTest.kt
@@ -19,7 +19,6 @@
import android.os.Binder
import android.os.Bundle
import android.os.IBinder
-import android.view.View
import androidx.lifecycle.Lifecycle
import androidx.privacysandbox.sdkruntime.client.EmptyActivity
import androidx.privacysandbox.sdkruntime.client.TestActivityHolder
@@ -31,7 +30,6 @@
import androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException
import androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat
import androidx.privacysandbox.sdkruntime.core.SandboxedSdkInfo
-import androidx.privacysandbox.sdkruntime.core.SandboxedSdkProviderCompat
import androidx.privacysandbox.sdkruntime.core.Versions
import androidx.privacysandbox.sdkruntime.core.activity.SdkSandboxActivityHandlerCompat
import androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCompat
@@ -227,60 +225,6 @@
assertThat(controller.sdkActivityHandlers[token]).isNull()
}
- class CurrentVersionProviderLoadTest : SandboxedSdkProviderCompat() {
- @JvmField
- var onLoadSdkBinder: Binder? = null
-
- @JvmField
- var lastOnLoadSdkParams: Bundle? = null
-
- @JvmField
- var isBeforeUnloadSdkCalled = false
-
- @Throws(LoadSdkCompatException::class)
- override fun onLoadSdk(params: Bundle): SandboxedSdkCompat {
- val result = CurrentVersionSdkTest(context!!)
- onLoadSdkBinder = result
-
- lastOnLoadSdkParams = params
- if (params.getBoolean("needFail", false)) {
- throw LoadSdkCompatException(RuntimeException(), params)
- }
- return SandboxedSdkCompat(result)
- }
-
- override fun beforeUnloadSdk() {
- isBeforeUnloadSdkCalled = true
- }
-
- override fun getView(
- windowContext: Context,
- params: Bundle,
- width: Int,
- height: Int
- ): View {
- return View(windowContext)
- }
- }
-
- @Suppress("unused") // Reflection calls
- internal class CurrentVersionSdkTest(
- private val context: Context
- ) : Binder() {
- fun getSandboxedSdks(): List<SandboxedSdkCompat> =
- SdkSandboxControllerCompat.from(context).getSandboxedSdks()
-
- fun getAppOwnedSdkSandboxInterfaces(): List<AppOwnedSdkSandboxInterfaceCompat> =
- SdkSandboxControllerCompat.from(context).getAppOwnedSdkSandboxInterfaces()
-
- fun registerSdkSandboxActivityHandler(handler: SdkSandboxActivityHandlerCompat): IBinder =
- SdkSandboxControllerCompat.from(context).registerSdkSandboxActivityHandler(handler)
-
- fun unregisterSdkSandboxActivityHandler(handler: SdkSandboxActivityHandlerCompat) {
- SdkSandboxControllerCompat.from(context).unregisterSdkSandboxActivityHandler(handler)
- }
- }
-
internal class TestClassLoaderFactory(
private val testStorage: TestLocalSdkStorage
) : SdkLoader.ClassLoaderFactory {
@@ -364,35 +308,23 @@
)
}
- // add SDK loaded from test sources
+ val currentVersionSdk = TestSdkInfo(
+ Versions.API_VERSION,
+ "test-sdks/current/classes.dex",
+ "androidx.privacysandbox.sdkruntime.testsdk.current.CompatProvider"
+ )
val controller = TestStubController()
+
+ val loadedSdk = loadTestSdkFromAssets(currentVersionSdk.localSdkConfig, controller)
+ assertThat(loadedSdk.extractApiVersion())
+ .isEqualTo(currentVersionSdk.apiVersion)
+
add(
arrayOf(
- "BuiltFromSource",
- Versions.API_VERSION,
+ currentVersionSdk.localSdkConfig.dexPaths[0],
+ currentVersionSdk.apiVersion,
controller,
- loadTestSdkFromSource(controller),
- )
- )
- }
-
- private fun loadTestSdkFromSource(controller: TestStubController): LocalSdkProvider {
- val sdkLoader = SdkLoader(
- object : SdkLoader.ClassLoaderFactory {
- override fun createClassLoaderFor(
- sdkConfig: LocalSdkConfig,
- parent: ClassLoader
- ): ClassLoader = javaClass.classLoader!!
- },
- ApplicationProvider.getApplicationContext(),
- controller
- )
-
- return sdkLoader.loadSdk(
- LocalSdkConfig(
- packageName = "test.CurrentVersionProviderLoadTest",
- dexPaths = emptyList(),
- entryPoint = CurrentVersionProviderLoadTest::class.java.name
+ loadedSdk
)
)
}
diff --git a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/SandboxedSdkContextCompat.kt b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/SandboxedSdkContextCompat.kt
index e08530e..2d2d2a7 100644
--- a/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/SandboxedSdkContextCompat.kt
+++ b/privacysandbox/sdkruntime/sdkruntime-client/src/main/java/androidx/privacysandbox/sdkruntime/client/loader/impl/SandboxedSdkContextCompat.kt
@@ -224,6 +224,9 @@
val targetBaseDataDir = Api24.dataDir(baseContext)
val targetSharedPreferencesDir = File(targetBaseDataDir, "shared_prefs")
+ if (!targetSharedPreferencesDir.exists()) {
+ targetSharedPreferencesDir.mkdir()
+ }
if (sourceSharedPreferencesDir == targetSharedPreferencesDir) {
return true
diff --git a/privacysandbox/sdkruntime/test-sdks/current/build.gradle b/privacysandbox/sdkruntime/test-sdks/current/build.gradle
new file mode 100644
index 0000000..c69ab15
--- /dev/null
+++ b/privacysandbox/sdkruntime/test-sdks/current/build.gradle
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 com.android.build.api.artifact.SingleArtifact
+
+plugins {
+ id("AndroidXPlugin")
+ id("com.android.application")
+ id("org.jetbrains.kotlin.android")
+}
+
+android {
+ namespace "androidx.privacysandbox.sdkruntime.testsdk.current"
+}
+
+dependencies {
+ implementation(project(":privacysandbox:sdkruntime:sdkruntime-core"))
+}
+
+/*
+ * Allow integration tests to consume the APK produced by this project
+ */
+configurations {
+ testSdkApk {
+ canBeConsumed = true
+ canBeResolved = false
+ attributes {
+ attribute(
+ LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE,
+ objects.named(LibraryElements, "testSdkApk")
+ )
+ }
+ }
+}
+
+androidComponents {
+ beforeVariants(selector().all()) { enabled = buildType == 'release' }
+ onVariants(selector().all().withBuildType("release"), { variant ->
+ artifacts {
+ testSdkApk(variant.artifacts.get(SingleArtifact.APK.INSTANCE))
+ }
+ })
+}
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/test-sdks/current/src/main/AndroidManifest.xml b/privacysandbox/sdkruntime/test-sdks/current/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..8de5974
--- /dev/null
+++ b/privacysandbox/sdkruntime/test-sdks/current/src/main/AndroidManifest.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+</manifest>
\ No newline at end of file
diff --git a/privacysandbox/sdkruntime/test-sdks/current/src/main/java/androidx/privacysandbox/sdkruntime/testsdk/current/CompatProvider.kt b/privacysandbox/sdkruntime/test-sdks/current/src/main/java/androidx/privacysandbox/sdkruntime/testsdk/current/CompatProvider.kt
new file mode 100644
index 0000000..03f31ea
--- /dev/null
+++ b/privacysandbox/sdkruntime/test-sdks/current/src/main/java/androidx/privacysandbox/sdkruntime/testsdk/current/CompatProvider.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.privacysandbox.sdkruntime.testsdk.current
+
+import android.content.Context
+import android.os.Binder
+import android.os.Bundle
+import android.os.IBinder
+import android.view.View
+import androidx.privacysandbox.sdkruntime.core.AppOwnedSdkSandboxInterfaceCompat
+import androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException
+import androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat
+import androidx.privacysandbox.sdkruntime.core.SandboxedSdkProviderCompat
+import androidx.privacysandbox.sdkruntime.core.activity.SdkSandboxActivityHandlerCompat
+import androidx.privacysandbox.sdkruntime.core.controller.SdkSandboxControllerCompat
+
+@Suppress("unused") // Reflection usage from tests in privacysandbox:sdkruntime:sdkruntime-client
+class CompatProvider : SandboxedSdkProviderCompat() {
+ @JvmField
+ var onLoadSdkBinder: Binder? = null
+
+ @JvmField
+ var lastOnLoadSdkParams: Bundle? = null
+
+ @JvmField
+ var isBeforeUnloadSdkCalled = false
+
+ @Throws(LoadSdkCompatException::class)
+ override fun onLoadSdk(params: Bundle): SandboxedSdkCompat {
+ val result = CurrentVersionSdkTest(context!!)
+ onLoadSdkBinder = result
+
+ lastOnLoadSdkParams = params
+ if (params.getBoolean("needFail", false)) {
+ throw LoadSdkCompatException(RuntimeException(), params)
+ }
+ return SandboxedSdkCompat(result)
+ }
+
+ override fun beforeUnloadSdk() {
+ isBeforeUnloadSdkCalled = true
+ }
+
+ override fun getView(
+ windowContext: Context,
+ params: Bundle,
+ width: Int,
+ height: Int
+ ): View {
+ return View(windowContext)
+ }
+
+ internal class CurrentVersionSdkTest(
+ private val context: Context
+ ) : Binder() {
+ fun getSandboxedSdks(): List<SandboxedSdkCompat> =
+ SdkSandboxControllerCompat.from(context).getSandboxedSdks()
+
+ fun getAppOwnedSdkSandboxInterfaces(): List<AppOwnedSdkSandboxInterfaceCompat> =
+ SdkSandboxControllerCompat.from(context).getAppOwnedSdkSandboxInterfaces()
+
+ fun registerSdkSandboxActivityHandler(handler: SdkSandboxActivityHandlerCompat): IBinder =
+ SdkSandboxControllerCompat.from(context).registerSdkSandboxActivityHandler(handler)
+
+ fun unregisterSdkSandboxActivityHandler(handler: SdkSandboxActivityHandlerCompat) {
+ SdkSandboxControllerCompat.from(context).unregisterSdkSandboxActivityHandler(handler)
+ }
+ }
+}
diff --git a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewSmoothScrollToPositionTest.kt b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewSmoothScrollToPositionTest.kt
index 6a10b3d..c10f74d 100644
--- a/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewSmoothScrollToPositionTest.kt
+++ b/recyclerview/recyclerview/src/androidTest/java/androidx/recyclerview/widget/RecyclerViewSmoothScrollToPositionTest.kt
@@ -158,12 +158,19 @@
"onStop should be called eventually",
calledOnStop.await(30, TimeUnit.SECONDS)
)
- Assert.assertNotNull(
- "smoothScrollToPosition should succeed " +
- "(first visible item: " + layoutManager.findFirstVisibleItemPosition() +
- ", last visible item: " + layoutManager.findLastVisibleItemPosition() + ")",
- recyclerView.findViewHolderForLayoutPosition(targetPosition)
- )
+
+ // This needs to be run on the UI thread 1) due to inspecting the results of operations
+ // (such as layout) that may occur after the latch is counted down, and 2) in order to
+ // ensure that it doesn't run concurrently with operations on the UI thread that might
+ // affect the state.
+ mActivityTestRule.runOnUiThread {
+ Assert.assertNotNull(
+ "smoothScrollToPosition should succeed " +
+ "(first visible item: " + layoutManager.findFirstVisibleItemPosition() +
+ ", last visible item: " + layoutManager.findLastVisibleItemPosition() + ")",
+ recyclerView.findViewHolderForLayoutPosition(targetPosition)
+ )
+ }
}
private fun setup(
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/MusicDao.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/MusicDao.kt
index 5753602..426a4f9 100644
--- a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/MusicDao.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/MusicDao.kt
@@ -13,6 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
+@file:Suppress("DEPRECATION") // For @MapInfo
+
package androidx.room.integration.kotlintestapp.dao
import androidx.collection.ArrayMap
@@ -22,6 +25,7 @@
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
+import androidx.room.MapColumn
import androidx.room.MapInfo
import androidx.room.Query
import androidx.room.RawQuery
@@ -374,4 +378,42 @@
@Transaction
@Query("SELECT * FROM Playlist WHERE mPlaylistId = :id")
fun getPlaylistsWithSongsFlow(id: Int): Flow<PlaylistWithSongs>
+
+ @Query("SELECT * FROM Artist JOIN Song ON Artist.mArtistName = Song.mArtist")
+ @RewriteQueriesToDropUnusedColumns
+ fun artistNameToSongsMapColumn():
+ Map<@MapColumn(columnName = "mArtistName") String,
+ List<@MapColumn(columnName = "mReleasedYear") Int>>
+
+ @Query(
+ """
+ SELECT * FROM Image
+ LEFT JOIN Artist ON Image.mArtistInImage = Artist.mArtistName
+ LEFT JOIN Album ON Artist.mArtistName = Album.mAlbumArtist
+ LEFT JOIN Song ON Album.mAlbumName = Song.mAlbum
+ """
+ )
+ @RewriteQueriesToDropUnusedColumns
+ fun getImageYearToArtistToAlbumsToSongsMapColumn():
+ Map<@MapColumn(columnName = "mImageYear") Long, Map<Artist,
+ Map<@MapColumn(columnName = "mAlbumName") String, List<Song>>>>
+
+ @Query(
+ """
+ SELECT * FROM Image
+ LEFT JOIN Artist ON Image.mArtistInImage = Artist.mArtistName
+ LEFT JOIN Album ON Artist.mArtistName = Album.mAlbumArtist
+ LEFT JOIN Song ON Album.mAlbumName = Song.mAlbum
+ """
+ )
+ @RewriteQueriesToDropUnusedColumns
+ fun getImageYearToArtistToAlbumsToSongsMultiMapColumn():
+ Map<Image, Map<Artist, Map<@MapColumn(columnName = "mAlbumName") String,
+ List<@MapColumn(columnName = "mReleasedYear") Int>>>>
+
+ @RawQuery
+ @RewriteQueriesToDropUnusedColumns
+ fun getImageYearToArtistToAlbumsToSongsMultiMapColumn(query: SupportSQLiteQuery):
+ Map<Image, Map<Artist, Map<@MapColumn(columnName = "mAlbumName") String,
+ List<@MapColumn(columnName = "mReleasedYear") Int>>>>
}
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/AmbiguousColumnResolverTest.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/AmbiguousColumnResolverTest.kt
index 3647b62..b11a259 100644
--- a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/AmbiguousColumnResolverTest.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/AmbiguousColumnResolverTest.kt
@@ -24,7 +24,7 @@
import androidx.room.Embedded
import androidx.room.Entity
import androidx.room.Insert
-import androidx.room.MapInfo
+import androidx.room.MapColumn
import androidx.room.PrimaryKey
import androidx.room.Query
import androidx.room.Relation
@@ -103,7 +103,7 @@
}
@Test
- fun withMapInfo() {
+ fun withMapColumn() {
dao.getUserIdAndComments().let { userIdAndComments ->
assertThat(userIdAndComments[1]).containsExactly(comment1)
assertThat(userIdAndComments[2]).containsExactly(comment2, comment3)
@@ -198,32 +198,31 @@
// Suppress on CURSOR_MISMATCH is because @RewriteQueriesToDropUnusedColumns does not
// rewrite queries with duplicate columns.
@Suppress(RoomWarnings.CURSOR_MISMATCH, RoomWarnings.AMBIGUOUS_COLUMN_IN_RESULT)
- @MapInfo(keyColumn = "id", keyTable = "User")
@Query("SELECT * FROM User JOIN Comment ON User.id = Comment.userId")
- fun getUserIdAndComments(): Map<Int, List<Comment>>
+ fun getUserIdAndComments(): Map<@MapColumn("id") Int, List<Comment>>
// This works because User.id is in the projection first, but if swapped with Comment.*
// it would return bad results, hence the AMBIGUOUS_COLUMN_IN_RESULT.
@Suppress(RoomWarnings.AMBIGUOUS_COLUMN_IN_RESULT)
- @MapInfo(keyColumn = "id", keyTable = "User")
@Query("SELECT User.id, Comment.* FROM User JOIN Comment ON User.id = Comment.userId")
- fun getUserIdAndCommentsTableOrderSwapped(): Map<Int, List<Comment>>
+ fun getUserIdAndCommentsTableOrderSwapped():
+ Map<@MapColumn("id") Int, List<Comment>>
// Aliasing the single ambiguous column is good.
- @MapInfo(keyColumn = "user_id")
@Query("""
SELECT Comment.*, User.id as user_id
FROM User JOIN Comment ON User.id = Comment.userId
""")
- fun getUserIdAliasedAndCommentsTableOrderSwapped(): Map<Int, List<Comment>>
+ fun getUserIdAliasedAndCommentsTableOrderSwapped():
+ Map<@MapColumn("user_id")Int, List<Comment>>
- @MapInfo(keyColumn = "id", keyTable = "User", valueColumn = "commentsCount")
@Query("""
SELECT User.id, count(*) AS commentsCount
FROM User JOIN Comment ON User.id = Comment.userId
GROUP BY User.id
""")
- fun getUserIdAndAmountOfComments(): Map<Int, Int>
+ fun getUserIdAndAmountOfComments():
+ Map<@MapColumn("id") Int, @MapColumn("commentsCount") Int>
@Query("SELECT * FROM User LEFT JOIN Comment ON User.id = Comment.userId")
fun getLeftJoinUserCommentMap(): Map<User, List<Comment>>
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/MultimapQueryTest.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/MultimapQueryTest.kt
index c44486f..6310e19 100644
--- a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/MultimapQueryTest.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/MultimapQueryTest.kt
@@ -59,6 +59,9 @@
/**
* Tests multimap return type for JOIN statements.
+ *
+ * Deprecation has been suppressed for @MapInfo. We still need these tests, but the annotation
+ * is deprecated.
*/
@MediumTest
@RunWith(AndroidJUnit4::class)
@@ -1334,6 +1337,176 @@
).isEmpty()
}
+ @Test
+ fun testDoubleNestedMapWithMapColumnKeyLeftJoin() {
+ mMusicDao.addArtists(mRhcp, mAcDc, mPinkFloyd)
+ mMusicDao.addAlbums(
+ mStadiumArcadium,
+ mCalifornication,
+ mTheDarkSideOfTheMoon,
+ mHighwayToHell,
+ mDreamland
+ )
+ mMusicDao.addSongs(mRhcpSong1, mRhcpSong2, mAcdcSong1, mRhcpSong3)
+ mMusicDao.addImages(mPinkFloydAlbumCover, mRhcpAlbumCover, mTheClashAlbumCover)
+
+ val doubleNestedMap = mMusicDao.getImageYearToArtistToAlbumsToSongsMultiMapColumn()
+ val rhcpImageMap = doubleNestedMap.getValue(mRhcpAlbumCover)
+ val rhcpMap = rhcpImageMap.getValue(mRhcp)
+ val stadiumArcadiumList = rhcpMap[mStadiumArcadium.mAlbumName]
+ val californicationList = rhcpMap[mCalifornication.mAlbumName]
+
+ val stadiumArcadiumExpectedList = listOf(mRhcpSong1.mReleasedYear, mRhcpSong2.mReleasedYear)
+ val californicationExpectedList = listOf(mRhcpSong3.mReleasedYear)
+
+ assertThat(doubleNestedMap.keys).containsExactlyElementsIn(
+ listOf(
+ mPinkFloydAlbumCover,
+ mRhcpAlbumCover,
+ mTheClashAlbumCover
+ )
+ )
+ assertThat(rhcpImageMap.keys).containsExactly(mRhcp)
+ assertThat(rhcpMap.keys).containsExactlyElementsIn(
+ listOf(mCalifornication.mAlbumName, mStadiumArcadium.mAlbumName)
+ )
+ assertThat(stadiumArcadiumList).containsExactlyElementsIn(stadiumArcadiumExpectedList)
+ assertThat(californicationList).containsExactlyElementsIn(californicationExpectedList)
+
+ // LEFT JOIN Checks
+ assertThat(doubleNestedMap).containsKey(mTheClashAlbumCover)
+ assertThat(doubleNestedMap[mTheClashAlbumCover]).isEmpty()
+ assertThat(doubleNestedMap).containsKey(mPinkFloydAlbumCover)
+ assertThat(doubleNestedMap[mPinkFloydAlbumCover]).containsKey(mPinkFloyd)
+ assertThat(doubleNestedMap[mPinkFloydAlbumCover]!![mPinkFloyd])
+ .containsKey(mTheDarkSideOfTheMoon.mAlbumName)
+ assertThat(
+ doubleNestedMap[mPinkFloydAlbumCover]
+ !![mPinkFloyd]
+ !![mTheDarkSideOfTheMoon.mAlbumName]
+ ).isEmpty()
+ }
+
+ @Test
+ fun testDoubleNestedMapWithMapColumnKeyLeftJoinRawQuery() {
+ mMusicDao.addArtists(mRhcp, mAcDc, mPinkFloyd)
+ mMusicDao.addAlbums(
+ mStadiumArcadium,
+ mCalifornication,
+ mTheDarkSideOfTheMoon,
+ mHighwayToHell,
+ mDreamland
+ )
+ mMusicDao.addSongs(mRhcpSong1, mRhcpSong2, mAcdcSong1, mRhcpSong3)
+ mMusicDao.addImages(mPinkFloydAlbumCover, mRhcpAlbumCover, mTheClashAlbumCover)
+
+ val doubleNestedMap = mMusicDao.getImageYearToArtistToAlbumsToSongsMultiMapColumn(
+ SimpleSQLiteQuery(
+ """
+ SELECT * FROM Image
+ LEFT JOIN Artist ON Image.mArtistInImage = Artist.mArtistName
+ LEFT JOIN Album ON Artist.mArtistName = Album.mAlbumArtist
+ LEFT JOIN Song ON Album.mAlbumName = Song.mAlbum
+ """
+ )
+ )
+ val rhcpImageMap = doubleNestedMap.getValue(mRhcpAlbumCover)
+ val rhcpMap = rhcpImageMap.getValue(mRhcp)
+ val stadiumArcadiumList = rhcpMap[mStadiumArcadium.mAlbumName]
+ val californicationList = rhcpMap[mCalifornication.mAlbumName]
+
+ val stadiumArcadiumExpectedList = listOf(mRhcpSong1.mReleasedYear, mRhcpSong2.mReleasedYear)
+ val californicationExpectedList = listOf(mRhcpSong3.mReleasedYear)
+
+ assertThat(doubleNestedMap.keys).containsExactlyElementsIn(
+ listOf(
+ mPinkFloydAlbumCover,
+ mRhcpAlbumCover,
+ mTheClashAlbumCover
+ )
+ )
+ assertThat(rhcpImageMap.keys).containsExactly(mRhcp)
+ assertThat(rhcpMap.keys).containsExactlyElementsIn(
+ listOf(mCalifornication.mAlbumName, mStadiumArcadium.mAlbumName)
+ )
+ assertThat(stadiumArcadiumList).containsExactlyElementsIn(stadiumArcadiumExpectedList)
+ assertThat(californicationList).containsExactlyElementsIn(californicationExpectedList)
+
+ // LEFT JOIN Checks
+ assertThat(doubleNestedMap).containsKey(mTheClashAlbumCover)
+ assertThat(doubleNestedMap[mTheClashAlbumCover]).isEmpty()
+ assertThat(doubleNestedMap).containsKey(mPinkFloydAlbumCover)
+ assertThat(doubleNestedMap[mPinkFloydAlbumCover]).containsKey(mPinkFloyd)
+ assertThat(doubleNestedMap[mPinkFloydAlbumCover]!![mPinkFloyd])
+ .containsKey(mTheDarkSideOfTheMoon.mAlbumName)
+ assertThat(
+ doubleNestedMap[mPinkFloydAlbumCover]
+ !![mPinkFloyd]
+ !![mTheDarkSideOfTheMoon.mAlbumName]
+ ).isEmpty()
+ }
+
+ @Test
+ fun testStringToListOfSongsMapColumn() {
+ mMusicDao.addSongs(mRhcpSong1, mRhcpSong2, mAcdcSong1, mPinkFloydSong1)
+ mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd)
+ val artistNameToSongsMap: Map<String, List<Int>> = mMusicDao.artistNameToSongsMapColumn()
+ assertThat(artistNameToSongsMap.containsKey("Pink Floyd")).isTrue()
+ assertThat(artistNameToSongsMap["Red Hot Chili Peppers"]).containsExactlyElementsIn(
+ listOf(mRhcpSong1.mReleasedYear, mRhcpSong2.mReleasedYear)
+ )
+ }
+
+ @Test
+ fun testDoubleNestedMapWithOneMapColumn() {
+ mMusicDao.addArtists(mRhcp, mAcDc, mPinkFloyd)
+ mMusicDao.addAlbums(
+ mStadiumArcadium,
+ mCalifornication,
+ mTheDarkSideOfTheMoon,
+ mHighwayToHell,
+ mDreamland
+ )
+ mMusicDao.addSongs(mRhcpSong1, mRhcpSong2, mAcdcSong1, mRhcpSong3)
+ mMusicDao.addImages(mPinkFloydAlbumCover, mRhcpAlbumCover, mTheClashAlbumCover)
+
+ val doubleNestedMap = mMusicDao.getImageYearToArtistToAlbumsToSongsMapColumn()
+ val rhcpImageMap = doubleNestedMap.getValue(mRhcpAlbumCover.mImageYear)
+ val rhcpMap = rhcpImageMap.getValue(mRhcp)
+ val stadiumArcadiumList = rhcpMap.getValue("Stadium Arcadium")
+ val californicationList = rhcpMap.getValue("Californication")
+
+ val stadiumArcadiumExpectedList = listOf(mRhcpSong1, mRhcpSong2)
+ val californicationExpectedList = listOf(mRhcpSong3)
+
+ assertThat(doubleNestedMap.keys).containsExactlyElementsIn(
+ listOf(
+ mPinkFloydAlbumCover.mImageYear,
+ mRhcpAlbumCover.mImageYear,
+ mTheClashAlbumCover.mImageYear
+ )
+ )
+ assertThat(rhcpImageMap.keys).containsExactly(mRhcp)
+ assertThat(rhcpMap.keys).containsExactlyElementsIn(
+ listOf("Stadium Arcadium", "Californication")
+ )
+ assertThat(stadiumArcadiumList).containsExactlyElementsIn(stadiumArcadiumExpectedList)
+ assertThat(californicationList).containsExactlyElementsIn(californicationExpectedList)
+
+ // LEFT JOIN Checks
+ assertThat(doubleNestedMap).containsKey(mTheClashAlbumCover.mImageYear)
+ assertThat(doubleNestedMap[mTheClashAlbumCover.mImageYear]).isEmpty()
+ assertThat(doubleNestedMap).containsKey(mPinkFloydAlbumCover.mImageYear)
+ assertThat(doubleNestedMap[mPinkFloydAlbumCover.mImageYear]).containsKey(mPinkFloyd)
+ assertThat(doubleNestedMap[mPinkFloydAlbumCover.mImageYear]!![mPinkFloyd])
+ .containsKey(mTheDarkSideOfTheMoon.mAlbumName)
+ assertThat(
+ doubleNestedMap[mPinkFloydAlbumCover.mImageYear]
+ !![mPinkFloyd]
+ !![mTheDarkSideOfTheMoon.mAlbumName]
+ ).isEmpty()
+ }
+
/**
* Checks that the contents of the map are as expected.
*
diff --git a/room/integration-tests/kotlintestapp/src/androidTestWithKspGenKotlin/java/androidx/room/integration/kotlintestapp/test/ValueClassConverterWrapperTest.kt b/room/integration-tests/kotlintestapp/src/androidTestWithKspGenKotlin/java/androidx/room/integration/kotlintestapp/test/ValueClassConverterWrapperTest.kt
index 14344bb..f735c1e 100644
--- a/room/integration-tests/kotlintestapp/src/androidTestWithKspGenKotlin/java/androidx/room/integration/kotlintestapp/test/ValueClassConverterWrapperTest.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTestWithKspGenKotlin/java/androidx/room/integration/kotlintestapp/test/ValueClassConverterWrapperTest.kt
@@ -214,7 +214,8 @@
assertThrows<IllegalStateException> {
db.dao().insertNullableEntity(data)
}.hasMessageThat().isEqualTo(
- "Cannot bind nullable value of inline class to a NOT NULL column."
+ "Cannot bind NULLABLE value 'data' of inline class 'NullableValue' to " +
+ "a NOT NULL column."
)
}
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/dao/MusicDao.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/dao/MusicDao.java
index 6f74a64..2b344fe 100644
--- a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/dao/MusicDao.java
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/dao/MusicDao.java
@@ -22,6 +22,7 @@
import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Insert;
+import androidx.room.MapColumn;
import androidx.room.MapInfo;
import androidx.room.Query;
import androidx.room.RawQuery;
@@ -46,14 +47,14 @@
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSetMultimap;
+import io.reactivex.Flowable;
+
import java.nio.ByteBuffer;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import io.reactivex.Flowable;
-
@Dao
@SuppressWarnings("ROOM_EXPAND_PROJECTION_WITH_UNUSED_COLUMNS")
public interface MusicDao {
@@ -198,114 +199,142 @@
getAllArtistAndTheirSongsAsLiveDataGuavaImmutableListMultimap();
@RewriteQueriesToDropUnusedColumns
+ @SuppressWarnings("deprecation")
@MapInfo(keyColumn = "mArtistName")
@Query("SELECT * FROM Artist JOIN Song ON Artist.mArtistName = Song.mArtist")
Map<String, List<Song>> getArtistNameToSongs();
@RewriteQueriesToDropUnusedColumns
+ @Query("SELECT * FROM Artist JOIN Song ON Artist.mArtistName = Song.mArtist")
+ Map<@MapColumn(columnName = "mArtistName") String,
+ List<@MapColumn(columnName = "mReleasedYear") Integer>> getArtistNameToSongsMapColumn();
+
+ @RewriteQueriesToDropUnusedColumns
+ @SuppressWarnings("deprecation") // for MapInfo
@MapInfo(keyColumn = "mReleasedYear", valueColumn = "mReleasedYear")
@Query("SELECT * FROM Album JOIN Song ON Song.mReleasedYear = Album.mAlbumReleaseYear")
Map<Integer, List<Song>> getReleaseYearToAlbums();
@RewriteQueriesToDropUnusedColumns
+ @SuppressWarnings("deprecation") // for MapInfo
@MapInfo(keyColumn = "mImageYear")
@Query("SELECT * FROM Artist JOIN Image ON Artist.mArtistName = Image.mArtistInImage")
LongSparseArray<Artist> getAllAlbumCoverYearToArtistsWithLongSparseArray();
@RewriteQueriesToDropUnusedColumns
+ @SuppressWarnings("deprecation") // for MapInfo
@MapInfo(keyColumn = "mImageYear")
@Query("SELECT * FROM Artist JOIN Image ON Artist.mArtistName = Image.mArtistInImage")
SparseArrayCompat<Artist> getAllAlbumCoverYearToArtistsWithIntSparseArray();
@RewriteQueriesToDropUnusedColumns
+ @SuppressWarnings("deprecation") // for MapInfo
@MapInfo(keyColumn = "mReleasedYear", valueColumn = "mTitle")
@Query("SELECT * FROM Album JOIN Song ON Song.mReleasedYear = Album.mAlbumReleaseYear")
Map<Integer, List<String>> getReleaseYearToSongNames();
@RewriteQueriesToDropUnusedColumns
+ @SuppressWarnings("deprecation") // for MapInfo
@MapInfo(keyColumn = "mArtistName", valueColumn = "mArtist")
@RawQuery
Map<String, List<Song>> getArtistNameToSongsRawQuery(SupportSQLiteQuery query);
@RewriteQueriesToDropUnusedColumns
+ @SuppressWarnings("deprecation") // for MapInfo
@MapInfo(keyColumn = "mReleasedYear", valueColumn = "mReleasedYear")
@RawQuery
Map<Integer, List<Song>> getReleaseYearToAlbumsRawQuery(SupportSQLiteQuery query);
@RewriteQueriesToDropUnusedColumns
+ @SuppressWarnings("deprecation") // for MapInfo
@MapInfo(keyColumn = "mReleasedYear", valueColumn = "mTitle")
@RawQuery
Map<Integer, List<String>> getReleaseYearToSongNamesRawQuery(SupportSQLiteQuery query);
@RewriteQueriesToDropUnusedColumns
+ @SuppressWarnings("deprecation") // for MapInfo
@MapInfo(valueColumn = "songCount")
@Query("SELECT *, COUNT(mSongId) as songCount FROM Artist JOIN Song ON Artist.mArtistName = "
+ "Song.mArtist GROUP BY mArtistName")
Map<Artist, Integer> getArtistAndSongCountMap();
@RewriteQueriesToDropUnusedColumns
+ @SuppressWarnings("deprecation") // for MapInfo
@MapInfo(valueColumn = "songCount")
@RawQuery
Map<Artist, Integer> getArtistAndSongCountMapRawQuery(SupportSQLiteQuery query);
// Other Map Key/Value Types
@RewriteQueriesToDropUnusedColumns
+ @SuppressWarnings("deprecation") // for MapInfo
@MapInfo(valueColumn = "mAlbumCover")
@Query("SELECT * FROM Artist JOIN Image ON Artist.mArtistName = Image.mArtistInImage")
ImmutableMap<Artist, ByteBuffer> getAllArtistsWithAlbumCovers();
+ @SuppressWarnings("deprecation") // for MapInfo
@MapInfo(valueColumn = "mAlbumCover")
@RawQuery
ImmutableMap<Artist, ByteBuffer> getAllArtistsWithAlbumCoversRawQuery(SupportSQLiteQuery query);
@RewriteQueriesToDropUnusedColumns
+ @SuppressWarnings("deprecation") // for MapInfo
@MapInfo(valueColumn = "mImageYear")
@Query("SELECT * FROM Artist JOIN Image ON Artist.mArtistName = Image.mArtistInImage")
ImmutableMap<Artist, Long> getAllArtistsWithAlbumCoverYear();
@RewriteQueriesToDropUnusedColumns
+ @SuppressWarnings("deprecation") // for MapInfo
@MapInfo(valueColumn = "mImageYear")
@Query("SELECT * FROM Artist JOIN Image ON Artist.mArtistName = Image.mArtistInImage")
ArrayMap<Artist, Long> getAllArtistsWithAlbumCoverYearArrayMap();
+ @SuppressWarnings("deprecation") // for MapInfo
@MapInfo(keyColumn = "mImageYear")
@RawQuery
ImmutableMap<Long, Artist> getAllAlbumCoverYearToArtistsWithRawQuery(SupportSQLiteQuery query);
+ @SuppressWarnings("deprecation") // for MapInfo
@MapInfo(keyColumn = "mImageYear")
@RawQuery
ArrayMap<Long, Artist> getAllAlbumCoverYearToArtistsWithRawQueryArrayMap(
SupportSQLiteQuery query
);
+ @SuppressWarnings("deprecation") // for MapInfo
@MapInfo(keyColumn = "mAlbumCover", valueColumn = "mIsActive")
@Query("SELECT * FROM Image JOIN Artist ON Artist.mArtistName = Image.mArtistInImage")
ImmutableMap<ByteBuffer, Boolean> getAlbumCoversWithBandActivity();
+ @SuppressWarnings("deprecation") // for MapInfo
@MapInfo(keyColumn = "mAlbumCover", valueColumn = "mIsActive")
@RawQuery
ImmutableMap<ByteBuffer, Boolean> getAlbumCoversWithBandActivityRawQuery(
SupportSQLiteQuery query
);
+ @SuppressWarnings("deprecation") // for MapInfo
@MapInfo(keyColumn = "mDateReleased", valueColumn = "mIsActive")
@Query("SELECT * FROM Image JOIN Artist ON Artist.mArtistName = Image.mArtistInImage")
ImmutableMap<Date, Boolean> getAlbumDateWithBandActivity();
+ @SuppressWarnings("deprecation") // for MapInfo
@MapInfo(keyColumn = "mDateReleased", valueColumn = "mIsActive")
@RawQuery
ImmutableMap<Date, Boolean> getAlbumDateWithBandActivityRawQuery(SupportSQLiteQuery query);
+ @SuppressWarnings("deprecation") // for MapInfo
@MapInfo(keyColumn = "mFormat", valueColumn = "mIsActive")
@Query("SELECT * FROM Image JOIN Artist ON Artist.mArtistName = Image.mArtistInImage")
ImmutableMap<ImageFormat, Boolean> getImageFormatWithBandActivity();
+ @SuppressWarnings("deprecation") // for MapInfo
@MapInfo(keyColumn = "mFormat", valueColumn = "mIsActive")
@RawQuery
ImmutableMap<ImageFormat, Boolean> getImageFormatWithBandActivityRawQuery(
SupportSQLiteQuery query
);
+ @SuppressWarnings("deprecation") // for MapInfo
@MapInfo(keyColumn = "dog", valueColumn = "cat")
@RawQuery
Map<Artist, Integer> getMapWithInvalidColumnRawQuery(SupportSQLiteQuery query);
@@ -320,6 +349,7 @@
ImmutableListMultimap<Artist, Album> getArtistAndAlbumsLeftJoinGuava();
@RewriteQueriesToDropUnusedColumns
+ @SuppressWarnings("deprecation") // for MapInfo
@MapInfo(valueColumn = "mAlbumName")
@Query("SELECT * FROM Artist LEFT JOIN Album ON Artist.mArtistName = Album.mAlbumArtist")
Map<Artist, List<String>> getArtistAndAlbumNamesLeftJoin();
diff --git a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/MultimapQueryTest.java b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/MultimapQueryTest.java
index d045d02..6fd554c 100644
--- a/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/MultimapQueryTest.java
+++ b/room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/MultimapQueryTest.java
@@ -53,6 +53,8 @@
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
+import io.reactivex.Flowable;
+
import org.hamcrest.MatcherAssert;
import org.junit.Before;
import org.junit.Rule;
@@ -70,8 +72,6 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
-import io.reactivex.Flowable;
-
/**
* Tests multimap return type for JOIN statements.
*/
@@ -765,6 +765,18 @@
}
@Test
+ public void testStringToListOfSongsMapColumn() {
+ mMusicDao.addSongs(mRhcpSong1, mRhcpSong2, mAcdcSong1, mPinkFloydSong1);
+ mMusicDao.addArtists(mRhcp, mAcDc, mTheClash, mPinkFloyd);
+
+ Map<String, List<Integer>> artistNameToSongsMap = mMusicDao.getArtistNameToSongsMapColumn();
+ assertThat(artistNameToSongsMap.containsKey("Pink Floyd")).isTrue();
+ assertThat(artistNameToSongsMap.get("Red Hot Chili Peppers")).containsExactlyElementsIn(
+ Arrays.asList(mRhcpSong1.mReleasedYear, mRhcpSong2.mReleasedYear)
+ );
+ }
+
+ @Test
public void testIntegerToListOfAlbums() {
mMusicDao.addSongs(mRhcpSong1, mRhcpSong2, mAcdcSong1, mPinkFloydSong1);
mMusicDao.addAlbums(
diff --git a/room/room-common/api/current.txt b/room/room-common/api/current.txt
index 95d009b..612c15f 100644
--- a/room/room-common/api/current.txt
+++ b/room/room-common/api/current.txt
@@ -263,15 +263,22 @@
property public abstract kotlin.reflect.KClass<?> value;
}
- @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.FUNCTION) public @interface MapInfo {
- method public abstract String keyColumn() default "";
- method public abstract String keyTable() default "";
- method public abstract String valueColumn() default "";
- method public abstract String valueTable() default "";
- property public abstract String keyColumn;
- property public abstract String keyTable;
- property public abstract String valueColumn;
- property public abstract String valueTable;
+ @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.TYPE) public @interface MapColumn {
+ method public abstract String columnName();
+ method public abstract String tableName() default "";
+ property public abstract String columnName;
+ property public abstract String tableName;
+ }
+
+ @Deprecated @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.FUNCTION) public @interface MapInfo {
+ method @Deprecated public abstract String keyColumn() default "";
+ method @Deprecated public abstract String keyTable() default "";
+ method @Deprecated public abstract String valueColumn() default "";
+ method @Deprecated public abstract String valueTable() default "";
+ property @Deprecated public abstract String keyColumn;
+ property @Deprecated public abstract String keyTable;
+ property @Deprecated public abstract String valueColumn;
+ property @Deprecated public abstract String valueTable;
}
@IntDef({androidx.room.OnConflictStrategy.Companion.NONE, androidx.room.OnConflictStrategy.Companion.REPLACE, androidx.room.OnConflictStrategy.Companion.ROLLBACK, androidx.room.OnConflictStrategy.Companion.ABORT, androidx.room.OnConflictStrategy.Companion.FAIL, androidx.room.OnConflictStrategy.Companion.IGNORE}) @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface OnConflictStrategy {
diff --git a/room/room-common/api/restricted_current.txt b/room/room-common/api/restricted_current.txt
index 85cc4ca..474272c 100644
--- a/room/room-common/api/restricted_current.txt
+++ b/room/room-common/api/restricted_current.txt
@@ -263,15 +263,22 @@
property public abstract kotlin.reflect.KClass<?> value;
}
- @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.FUNCTION) public @interface MapInfo {
- method public abstract String keyColumn() default "";
- method public abstract String keyTable() default "";
- method public abstract String valueColumn() default "";
- method public abstract String valueTable() default "";
- property public abstract String keyColumn;
- property public abstract String keyTable;
- property public abstract String valueColumn;
- property public abstract String valueTable;
+ @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.TYPE) public @interface MapColumn {
+ method public abstract String columnName();
+ method public abstract String tableName() default "";
+ property public abstract String columnName;
+ property public abstract String tableName;
+ }
+
+ @Deprecated @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.FUNCTION) public @interface MapInfo {
+ method @Deprecated public abstract String keyColumn() default "";
+ method @Deprecated public abstract String keyTable() default "";
+ method @Deprecated public abstract String valueColumn() default "";
+ method @Deprecated public abstract String valueTable() default "";
+ property @Deprecated public abstract String keyColumn;
+ property @Deprecated public abstract String keyTable;
+ property @Deprecated public abstract String valueColumn;
+ property @Deprecated public abstract String valueTable;
}
@IntDef({androidx.room.OnConflictStrategy.Companion.NONE, androidx.room.OnConflictStrategy.Companion.REPLACE, androidx.room.OnConflictStrategy.Companion.ROLLBACK, androidx.room.OnConflictStrategy.Companion.ABORT, androidx.room.OnConflictStrategy.Companion.FAIL, androidx.room.OnConflictStrategy.Companion.IGNORE}) @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface OnConflictStrategy {
diff --git a/room/room-common/src/main/java/androidx/room/MapColumn.kt b/room/room-common/src/main/java/androidx/room/MapColumn.kt
new file mode 100644
index 0000000..423c6c4
--- /dev/null
+++ b/room/room-common/src/main/java/androidx/room/MapColumn.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room
+
+/**
+ * Declares which column is used to build a map or multimap return value in a [Dao]
+ * query method.
+ *
+ * This annotation is required when the key or value of a Map (or nested map) is a single column of
+ * one of the built in types (primitives, boxed primitives, enum, String, byte[], ByteBuffer) or
+ * a type with a converter (e.g. Date, UUID, etc).
+ *
+ * The use of this annotation provides clarity on which column should be used in retrieving
+ * information required by the return type.
+ *
+ * Example:
+ *
+ * ```
+ * @Query("SELECT * FROM Artist JOIN Song ON Artist.artistName = Song.artist")
+ * fun getArtistNameToSongNames():
+ * Map<@MapColumn(columnName = "artistName") String,
+ * @MapColumn(columnName = "songName") List<String>>
+ *
+ * @Query("SELECT *, COUNT(mSongId) as songCount FROM Artist JOIN Song ON
+ * Artist.artistName = Song.artist GROUP BY artistName")
+ * fun getArtistAndSongCounts(): Map<Artist, @MapColumn(columnName = "songCount") Integer>
+ * ```
+ *
+ * Column(s) specified in the provided @MapColumn annotation must be present in the query result.
+ */
+@Target(AnnotationTarget.TYPE)
+@Retention(AnnotationRetention.BINARY)
+public annotation class MapColumn(
+ /**
+ * The name of the column to be used for the map's key or value.
+ *
+ * @return The column name.
+ */
+ val columnName: String,
+
+ /**
+ * The name of the table or alias to be used for the map's column.
+ *
+ * Providing this value is optional. Useful for disambiguating between duplicate column names.
+ * For example, consider the following query:
+ * `SELECT * FROM Artist AS a JOIN Song AS s ON a.id == s.artistId`, then the `@MapColumn`
+ * for a return type `Map<String, List<Song>>` would be
+ * `Map<@MapColumn(columnName = "id", tableName = "a") String, List<Song>>`.
+ *
+ * @return The column table name.
+ */
+ val tableName: String = "",
+)
diff --git a/room/room-common/src/main/java/androidx/room/MapInfo.kt b/room/room-common/src/main/java/androidx/room/MapInfo.kt
index 526f949..93a0aeb 100644
--- a/room/room-common/src/main/java/androidx/room/MapInfo.kt
+++ b/room/room-common/src/main/java/androidx/room/MapInfo.kt
@@ -17,7 +17,7 @@
package androidx.room
/**
- * Declares which column(s) are used to build a map or multimap return value in a {@link Dao}
+ * Declares which column(s) are used to build a map or multimap return value in a [Dao]
* query method.
*
* This annotation is required when the key or value of the Map is a single column of one of the
@@ -46,6 +46,7 @@
*/
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.BINARY)
+@Deprecated("Use @MapColumn instead.")
public annotation class MapInfo(
/**
* The name of the column to be used for the map's keys.
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XAnnotated.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XAnnotated.kt
index ca01837..f72cec5 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XAnnotated.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/XAnnotated.kt
@@ -16,6 +16,7 @@
package androidx.room.compiler.processing
+import androidx.room.compiler.codegen.XClassName
import com.squareup.javapoet.ClassName
import kotlin.reflect.KClass
@@ -39,6 +40,21 @@
}
/**
+ * Returns the list of [XAnnotation] elements that have the same qualified name as the given
+ * [annotationName]. Otherwise, returns an empty list.
+ *
+ * For repeated annotations declared in Java code, please use the repeated annotation type,
+ * not the container. Calling this method with a container annotation will have inconsistent
+ * behaviour between Java AP and KSP.
+ *
+ * @see [hasAnnotation]
+ * @see [hasAnnotationWithPackage]
+ */
+ fun getAnnotations(annotationName: XClassName): List<XAnnotation> {
+ return getAllAnnotations().filter { annotationName.canonicalName == it.qualifiedName }
+ }
+
+ /**
* Gets the list of annotations with the given type.
*
* For repeated annotations declared in Java code, please use the repeated annotation type,
@@ -89,6 +105,16 @@
}
/**
+ * Returns `true` if this element is annotated with an [XAnnotation] that has the same
+ * qualified name as the given [annotationName].
+ *
+ * @see [hasAnyAnnotation]
+ */
+ fun hasAnnotation(annotationName: XClassName): Boolean {
+ return getAnnotations(annotationName).isNotEmpty()
+ }
+
+ /**
* Returns `true` if this element has an annotation that is declared in the given package.
* Alternatively, all annotations can be accessed with [getAllAnnotations].
*/
@@ -160,6 +186,18 @@
}
/**
+ * Returns the [XAnnotation] that has the same qualified name as [annotationName].
+ * Otherwise, `null` value is returned.
+ *
+ * @see [hasAnnotation]
+ * @see [getAnnotations]
+ * @see [hasAnnotationWithPackage]
+ */
+ fun getAnnotation(annotationName: XClassName): XAnnotation? {
+ return getAnnotations(annotationName).firstOrNull()
+ }
+
+ /**
* Returns the [Annotation]s that are annotated with [annotationName]
*/
fun getAnnotationsAnnotatedWith(
@@ -171,6 +209,17 @@
}
/**
+ * Returns the [Annotation]s that are annotated with [annotationName]
+ */
+ fun getAnnotationsAnnotatedWith(
+ annotationName: XClassName
+ ): Set<XAnnotation> {
+ return getAllAnnotations().filter {
+ it.type.typeElement?.hasAnnotation(annotationName) ?: false
+ }.toSet()
+ }
+
+ /**
* Returns the [XAnnotation] that has the same qualified name as [annotationName].
*
* @see [hasAnnotation]
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacConstructorElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacConstructorElement.kt
index 0d041a0..e3e5a57 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacConstructorElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacConstructorElement.kt
@@ -83,6 +83,7 @@
}
override val kotlinMetadata: KmConstructorContainer? by lazy {
- (enclosingElement as? JavacTypeElement)?.kotlinMetadata?.getConstructorMetadata(element)
+ (enclosingElement as? JavacTypeElement)?.kotlinMetadata
+ ?.getConstructorMetadata(element)
}
}
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacExecutableElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacExecutableElement.kt
index cdf34b6..356b357 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacExecutableElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacExecutableElement.kt
@@ -29,7 +29,7 @@
abstract override val kotlinMetadata: KmFunctionContainer?
override val jvmDescriptor by lazy {
- element.descriptor()
+ element.descriptor(env.delegate)
}
abstract override val parameters: List<JavacMethodParameter>
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacFieldElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacFieldElement.kt
index ca1d5da..a0d9c1c 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacFieldElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacFieldElement.kt
@@ -28,6 +28,10 @@
env: JavacProcessingEnv,
element: VariableElement
) : JavacVariableElement(env, element), XFieldElement {
+
+ override val name: String
+ get() = (kotlinMetadata?.name ?: super.name)
+
override fun getAllAnnotations(): List<XAnnotation> {
return buildList {
addAll(super.getAllAnnotations())
@@ -48,7 +52,7 @@
}
override val kotlinMetadata: KmPropertyContainer? by lazy {
- (enclosingElement as? JavacTypeElement)?.kotlinMetadata?.getPropertyMetadata(name)
+ (enclosingElement as? JavacTypeElement)?.kotlinMetadata?.getPropertyMetadata(element)
}
private val syntheticMethodForAnnotations: JavacMethodElement? by lazy {
@@ -68,7 +72,7 @@
get() = enclosingElement
override val jvmDescriptor: String
- get() = element.descriptor()
+ get() = element.descriptor(env.delegate)
override val getter: XMethodElement? by lazy {
kotlinMetadata?.getter?.let { getterMetadata ->
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacMethodParameter.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacMethodParameter.kt
index 8c78886..66c3ea0 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacMethodParameter.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacMethodParameter.kt
@@ -51,9 +51,14 @@
override val kotlinMetadata by lazy { kotlinMetadataFactory() }
override val name: String
- get() = (kotlinMetadata?.name ?: super.name).sanitizeAsJavaParameterName(
- argIndex = argIndex
- )
+ get() = if (isReceiverParam() && enclosingElement.isAbstract()) {
+ // Receiver parameter names for abstract methods are not reliable across different
+ // versions of KAPT so we just build the name ourselves to match KSP.
+ // https://youtrack.jetbrains.com/issue/KT-18048/kapt-drops-method-parameter-names
+ "\$this\$${enclosingElement.name}"
+ } else {
+ (kotlinMetadata?.name ?: super.name)
+ }.sanitizeAsJavaParameterName(argIndex)
override val kotlinType: KmTypeContainer?
get() = kotlinMetadata?.type
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacTypeElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacTypeElement.kt
index 5d3d6e4..3e30e02 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacTypeElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/JavacTypeElement.kt
@@ -108,6 +108,8 @@
element = it,
)
}
+ // To be consistent with KSP consider delegates to not have a backing field.
+ .filterNot { it.kotlinMetadata?.isDelegated() == true }
}
private val allMethods = MemoizedSequence {
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/kotlin/JvmDescriptorUtils.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/kotlin/JvmDescriptorUtils.kt
index 402ab9e..7695ee9 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/kotlin/JvmDescriptorUtils.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/kotlin/JvmDescriptorUtils.kt
@@ -19,9 +19,9 @@
import com.squareup.javapoet.ArrayTypeName
import com.squareup.javapoet.ClassName
import com.squareup.javapoet.TypeName
+import javax.annotation.processing.ProcessingEnvironment
import javax.lang.model.element.Element
import javax.lang.model.element.ExecutableElement
-import javax.lang.model.element.NestingKind
import javax.lang.model.element.QualifiedNameable
import javax.lang.model.element.TypeElement
import javax.lang.model.element.VariableElement
@@ -45,16 +45,19 @@
*
* For reference, see the [JVM specification, section 4.3.2](https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3.2)
*/
-internal fun VariableElement.descriptor() = "$simpleName:${asType().descriptor()}"
+internal fun VariableElement.descriptor(env: ProcessingEnvironment) =
+ "$simpleName:${asType().descriptor(env)}"
/**
* Returns the method descriptor of this [ExecutableElement].
*
* For reference, see the [JVM specification, section 4.3.3](https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3.3)
*/
-internal fun ExecutableElement.descriptor() = "$simpleName${asType().descriptor()}"
+internal fun ExecutableElement.descriptor(env: ProcessingEnvironment) =
+ "$simpleName${asType().descriptor(env)}"
-private fun TypeMirror.descriptor() = JvmDescriptorTypeVisitor.visit(this)
+private fun TypeMirror.descriptor(env: ProcessingEnvironment) =
+ JvmDescriptorTypeVisitor.visit(this, env)
// see https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3.2-200
internal fun String.typeNameFromJvmSignature(): TypeName {
@@ -110,13 +113,14 @@
*
* For reference, see the [JVM specification, section 4.3](http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3).
*/
-private object JvmDescriptorTypeVisitor : AbstractTypeVisitor8<String, Any?>() {
+private object JvmDescriptorTypeVisitor : AbstractTypeVisitor8<String, ProcessingEnvironment>() {
- override fun visitNoType(t: NoType, u: Any?): String = "V"
+ override fun visitNoType(t: NoType, env: ProcessingEnvironment): String = "V"
- override fun visitDeclared(t: DeclaredType, u: Any?): String = "L${t.asElement().internalName};"
+ override fun visitDeclared(t: DeclaredType, env: ProcessingEnvironment): String =
+ "L${t.asElement().internalName(env)};"
- override fun visitPrimitive(t: PrimitiveType, u: Any?): String {
+ override fun visitPrimitive(t: PrimitiveType, env: ProcessingEnvironment): String {
return when (t.kind) {
TypeKind.BYTE -> "B"
TypeKind.CHAR -> "C"
@@ -130,65 +134,46 @@
}
}
- override fun visitArray(t: ArrayType, u: Any?): String = "[" + visit(t.componentType)
+ override fun visitArray(t: ArrayType, env: ProcessingEnvironment): String =
+ "[" + visit(t.componentType, env)
- override fun visitWildcard(t: WildcardType, u: Any?): String = visitUnknown(t, u)
+ override fun visitWildcard(t: WildcardType, env: ProcessingEnvironment): String =
+ visitUnknown(t, env)
- override fun visitExecutable(t: ExecutableType, u: Any?): String {
- val parameterDescriptors = t.parameterTypes.joinToString("") { visit(it) }
- val returnDescriptor = visit(t.returnType)
+ override fun visitExecutable(t: ExecutableType, env: ProcessingEnvironment): String {
+ val parameterDescriptors = t.parameterTypes.joinToString("") { visit(it, env) }
+ val returnDescriptor = visit(t.returnType, env)
return "($parameterDescriptors)$returnDescriptor"
}
- override fun visitTypeVariable(t: TypeVariable, u: Any?): String = visit(t.upperBound)
+ override fun visitTypeVariable(t: TypeVariable, env: ProcessingEnvironment): String =
+ visit(t.upperBound, env)
- override fun visitNull(t: NullType, u: Any?): String = visitUnknown(t, u)
+ override fun visitNull(t: NullType, env: ProcessingEnvironment): String = visitUnknown(t, env)
- override fun visitError(t: ErrorType, u: Any?): String = visitDeclared(t, u)
+ override fun visitError(t: ErrorType, env: ProcessingEnvironment): String =
+ visitDeclared(t, env)
// For a type variable with multiple bounds: "the erasure of a type variable is determined
// by the first type in its bound" - JLS Sec 4.4
// See https://docs.oracle.com/javase/specs/jls/se16/html/jls-4.html#jls-4.4
- override fun visitIntersection(t: IntersectionType, u: Any?): String = visit(t.bounds[0])
+ override fun visitIntersection(t: IntersectionType, env: ProcessingEnvironment): String =
+ visit(t.bounds[0], env)
- override fun visitUnion(t: UnionType, u: Any?): String = visitUnknown(t, u)
+ override fun visitUnion(t: UnionType, env: ProcessingEnvironment): String = visitUnknown(t, env)
- override fun visitUnknown(t: TypeMirror, u: Any?): String = error("Unsupported type $t")
+ override fun visitUnknown(t: TypeMirror, env: ProcessingEnvironment): String =
+ error("Unsupported type $t")
/**
* Returns the name of this [TypeElement] in its "internal form".
*
* For reference, see the [JVM specification, section 4.2](https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.2).
*/
- private val Element.internalName: String
- get() = when (this) {
- is TypeElement ->
- when (nestingKind) {
- NestingKind.TOP_LEVEL ->
- qualifiedName.toString().replace('.', '/')
- NestingKind.MEMBER, NestingKind.LOCAL ->
- enclosingElement.internalName + "$" + simpleName
- NestingKind.ANONYMOUS ->
- elementError("Unsupported nesting $nestingKind", this)
- else ->
- elementError("Unsupported, nestingKind == null", this)
- }
- is ExecutableElement -> enclosingElement.internalName
- is QualifiedNameable -> qualifiedName.toString().replace('.', '/')
- else -> simpleName.toString()
- }
-
- /**
- * Throws an exception with the error [msg] and the [element] and its enclosing elements appended.
- */
- private fun elementError(msg: String, element: Element): Nothing {
- fun buildName(element: Element): String {
- val enclosingPart =
- element.enclosingElement?.let { buildName(it) + "." } ?: ""
- val simpleName = element.simpleName.ifEmpty { "<unnamed>" }
- return enclosingPart + simpleName
- }
- val name = buildName(element)
- error("$msg - On element $name")
+ private fun Element.internalName(env: ProcessingEnvironment): String = when (this) {
+ is TypeElement -> env.elementUtils.getBinaryName(this).toString().replace('.', '/')
+ is ExecutableElement -> enclosingElement.internalName(env)
+ is QualifiedNameable -> qualifiedName.toString().replace('.', '/')
+ else -> simpleName.toString()
}
}
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/kotlin/KotlinClassMetadataUtils.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/kotlin/KotlinClassMetadataUtils.kt
index 7d93ec0..8c9d94b 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/kotlin/KotlinClassMetadataUtils.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/javac/kotlin/KotlinClassMetadataUtils.kt
@@ -27,6 +27,7 @@
import javax.lang.model.element.Element
import javax.lang.model.element.ElementKind
import javax.lang.model.element.ExecutableElement
+import javax.lang.model.element.VariableElement
import javax.tools.Diagnostic
import kotlinx.metadata.Flag
import kotlinx.metadata.Flags
@@ -42,6 +43,7 @@
import kotlinx.metadata.KmValueParameter
import kotlinx.metadata.jvm.KotlinClassMetadata
import kotlinx.metadata.jvm.annotations
+import kotlinx.metadata.jvm.fieldSignature
import kotlinx.metadata.jvm.getterSignature
import kotlinx.metadata.jvm.setterSignature
import kotlinx.metadata.jvm.signature
@@ -52,6 +54,7 @@
}
internal class KmClassContainer(
+ private val env: JavacProcessingEnv,
private val kmClass: KmClass
) : KmFlags {
override val flags: Flags
@@ -116,7 +119,7 @@
check(method.kind == ElementKind.METHOD) {
"must pass an element type of method"
}
- return functionByDescriptor[method.descriptor()]
+ return functionByDescriptor[method.descriptor(env.delegate)]
}
private val functionByDescriptor: Map<String, KmFunctionContainer> by lazy {
@@ -136,12 +139,17 @@
check(method.kind == ElementKind.CONSTRUCTOR) {
"must pass an element type of constructor"
}
- val methodSignature = method.descriptor()
+ val methodSignature = method.descriptor(env.delegate)
return constructorList.firstOrNull { it.descriptor == methodSignature }
}
- fun getPropertyMetadata(propertyName: String): KmPropertyContainer? =
- propertyList.firstOrNull { it.name == propertyName }
+ fun getPropertyMetadata(field: VariableElement): KmPropertyContainer? {
+ check(field.kind == ElementKind.FIELD) {
+ "must pass an element type of field"
+ }
+ val fieldName = field.simpleName.toString()
+ return propertyList.firstOrNull { it.backingFieldName == fieldName || it.name == fieldName }
+ }
companion object {
/**
@@ -162,7 +170,7 @@
)
}
return when (classMetadata) {
- is KotlinClassMetadata.Class -> KmClassContainer(classMetadata.toKmClass())
+ is KotlinClassMetadata.Class -> KmClassContainer(env, classMetadata.toKmClass())
// Synthetic classes generated for various Kotlin features ($DefaultImpls,
// $WhenMappings, etc) are ignored because the data contained does not affect
// the metadata derived APIs. These classes are never referenced by user code but
@@ -268,6 +276,7 @@
internal class KmPropertyContainer(
private val kmProperty: KmProperty,
val type: KmTypeContainer,
+ val backingFieldName: String?,
val getter: KmFunctionContainer?,
val setter: KmFunctionContainer?,
val syntheticMethodForAnnotations: KmFunctionContainer?,
@@ -279,6 +288,7 @@
val typeParameters: List<KmTypeContainer>
get() = type.typeArguments
fun isNullable() = type.isNullable()
+ fun isDelegated() = Flag.Property.IS_DELEGATED(flags)
}
internal class KmTypeContainer(
@@ -408,6 +418,7 @@
KmPropertyContainer(
kmProperty = this,
type = this.returnType.asContainer(),
+ backingFieldName = fieldSignature?.name,
getter = getterSignature?.let {
KmPropertyFunctionContainerImpl(
flags = this.getterFlags,
diff --git a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt
index 354ee85..44bb15d 100644
--- a/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt
+++ b/room/room-compiler-processing/src/main/java/androidx/room/compiler/processing/ksp/KspTypeElement.kt
@@ -174,7 +174,7 @@
private val _declaredFields by lazy {
_declaredProperties.filter {
- it.declaration.hasBackingFieldFixed
+ it.declaration.hasBackingField
}
}
@@ -410,13 +410,6 @@
}
}
- /**
- * Workaround for https://github.com/google/ksp/issues/529 where KSP returns false for
- * backing field when the property has a lateinit modifier.
- */
- private val KSPropertyDeclaration.hasBackingFieldFixed
- get() = hasBackingField || modifiers.contains(Modifier.LATEINIT)
-
@OptIn(KspExperimental::class)
fun KSDeclarationContainer?.getDeclarationsInSourceOrder() = this?.let {
env.resolver.getDeclarationsInSourceOrder(it)
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt
index db2aa22..181265e6 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XExecutableElementTest.kt
@@ -1429,4 +1429,52 @@
)
}
}
+
+ @Test
+ fun receiverParameterNames(@TestParameter isPrecompiled: Boolean) {
+ val kotlinSource = Source.kotlin(
+ "foo.bar.Subject.kt",
+ """
+ package foo.bar
+ abstract class Subject {
+ fun Bar.method(): Unit = TODO()
+ fun Bar.methodWithParam(name: String): Unit = TODO()
+ abstract fun Bar.abstractMethod()
+ abstract fun Bar.abstractMethodWithParam(name: String)
+ @JvmName("newMethodWithJvmName")
+ fun Bar.methodWithJvmName(): Unit = TODO()
+ @JvmName("newMethodWithJvmNameAndParam")
+ fun Bar.methodWithJvmNameAndParam(name: String): Unit = TODO()
+ }
+ class Bar
+ """.trimIndent())
+ runProcessorTest(
+ sources = if (isPrecompiled) {
+ emptyList()
+ } else {
+ listOf(kotlinSource)
+ },
+ classpath = if (isPrecompiled) {
+ compileFiles(listOf(kotlinSource))
+ } else {
+ emptyList()
+ }
+ ) { invocation ->
+ val subject = invocation.processingEnv.requireTypeElement("foo.bar.Subject")
+
+ // Assert on the method and parameter names of each declared method.
+ assertThat(
+ subject.getDeclaredMethods().map { method ->
+ "${method.name}(${method.parameters.joinToString(", ") { it.name }})"
+ }
+ ).containsExactly(
+ "method(\$this\$method)",
+ "methodWithParam(\$this\$methodWithParam, name)",
+ "abstractMethod(\$this\$abstractMethod)",
+ "abstractMethodWithParam(\$this\$abstractMethodWithParam, name)",
+ "methodWithJvmName(\$this\$methodWithJvmName)",
+ "methodWithJvmNameAndParam(\$this\$methodWithJvmNameAndParam, name)",
+ ).inOrder()
+ }
+ }
}
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt
index 0d3d952..5b165ae 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/XTypeElementTest.kt
@@ -29,6 +29,7 @@
import androidx.room.compiler.processing.util.compileFiles
import androidx.room.compiler.processing.util.getAllFieldNames
import androidx.room.compiler.processing.util.getDeclaredField
+import androidx.room.compiler.processing.util.getDeclaredMethodByJvmName
import androidx.room.compiler.processing.util.getField
import androidx.room.compiler.processing.util.getMethodByJvmName
import androidx.room.compiler.processing.util.runProcessorTest
@@ -822,6 +823,26 @@
}
@Test
+ fun lazyProperty() {
+ val src = Source.kotlin(
+ "Subject.kt",
+ """
+ class Subject {
+ val myLazy by lazy { "wow" }
+ }
+ """.trimIndent()
+ )
+ runTest(sources = listOf(src)) {
+ val subject = it.processingEnv.requireTypeElement("Subject")
+ val fields = subject.getDeclaredFields()
+ assertThat(fields).isEmpty()
+ val method = subject.getDeclaredMethodByJvmName("getMyLazy")
+ assertThat(method).isNotNull()
+ assertThat(method.isKotlinPropertyMethod()).isTrue()
+ }
+ }
+
+ @Test
fun declaredAndInstanceMethods() {
val src = Source.kotlin(
"Foo.kt",
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/JvmDescriptorUtilsTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/JvmDescriptorUtilsTest.kt
index 86d3822..90f1bac 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/JvmDescriptorUtilsTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/JvmDescriptorUtilsTest.kt
@@ -425,9 +425,9 @@
roundEnv.getElementsAnnotatedWith(annotations.first()).map { element ->
when (element.kind) {
FIELD ->
- MoreElements.asVariable(element).descriptor()
+ MoreElements.asVariable(element).descriptor(processingEnv)
METHOD, CONSTRUCTOR ->
- MoreElements.asExecutable(element).descriptor()
+ MoreElements.asExecutable(element).descriptor(processingEnv)
else -> error("Unsupported element to describe.")
}
}.toSet().let(handler)
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/KotlinMetadataElementTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/KotlinMetadataElementTest.kt
index eb676b6..26b9bab 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/KotlinMetadataElementTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/javac/kotlin/KotlinMetadataElementTest.kt
@@ -19,7 +19,6 @@
import androidx.kruth.assertThat
import androidx.kruth.assertWithMessage
import androidx.room.compiler.processing.XNullability
-import androidx.room.compiler.processing.XProcessingEnvConfig
import androidx.room.compiler.processing.javac.JavacProcessingEnv
import androidx.room.compiler.processing.util.Source
import androidx.room.compiler.processing.util.XTestInvocation
@@ -27,7 +26,6 @@
import androidx.room.compiler.processing.util.runJavaProcessorTest
import androidx.room.compiler.processing.util.runKaptTest
import androidx.room.compiler.processing.util.sanitizeAsJavaParameterName
-import javax.annotation.processing.ProcessingEnvironment
import javax.lang.model.element.ExecutableElement
import javax.lang.model.element.TypeElement
import javax.lang.model.util.ElementFilter
@@ -55,9 +53,9 @@
}
""".trimIndent()
)
- simpleRun(listOf(src)) { processingEnv ->
+ simpleRun(listOf(src)) { env ->
val (testClassElement, metadataElement) = getMetadataElement(
- processingEnv,
+ env,
"Subject"
)
val constructors = testClassElement.getConstructors()
@@ -109,9 +107,9 @@
}
""".trimIndent()
)
- simpleRun(listOf(src)) { processingEnv ->
+ simpleRun(listOf(src)) { env ->
val (testClassElement, metadataElement) = getMetadataElement(
- processingEnv,
+ env,
"Subject"
)
testClassElement.getDeclaredMethod("functionWithParams")
@@ -157,14 +155,14 @@
}
""".trimIndent()
)
- simpleRun(listOf(src)) { invocation ->
+ simpleRun(listOf(src)) { env ->
val (testClassElement, metadataElement) = getMetadataElement(
- invocation,
+ env,
"Subject"
)
assertThat(
testClassElement.getConstructors().map {
- val desc = it.descriptor()
+ val desc = it.descriptor(env.delegate)
desc to (desc == metadataElement.primaryConstructorSignature)
}
).containsExactly(
@@ -191,14 +189,15 @@
}
""".trimIndent()
)
- simpleRun(listOf(src)) { invocation ->
+ simpleRun(listOf(src)) { env ->
val (testClassElement, metadataElement) = getMetadataElement(
- invocation,
+ env,
"Subject"
)
assertThat(
testClassElement.getDeclaredMethods().map {
- it.simpleName.toString() to metadataElement.getFunctionMetadata(it)?.isSuspend()
+ it.simpleName.toString() to metadataElement.getFunctionMetadata(it)
+ ?.isSuspend()
}
).containsExactly(
"emptyFunction" to false,
@@ -220,12 +219,12 @@
object AnObject
""".trimIndent()
)
- simpleRun(listOf(src)) { invocation ->
- val (_, objectTypeMetadata) = getMetadataElement(invocation, "AnObject")
+ simpleRun(listOf(src)) { env ->
+ val (_, objectTypeMetadata) = getMetadataElement(env, "AnObject")
assertThat(objectTypeMetadata.isObject()).isTrue()
- val (_, classTypeMetadata) = getMetadataElement(invocation, "KotlinClass")
+ val (_, classTypeMetadata) = getMetadataElement(env, "KotlinClass")
assertThat(classTypeMetadata.isObject()).isFalse()
- val (_, interfaceMetadata) = getMetadataElement(invocation, "KotlinInterface")
+ val (_, interfaceMetadata) = getMetadataElement(env, "KotlinInterface")
assertThat(interfaceMetadata.isObject()).isFalse()
}
}
@@ -241,9 +240,9 @@
}
""".trimIndent()
)
- simpleRun(listOf(src)) { invocation ->
+ simpleRun(listOf(src)) { env ->
val (testDaoElement, testDaoMetadata) = getMetadataElement(
- invocation,
+ env,
"Subject"
)
val nonNullListMethod = testDaoElement.getDeclaredMethod("nonNullList")
@@ -273,38 +272,48 @@
}
""".trimIndent()
)
- simpleRun(listOf(src)) { invocation ->
- val (_, testMetadata) = getMetadataElement(
- invocation,
+ simpleRun(listOf(src)) { env ->
+ val (typeElement, testMetadata) = getMetadataElement(
+ env,
"Properties"
)
- testMetadata.getPropertyMetadata("nonNull").let { property ->
+ testMetadata.getPropertyMetadata(
+ typeElement.getDeclaredField("nonNull")
+ ).let { property ->
assertThat(property?.name).isEqualTo("nonNull")
assertThat(property?.typeParameters).isEmpty()
assertThat(property?.isNullable()).isFalse()
}
- testMetadata.getPropertyMetadata("nullable").let { property ->
+ testMetadata.getPropertyMetadata(
+ typeElement.getDeclaredField("nullable")
+ ).let { property ->
assertThat(property?.name).isEqualTo("nullable")
assertThat(property?.typeParameters).isEmpty()
assertThat(property?.isNullable()).isTrue()
}
- testMetadata.getPropertyMetadata("nullableTypeArgument").let { property ->
+ testMetadata.getPropertyMetadata(
+ typeElement.getDeclaredField("nullableTypeArgument")
+ ).let { property ->
assertThat(property?.name).isEqualTo("nullableTypeArgument")
assertThat(property?.isNullable()).isFalse()
assertThat(property?.typeParameters).hasSize(1)
assertThat(property?.typeParameters?.single()?.isNullable()).isTrue()
}
- testMetadata.getPropertyMetadata("nonNullTypeArgument").let { property ->
+ testMetadata.getPropertyMetadata(
+ typeElement.getDeclaredField("nonNullTypeArgument")
+ ).let { property ->
assertThat(property?.name).isEqualTo("nonNullTypeArgument")
assertThat(property?.isNullable()).isFalse()
assertThat(property?.typeParameters).hasSize(1)
assertThat(property?.typeParameters?.single()?.isNullable()).isFalse()
}
- testMetadata.getPropertyMetadata("multipleTypeArguments").let { property ->
+ testMetadata.getPropertyMetadata(
+ typeElement.getDeclaredField("multipleTypeArguments")
+ ).let { property ->
assertThat(property?.name).isEqualTo("multipleTypeArguments")
assertThat(property?.isNullable()).isFalse()
assertThat(property?.typeParameters).hasSize(2)
@@ -341,9 +350,9 @@
}
""".trimIndent()
)
- simpleRun(listOf(src)) { invocation ->
+ simpleRun(listOf(src)) { env ->
val (element, metadata) = getMetadataElement(
- invocation,
+ env,
"Subject"
)
@@ -485,7 +494,9 @@
// tests value class properties. They won't show up in KAPT stubs since they don't have
// valid java source names but we still validate them here for consistency. Maybe one
// day we'll change Javac element to include these if we support Kotlin codegen in KAPT
- metadata.getPropertyMetadata("valueProp").let { valueProp ->
+ metadata.getPropertyMetadata(
+ element.getDeclaredField("valueProp")
+ ).let { valueProp ->
assertGetter(
kmFunction = valueProp?.getter,
name = "getValueProp",
@@ -499,7 +510,9 @@
paramNullable = true
)
}
- metadata.getPropertyMetadata("internalValueProp").let { valueProp ->
+ metadata.getPropertyMetadata(
+ element.getDeclaredField("internalValueProp")
+ ).let { valueProp ->
assertGetter(
kmFunction = valueProp?.getter,
name = "getInternalValueProp",
@@ -529,9 +542,9 @@
}
""".trimIndent()
)
- simpleRun(listOf(src)) { invocation ->
+ simpleRun(listOf(src)) { env ->
val (element, metadata) = getMetadataElement(
- invocation,
+ env,
"Subject"
)
metadata.getFunctionMetadata(
@@ -578,9 +591,9 @@
}
""".trimIndent()
)
- simpleRun(listOf(src)) { invocation ->
+ simpleRun(listOf(src)) { env ->
val (testDaoElement, testDaoMetadata) = getMetadataElement(
- invocation,
+ env,
"Subject"
)
fun assertParams(params: List<KmValueParameterContainer>?) {
@@ -617,17 +630,17 @@
val src = Source.kotlin(
"Subject.kt",
"""
- interface Subject {
- val nullableArrayWithNonNullComponent : Array<Int>?
- val nullableArrayWithNullableComponent : Array<Int?>?
- val nonNullArrayWithNonNullComponent : Array<Int>
- val nonNullArrayWithNullableComponent : Array<Int?>
+ class Subject {
+ val nullableArrayWithNonNullComponent : Array<Int>? = TODO()
+ val nullableArrayWithNullableComponent : Array<Int?>? = TODO()
+ val nonNullArrayWithNonNullComponent : Array<Int> = TODO()
+ val nonNullArrayWithNullableComponent : Array<Int?> = TODO()
}
""".trimIndent()
)
- simpleRun(listOf(src)) { invocation ->
- val (_, metadata) = getMetadataElement(
- invocation,
+ simpleRun(listOf(src)) { env ->
+ val (typeElement, metadata) = getMetadataElement(
+ env,
"Subject"
)
val propertyNames = listOf(
@@ -638,7 +651,7 @@
)
assertThat(
propertyNames
- .mapNotNull(metadata::getPropertyMetadata)
+ .mapNotNull { metadata.getPropertyMetadata(typeElement.getDeclaredField(it)) }
.map {
Triple(it.name, it.isNullable(), it.typeParameters.single().isNullable())
}
@@ -669,18 +682,18 @@
abstract class WithSuperType : Map<String, Int?> {}
""".trimIndent()
)
- simpleRun(listOf(src)) { invocation ->
- val (_, simple) = getMetadataElement(invocation, "Simple")
+ simpleRun(listOf(src)) { env ->
+ val (_, simple) = getMetadataElement(env, "Simple")
assertThat(simple.type.isNullable()).isFalse()
assertThat(simple.type.typeArguments).isEmpty()
- val (_, twoArgGeneric) = getMetadataElement(invocation, "TwoArgGeneric")
+ val (_, twoArgGeneric) = getMetadataElement(env, "TwoArgGeneric")
assertThat(twoArgGeneric.type.isNullable()).isFalse()
assertThat(twoArgGeneric.type.typeArguments).hasSize(2)
assertThat(twoArgGeneric.type.typeArguments[0].isNullable()).isFalse()
assertThat(twoArgGeneric.type.typeArguments[1].isNullable()).isFalse()
- val (_, withUpperBounds) = getMetadataElement(invocation, "WithUpperBounds")
+ val (_, withUpperBounds) = getMetadataElement(env, "WithUpperBounds")
assertThat(withUpperBounds.type.typeArguments).hasSize(2)
assertThat(withUpperBounds.type.typeArguments[0].upperBounds).hasSize(1)
assertThat(withUpperBounds.type.typeArguments[0].upperBounds!![0].isNullable())
@@ -689,7 +702,7 @@
assertThat(withUpperBounds.type.typeArguments[1].upperBounds!![0].isNullable())
.isTrue()
- val (_, withSuperType) = getMetadataElement(invocation, "WithSuperType")
+ val (_, withSuperType) = getMetadataElement(env, "WithSuperType")
assertThat(withSuperType.superType?.typeArguments?.get(0)?.isNullable()).isFalse()
assertThat(withSuperType.superType?.typeArguments?.get(1)?.isNullable()).isTrue()
}
@@ -707,17 +720,23 @@
}
""".trimIndent()
)
- simpleRun(listOf(src)) { invocation ->
- val (_, subject) = getMetadataElement(invocation, "Subject")
- subject.getPropertyMetadata("simple")!!.type.erasure().let {
+ simpleRun(listOf(src)) { env ->
+ val (typeElement, subject) = getMetadataElement(env, "Subject")
+ subject.getPropertyMetadata(
+ typeElement.getDeclaredField("simple")
+ )!!.type.erasure().let {
assertThat(it.isNullable()).isFalse()
assertThat(it.typeArguments).isEmpty()
}
- subject.getPropertyMetadata("nullableGeneric")!!.type.erasure().let {
+ subject.getPropertyMetadata(
+ typeElement.getDeclaredField("nullableGeneric")
+ )!!.type.erasure().let {
assertThat(it.isNullable()).isTrue()
assertThat(it.typeArguments).isEmpty()
}
- subject.getPropertyMetadata("nonNullGeneric")!!.type.erasure().let {
+ subject.getPropertyMetadata(
+ typeElement.getDeclaredField("nonNullGeneric")
+ )!!.type.erasure().let {
assertThat(it.isNullable()).isFalse()
assertThat(it.typeArguments).isEmpty()
}
@@ -749,7 +768,7 @@
) { invocation ->
val (_, metadata) =
getMetadataElement(
- (invocation.processingEnv as JavacProcessingEnv).delegate,
+ invocation.processingEnv as JavacProcessingEnv,
"KotlinClass"
)
assertThat(metadata).isNotNull()
@@ -779,8 +798,8 @@
abstract class C
""".trimIndent()
)
- simpleRun(listOf(src)) { invocation ->
- val (subjectElement, subjectMetadata) = getMetadataElement(invocation, "Subject")
+ simpleRun(listOf(src)) { env ->
+ val (subjectElement, subjectMetadata) = getMetadataElement(env, "Subject")
fun assertKmFunctionFound(functionName: String) {
val kmFunction = subjectMetadata.getFunctionMetadata(
subjectElement.getDeclaredMethod(functionName)
@@ -794,6 +813,59 @@
}
@Test
+ fun properties_anonymousNestingKind() {
+ // Only private functions are relevant to the test since public (or internal) properties
+ // are required to declare their type explicitly when right-hand side is ambiguous.
+ // b/232742201
+ val src = Source.kotlin(
+ "Subject.kt",
+ """
+ class Subject {
+ private val lazyA by lazy {
+ object: A { }
+ }
+ private val lazyAB by lazy {
+ object: A, B { }
+ }
+ private val lazyABC by lazy {
+ object: C(), A, B { }
+ }
+ private val lazyC by lazy {
+ object: C() { }
+ }
+ private val lazyAC by lazy {
+ object: C(), A { }
+ }
+ private val lazyAB_declaredA: A by lazy {
+ object: A, B { }
+ }
+ private val lazyAB_declaredB: B by lazy {
+ object: A, B { }
+ }
+ private val lazyABC_declaredC: C by lazy {
+ object: C(), A { }
+ }
+ }
+
+ interface A
+ interface B
+ abstract class C
+ """.trimIndent()
+ )
+ simpleRun(
+ sources = listOf(src)
+ ) { env ->
+ val subject = env.requireTypeElement("Subject")
+ subject.getDeclaredFields().forEach {
+ assertThat(it.getter).isNotNull()
+ }
+ subject.getDeclaredMethods().forEach {
+ assertThat(it.isKotlinPropertyMethod()).isTrue()
+ }
+ }
+ }
+
+ @Test
fun ignore_syntheticMetadata_defaultImpls() {
val src = Source.kotlin(
"Subject.kt",
@@ -806,8 +878,8 @@
simpleRun(
sources = listOf(src),
kotlincArgs = listOf("-Xjvm-default=disable")
- ) {
- val subjectElement = processingEnv.requireTypeElement("Subject.DefaultImpls")
+ ) { env ->
+ val subjectElement = env.requireTypeElement("Subject.DefaultImpls")
// Call metadata derived API causing it to be read
assertThat(subjectElement.isKotlinObject()).isFalse()
assertCompilationResult {
@@ -840,9 +912,9 @@
)
simpleRun(
sources = listOf(src),
- ) {
- assertThat(processingEnv.findTypeElement("Subject.Fruit")).isNotNull()
- val subjectElement = processingEnv.findTypeElement("Subject.WhenMappings")
+ ) { env ->
+ assertThat(env.findTypeElement("Subject.Fruit")).isNotNull()
+ val subjectElement = env.findTypeElement("Subject.WhenMappings")
// Currently $WhenMapping has the ACC_SYNTHETIC flag making it unreadable by
// annotation processors making it impossible to verify synthetic metadata is
// ignored.
@@ -877,19 +949,19 @@
)
simpleRun(
sources = listOf(aSrc, bSrc),
- ) {
+ ) { env ->
// Find the multi file class facade element
- val facadeElement = processingEnv.requireTypeElement("Subject")
+ val facadeElement = env.requireTypeElement("Subject")
// Call metadata derived API causing it to be read
assertThat(facadeElement.isKotlinObject()).isFalse()
// Try to find the multi file class part elements, currently these classes have the
// ACC_SYNTHETIC flag making them unreadable by annotation processors and impossible to
// verify that multi file metadata is ignored.
- val facadePartOne = processingEnv.findTypeElement("Subject__AKt")
+ val facadePartOne = env.findTypeElement("Subject__AKt")
?: throw AssumptionViolatedException("No test if MultiFileClassPart is not found")
assertThat(facadePartOne.isKotlinObject()).isFalse()
- val facadePartTwo = processingEnv.findTypeElement("Subject__BKt")
+ val facadePartTwo = env.findTypeElement("Subject__BKt")
?: throw AssumptionViolatedException("No test if MultiFileClassPart is not found")
assertThat(facadePartTwo.isKotlinObject()).isFalse()
assertCompilationResult {
@@ -904,13 +976,16 @@
it.simpleName.toString() == name
}
+ private fun TypeElement.getDeclaredField(name: String) =
+ ElementFilter.fieldsIn(enclosedElements).first { it.simpleName.toString() == name }
+
private fun TypeElement.getConstructors() = ElementFilter.constructorsIn(enclosedElements)
@Suppress("NAME_SHADOWING") // intentional
private fun simpleRun(
sources: List<Source> = emptyList(),
kotlincArgs: List<String> = emptyList(),
- handler: XTestInvocation.(ProcessingEnvironment) -> Unit
+ handler: XTestInvocation.(JavacProcessingEnv) -> Unit
) {
val (sources, classpath) = if (preCompiled) {
emptyList<Source>() to compileFiles(sources)
@@ -922,18 +997,17 @@
classpath = classpath,
kotlincArguments = kotlincArgs
) {
- val processingEnv = it.processingEnv
- if (processingEnv !is JavacProcessingEnv) {
+ val env = it.processingEnv
+ if (env !is JavacProcessingEnv) {
throw AssumptionViolatedException("This test only works for java/kapt compilation")
}
- it.handler(processingEnv.delegate)
+ it.handler(env)
}
}
- private fun getMetadataElement(processingEnv: ProcessingEnvironment, qName: String) =
- processingEnv.elementUtils.getTypeElement(qName).let {
- it to KmClassContainer.createFor(
- JavacProcessingEnv(processingEnv, XProcessingEnvConfig.DEFAULT), it)!!
+ private fun getMetadataElement(env: JavacProcessingEnv, qName: String) =
+ env.elementUtils.getTypeElement(qName).let {
+ it to KmClassContainer.createFor(env, it)!!
}
companion object {
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt b/room/room-compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt
index 86990ae..425bf1c 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/processor/ProcessorErrors.kt
@@ -64,6 +64,8 @@
val CANNOT_USE_UNBOUND_GENERICS_IN_DAO_CLASSES = "Cannot use unbound generics in Dao classes." +
" If you are trying to create a base DAO, create a normal class, extend it with type" +
" params then mark the subclass with @Dao."
+ val CANNOT_USE_MAP_COLUMN_AND_MAP_INFO_SIMULTANEOUSLY = "Cannot use @MapColumn and " +
+ " @MapInfo annotation in the same function. Please prefer using @MapColumn only."
val CANNOT_FIND_GETTER_FOR_FIELD = "Cannot find getter for field."
val CANNOT_FIND_SETTER_FOR_FIELD = "Cannot find setter for field."
val MISSING_PRIMARY_KEY = "An entity must have at least 1 field annotated with @PrimaryKey"
@@ -152,24 +154,18 @@
val DELETION_MISSING_PARAMS = "Method annotated with" +
" @Delete but does not have any parameters to delete."
- fun cannotMapInfoSpecifiedColumn(column: String, columnsInQuery: List<String>) =
- "Column specified in the provided @MapInfo annotation must be present in the query. " +
+ fun cannotMapSpecifiedColumn(column: String, columnsInQuery: List<String>, annotation: String) =
+ "Column specified in the provided @$annotation annotation must be present in the query. " +
"Provided: $column. Columns found: ${columnsInQuery.joinToString(", ")}"
val MAP_INFO_MUST_HAVE_AT_LEAST_ONE_COLUMN_PROVIDED = "To use the @MapInfo annotation, you " +
"must provide either the key column name, value column name, or both."
- fun keyMayNeedMapInfo(keyArg: String): String {
+ fun mayNeedMapColumn(columnArg: String): String {
return """
- Looks like you may need to use @MapInfo to clarify the 'keyColumn' needed for
- the return type of a method. Type argument that needs @MapInfo: $keyArg
- """.trim()
- }
-
- fun valueMayNeedMapInfo(valueArg: String): String {
- return """
- Looks like you may need to use @MapInfo to clarify the 'valueColumn' needed for
- the return type of a method. Type argument that needs @MapInfo: $valueArg
+ Looks like you may need to use @MapColumn to clarify the 'columnName' needed for
+ type argument(s) in the return type of a method. Type argument that needs
+ @MapColumn: $columnArg
""".trim()
}
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/processor/QueryMethodProcessor.kt b/room/room-compiler/src/main/kotlin/androidx/room/processor/QueryMethodProcessor.kt
index d9333f1..50be0d2 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/processor/QueryMethodProcessor.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/processor/QueryMethodProcessor.kt
@@ -26,7 +26,7 @@
import androidx.room.parser.ParsedQuery
import androidx.room.parser.QueryType
import androidx.room.parser.SqlParser
-import androidx.room.processor.ProcessorErrors.cannotMapInfoSpecifiedColumn
+import androidx.room.processor.ProcessorErrors.cannotMapSpecifiedColumn
import androidx.room.solver.TypeAdapterExtras
import androidx.room.solver.query.result.PojoRowAdapter
import androidx.room.verifier.ColumnInfo
@@ -208,6 +208,7 @@
)
}
+ @Suppress("DEPRECATION") // Due to MapInfo usage
private fun getQueryMethod(
delegate: MethodProcessorDelegate,
returnType: XType,
@@ -284,6 +285,7 @@
* Parse @MapInfo annotation, validate its inputs and put information in the bag of extras,
* it will be later used by the TypeAdapterStore.
*/
+ @Suppress("DEPRECATION") // Due to @MapInfo usage
private fun processMapInfo(
mapInfoAnnotation: XAnnotationBox<androidx.room.MapInfo>,
query: ParsedQuery,
@@ -323,18 +325,20 @@
keyColumn.isEmpty() || resultColumns.contains(keyColumn, keyTable),
queryExecutableElement
) {
- cannotMapInfoSpecifiedColumn(
+ cannotMapSpecifiedColumn(
(if (keyTable != null) "$keyTable." else "") + keyColumn,
- resultColumns.map { it.name }
+ resultColumns.map { it.name },
+ androidx.room.MapInfo::class.java.simpleName
)
}
context.checker.check(
valueColumn.isEmpty() || resultColumns.contains(valueColumn, valueTable),
queryExecutableElement
) {
- cannotMapInfoSpecifiedColumn(
+ cannotMapSpecifiedColumn(
(if (valueTable != null) "$valueTable." else "") + valueColumn,
- resultColumns.map { it.name }
+ resultColumns.map { it.name },
+ androidx.room.MapInfo::class.java.simpleName
)
}
}
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/processor/RawQueryMethodProcessor.kt b/room/room-compiler/src/main/kotlin/androidx/room/processor/RawQueryMethodProcessor.kt
index f51988f..f2ec672 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/processor/RawQueryMethodProcessor.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/processor/RawQueryMethodProcessor.kt
@@ -60,10 +60,10 @@
val query = SqlParser.rawQueryForTables(observedTableNames)
// build the query but don't calculate result info since we just guessed it.
val resultBinder = delegate.findResultBinder(returnType, query) {
+ @Suppress("DEPRECATION")
delegate.executableElement.getAnnotation(androidx.room.MapInfo::class)?.let {
- val keyColumn = it.value.keyColumn.toString()
- val valueColumn = it.value.valueColumn.toString()
-
+ val keyColumn = it.value.keyColumn
+ val valueColumn = it.value.valueColumn
context.checker.check(
keyColumn.isNotEmpty() || valueColumn.isNotEmpty(),
executableElement,
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt
index d6e4dcb..614b818 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt
@@ -77,6 +77,7 @@
import androidx.room.solver.query.result.MapQueryResultAdapter
import androidx.room.solver.query.result.MapValueResultAdapter
import androidx.room.solver.query.result.MultimapQueryResultAdapter
+import androidx.room.solver.query.result.MultimapQueryResultAdapter.Companion.getMapColumnName
import androidx.room.solver.query.result.MultimapQueryResultAdapter.Companion.validateMapKeyTypeArg
import androidx.room.solver.query.result.MultimapQueryResultAdapter.Companion.validateMapValueTypeArg
import androidx.room.solver.query.result.MultimapQueryResultAdapter.MapType.Companion.isSparseArray
@@ -624,29 +625,40 @@
// Get @MapInfo info if any (this might be null)
val mapInfo = extras.getData(MapInfo::class)
+ val mapKeyColumn = getMapColumnName(context, query, keyTypeArg)
+ val mapValueColumn = getMapColumnName(context, query, valueTypeArg)
+ if (mapInfo != null && (mapKeyColumn != null || mapValueColumn != null)) {
+ context.logger.e(
+ ProcessorErrors.CANNOT_USE_MAP_COLUMN_AND_MAP_INFO_SIMULTANEOUSLY
+ )
+ }
+
+ val mappedKeyColumnName = mapKeyColumn ?: mapInfo?.keyColumnName
+ val mappedValueColumnName = mapValueColumn ?: mapInfo?.valueColumnName
+
val keyRowAdapter = findRowAdapter(
typeMirror = keyTypeArg,
query = query,
- columnName = mapInfo?.keyColumnName
+ columnName = mappedKeyColumnName
) ?: return null
val valueRowAdapter = findRowAdapter(
typeMirror = valueTypeArg,
query = query,
- columnName = mapInfo?.valueColumnName
+ columnName = mappedValueColumnName
) ?: return null
validateMapKeyTypeArg(
context = context,
keyTypeArg = keyTypeArg,
keyReader = findCursorValueReader(keyTypeArg, null),
- mapInfo = mapInfo
+ keyColumnName = mappedKeyColumnName
)
validateMapValueTypeArg(
context = context,
valueTypeArg = valueTypeArg,
valueReader = findCursorValueReader(valueTypeArg, null),
- mapInfo = mapInfo
+ valueColumnName = mappedValueColumnName
)
return GuavaImmutableMultimapQueryResultAdapter(
context = context,
@@ -694,18 +706,25 @@
// Get @MapInfo info if any (this might be null)
val mapInfo = extras.getData(MapInfo::class)
+ val mapColumn = getMapColumnName(context, query, keyTypeArg)
+ if (mapInfo != null && mapColumn != null) {
+ context.logger.e(
+ ProcessorErrors.CANNOT_USE_MAP_COLUMN_AND_MAP_INFO_SIMULTANEOUSLY
+ )
+ }
+ val mappedKeyColumnName = mapColumn ?: mapInfo?.keyColumnName
val keyRowAdapter = findRowAdapter(
typeMirror = keyTypeArg,
query = query,
- columnName = mapInfo?.keyColumnName
+ columnName = mappedKeyColumnName
) ?: return null
validateMapKeyTypeArg(
context = context,
keyTypeArg = keyTypeArg,
keyReader = findCursorValueReader(keyTypeArg, null),
- mapInfo = mapInfo
+ keyColumnName = mappedKeyColumnName
)
val mapValueResultAdapter = findMapValueResultAdapter(
@@ -801,17 +820,25 @@
}
val valueTypeArg = mapValueTypeArg.typeArguments.single().extendsBoundOrSelf()
+ val mapColumnName = getMapColumnName(context, query, valueTypeArg)
+ if (mapColumnName != null && mapInfo != null) {
+ context.logger.e(
+ ProcessorErrors.CANNOT_USE_MAP_COLUMN_AND_MAP_INFO_SIMULTANEOUSLY
+ )
+ }
+
+ val mappedValueColumnName = mapColumnName ?: mapInfo?.valueColumnName
val valueRowAdapter = findRowAdapter(
typeMirror = valueTypeArg,
query = query,
- columnName = mapInfo?.valueColumnName
+ columnName = mappedValueColumnName
) ?: return null
validateMapValueTypeArg(
context = context,
valueTypeArg = valueTypeArg,
valueReader = findCursorValueReader(valueTypeArg, null),
- mapInfo = mapInfo
+ valueColumnName = mappedValueColumnName
)
return MapValueResultAdapter.EndMapValueResultAdapter(
@@ -821,14 +848,19 @@
)
} else if (mapValueTypeArg.isTypeOf(java.util.Map::class)) {
val keyTypeArg = mapValueTypeArg.typeArguments[0].extendsBoundOrSelf()
+ val valueTypeArg = mapValueTypeArg.typeArguments[1].extendsBoundOrSelf()
+
val keyRowAdapter = findRowAdapter(
typeMirror = keyTypeArg,
query = query,
- columnName = null
+ // No need to account for @MapInfo since nested maps did not support
+ // this now deprecated annotation anyway.
+ columnName = getMapColumnName(context, query, keyTypeArg)
) ?: return null
- val valueTypeArg = mapValueTypeArg.typeArguments[1].extendsBoundOrSelf()
val valueMapAdapter = findMapValueResultAdapter(
- query, mapInfo, valueTypeArg
+ query = query,
+ mapInfo = mapInfo,
+ mapValueTypeArg = valueTypeArg
) ?: return null
return MapValueResultAdapter.NestedMapValueResultAdapter(
keyRowAdapter = keyRowAdapter,
@@ -837,17 +869,19 @@
mapValueResultAdapter = valueMapAdapter
)
} else {
+ val mappedValueColumnName = getMapColumnName(context, query, mapValueTypeArg)
+ ?: mapInfo?.valueColumnName
val valueRowAdapter = findRowAdapter(
typeMirror = mapValueTypeArg,
query = query,
- columnName = mapInfo?.valueColumnName
+ columnName = mappedValueColumnName
) ?: return null
validateMapValueTypeArg(
context = context,
valueTypeArg = mapValueTypeArg,
valueReader = findCursorValueReader(mapValueTypeArg, null),
- mapInfo = mapInfo
+ valueColumnName = mappedValueColumnName
)
return MapValueResultAdapter.EndMapValueResultAdapter(
valueRowAdapter = valueRowAdapter,
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/MapValueResultAdapter.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/MapValueResultAdapter.kt
index 3d77078..a758ff4 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/MapValueResultAdapter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/MapValueResultAdapter.kt
@@ -340,8 +340,12 @@
language == CodeLanguage.KOTLIN &&
valueTypeArg.nullability == XNullability.NONNULL
) {
- // TODO(b/249984504): Generate / output a better message.
- addStatement("error(%S)", "Missing value for a key.")
+ addStatement(
+ "error(%S)",
+ "The column(s) of the map value object of type " +
+ "'$valueTypeArg' are NULL but the map's value type " +
+ "argument expect it to be NON-NULL"
+ )
} else {
genPutValueCode.invoke("null", false)
addStatement("continue")
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/MultimapQueryResultAdapter.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/MultimapQueryResultAdapter.kt
index c5791df..1d71248 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/MultimapQueryResultAdapter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/MultimapQueryResultAdapter.kt
@@ -16,9 +16,11 @@
package androidx.room.solver.query.result
+import androidx.room.MapColumn
import androidx.room.compiler.codegen.CodeLanguage
import androidx.room.compiler.codegen.XClassName
import androidx.room.compiler.codegen.XCodeBlock
+import androidx.room.compiler.codegen.asClassName
import androidx.room.compiler.processing.XType
import androidx.room.ext.CollectionTypeNames
import androidx.room.ext.CommonTypeNames
@@ -30,8 +32,8 @@
import androidx.room.processor.ProcessorErrors.AmbiguousColumnLocation.MAP_INFO
import androidx.room.processor.ProcessorErrors.AmbiguousColumnLocation.POJO
import androidx.room.solver.types.CursorValueReader
+import androidx.room.verifier.ColumnInfo
import androidx.room.vo.ColumnIndexVar
-import androidx.room.vo.MapInfo
import androidx.room.vo.Warning
/**
@@ -118,14 +120,14 @@
companion object {
/**
- * Checks if the @MapInfo annotation is needed for clarification regarding the key type
+ * Checks if the @MapColumn annotation is needed for clarification regarding the key type
* arg of a Map return type.
*/
fun validateMapKeyTypeArg(
context: Context,
keyTypeArg: XType,
keyReader: CursorValueReader?,
- mapInfo: MapInfo?,
+ keyColumnName: String?,
) {
if (!keyTypeArg.implementsEqualsAndHashcode()) {
context.logger.w(
@@ -136,10 +138,10 @@
)
}
- val hasKeyColumnName = mapInfo?.keyColumnName?.isNotEmpty() ?: false
+ val hasKeyColumnName = keyColumnName?.isNotEmpty() ?: false
if (!hasKeyColumnName && keyReader != null) {
context.logger.e(
- ProcessorErrors.keyMayNeedMapInfo(
+ ProcessorErrors.mayNeedMapColumn(
keyTypeArg.asTypeName().toString(context.codeLanguage)
)
)
@@ -147,24 +149,68 @@
}
/**
- * Checks if the @MapInfo annotation is needed for clarification regarding the value type
+ * Checks if the @MapColumn annotation is needed for clarification regarding the value type
* arg of a Map return type.
*/
fun validateMapValueTypeArg(
context: Context,
valueTypeArg: XType,
valueReader: CursorValueReader?,
- mapInfo: MapInfo?,
+ valueColumnName: String?,
) {
- val hasValueColumnName = mapInfo?.valueColumnName?.isNotEmpty() ?: false
+ val hasValueColumnName = valueColumnName?.isNotEmpty() ?: false
if (!hasValueColumnName && valueReader != null) {
context.logger.e(
- ProcessorErrors.valueMayNeedMapInfo(
+ ProcessorErrors.mayNeedMapColumn(
valueTypeArg.asTypeName().toString(context.codeLanguage)
)
)
}
}
+
+ /**
+ * Retrieves the `columnName` value from a @MapColumn annotation.
+ */
+ fun getMapColumnName(context: Context, query: ParsedQuery, type: XType): String? {
+ val resultColumns = query.resultInfo?.columns
+ val resultTableAliases = query.tables.associate { it.name to it.alias }
+ val annotation = type.getAnnotation(MapColumn::class.asClassName()) ?: return null
+
+ val mapColumnName = annotation.getAsString("columnName")
+ val mapColumnTableName = annotation.getAsString("tableName")
+
+ fun List<ColumnInfo>.contains(
+ columnName: String,
+ tableName: String?
+ ) = any { resultColumn ->
+ val resultTableAlias = resultColumn.originTable?.let {
+ resultTableAliases[it] ?: it
+ }
+ resultColumn.name == columnName && (
+ if (!tableName.isNullOrEmpty()) {
+ resultTableAlias == tableName || resultColumn.originTable == tableName
+ } else true)
+ }
+
+ if (resultColumns != null) {
+ // Disambiguation check for MapColumn
+ if (!resultColumns.contains(mapColumnName, mapColumnTableName)) {
+ val errorColumn = if (mapColumnTableName.isNotEmpty()) {
+ "$mapColumnTableName."
+ } else {
+ ""
+ } + mapColumnName
+ context.logger.e(
+ ProcessorErrors.cannotMapSpecifiedColumn(
+ errorColumn,
+ resultColumns.map { it.name },
+ MapColumn::class.java.simpleName
+ )
+ )
+ }
+ }
+ return mapColumnName
+ }
}
/**
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/SingleItemQueryResultAdapter.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/SingleItemQueryResultAdapter.kt
index b9fdcbb..519ff4a 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/SingleItemQueryResultAdapter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/query/result/SingleItemQueryResultAdapter.kt
@@ -41,8 +41,11 @@
type.nullability == XNullability.NONNULL &&
defaultValue == "null"
) {
- // TODO(b/249984504): Generate / output a better message.
- addStatement("error(%S)", "Cursor was empty, but expected a single item.")
+ addStatement(
+ "error(%S)", "The query result was empty, but expected a single row to " +
+ "return a NON-NULL object of " +
+ "type <${type.asTypeName().toString(language)}>."
+ )
} else {
addStatement("%L = %L", outVarName, rowAdapter.out.defaultValue())
}
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/types/NullAwareTypeConverters.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/types/NullAwareTypeConverters.kt
index 7d2196e..03c641c 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/types/NullAwareTypeConverters.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/types/NullAwareTypeConverters.kt
@@ -93,7 +93,7 @@
private fun XCodeBlock.Builder.addIllegalStateException() {
val typeName = from.asTypeName().copy(nullable = false).toString(language)
- val message = "Expected non-null $typeName, but it was null."
+ val message = "Expected NON-NULL '$typeName', but it was NULL."
when (language) {
CodeLanguage.JAVA -> {
addStatement(
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/solver/types/ValueClassConverterWrapper.kt b/room/room-compiler/src/main/kotlin/androidx/room/solver/types/ValueClassConverterWrapper.kt
index cc2d6ee..831520d 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/solver/types/ValueClassConverterWrapper.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/solver/types/ValueClassConverterWrapper.kt
@@ -80,13 +80,13 @@
scope.builder.apply {
val propertyName = scope.getTmpVar("_$valuePropertyName")
val assignmentBlock = if (out.nullability == XNullability.NONNULL) {
- // TODO(b/249984504): Generate / output a better message.
XCodeBlock.of(
scope.language,
"checkNotNull(%L.%L) { %S }",
valueVarName,
valuePropertyName,
- "Cannot bind nullable value of inline class to a NOT NULL column."
+ "Cannot bind NULLABLE value '$valuePropertyName' of inline " +
+ "class '$out' to a NOT NULL column."
)
} else {
XCodeBlock.of(
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/vo/RelationCollector.kt b/room/room-compiler/src/main/kotlin/androidx/room/vo/RelationCollector.kt
index dce1f83..eceddad 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/vo/RelationCollector.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/vo/RelationCollector.kt
@@ -174,8 +174,13 @@
addStatement("%L = %L.get(%L)", tmpRelationVar, varName, tmpKeyVar)
if (language == CodeLanguage.KOTLIN && relation.field.nonNull) {
beginControlFlow("if (%L == null)", tmpRelationVar)
- // TODO(b/249984504): Generate / output a better message.
- addStatement("error(%S)", "Missing relationship item.")
+ addStatement(
+ "error(%S)",
+ "Relationship item '${relation.field.name}' was expected to" +
+ " be NON-NULL but is NULL in @Relation involving " +
+ "a parent column named '${relation.parentField.columnName}' and " +
+ "entityColumn named '${relation.entityField.columnName}'."
+ )
endControlFlow()
}
}
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityDeletionAdapterWriter.kt b/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityDeletionAdapterWriter.kt
index d1d1379..204bc48 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityDeletionAdapterWriter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityDeletionAdapterWriter.kt
@@ -60,7 +60,7 @@
XFunSpec.builder(
language = language,
name = "createQuery",
- visibility = VisibilityModifier.PUBLIC,
+ visibility = VisibilityModifier.PROTECTED,
isOverride = true
).apply {
returns(CommonTypeNames.STRING)
@@ -73,7 +73,7 @@
XFunSpec.builder(
language = language,
name = "bind",
- visibility = VisibilityModifier.PUBLIC,
+ visibility = VisibilityModifier.PROTECTED,
isOverride = true
).apply {
val stmtParam = "statement"
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityInsertionAdapterWriter.kt b/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityInsertionAdapterWriter.kt
index 5e2b01f..3a6d684 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityInsertionAdapterWriter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityInsertionAdapterWriter.kt
@@ -75,7 +75,7 @@
XFunSpec.builder(
language = language,
name = "createQuery",
- visibility = VisibilityModifier.PUBLIC,
+ visibility = VisibilityModifier.PROTECTED,
isOverride = true
).apply {
returns(CommonTypeNames.STRING)
@@ -105,7 +105,7 @@
XFunSpec.builder(
language = language,
name = "bind",
- visibility = VisibilityModifier.PUBLIC,
+ visibility = VisibilityModifier.PROTECTED,
isOverride = true
).apply {
returns(XTypeName.UNIT_VOID)
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityUpdateAdapterWriter.kt b/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityUpdateAdapterWriter.kt
index c7f487c..f06592d 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityUpdateAdapterWriter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/writer/EntityUpdateAdapterWriter.kt
@@ -53,7 +53,7 @@
XFunSpec.builder(
language = language,
name = "createQuery",
- visibility = VisibilityModifier.PUBLIC,
+ visibility = VisibilityModifier.PROTECTED,
isOverride = true
).apply {
returns(CommonTypeNames.STRING)
@@ -80,7 +80,7 @@
XFunSpec.builder(
language = language,
name = "bind",
- visibility = VisibilityModifier.PUBLIC,
+ visibility = VisibilityModifier.PROTECTED,
isOverride = true
).apply {
val stmtParam = "statement"
diff --git a/room/room-compiler/src/main/kotlin/androidx/room/writer/FieldReadWriteWriter.kt b/room/room-compiler/src/main/kotlin/androidx/room/writer/FieldReadWriteWriter.kt
index 8b856c3..b523301 100644
--- a/room/room-compiler/src/main/kotlin/androidx/room/writer/FieldReadWriteWriter.kt
+++ b/room/room-compiler/src/main/kotlin/androidx/room/writer/FieldReadWriteWriter.kt
@@ -373,10 +373,10 @@
typeName.nullability == XNullability.NONNULL &&
defaultValue == "null"
) {
- // TODO(b/249984504): Generate / output a better message.
addStatement(
"error(%S)",
- "Missing column '${field.columnName}' for a non null value."
+ "Missing value for a NON-NULL column '${field.columnName}', " +
+ "found NULL value instead."
)
} else {
addStatement("%L = %L", tmpField, defaultValue)
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/QueryMethodProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/QueryMethodProcessorTest.kt
index 13d0293..9c40b99 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/QueryMethodProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/QueryMethodProcessorTest.kt
@@ -41,11 +41,11 @@
import androidx.room.ext.RxJava3TypeNames
import androidx.room.parser.QueryType
import androidx.room.parser.Table
+import androidx.room.processor.ProcessorErrors.CANNOT_USE_MAP_COLUMN_AND_MAP_INFO_SIMULTANEOUSLY
import androidx.room.processor.ProcessorErrors.DO_NOT_USE_GENERIC_IMMUTABLE_MULTIMAP
import androidx.room.processor.ProcessorErrors.MAP_INFO_MUST_HAVE_AT_LEAST_ONE_COLUMN_PROVIDED
import androidx.room.processor.ProcessorErrors.cannotFindQueryResultAdapter
-import androidx.room.processor.ProcessorErrors.keyMayNeedMapInfo
-import androidx.room.processor.ProcessorErrors.valueMayNeedMapInfo
+import androidx.room.processor.ProcessorErrors.mayNeedMapColumn
import androidx.room.solver.query.result.DataSourceFactoryQueryResultBinder
import androidx.room.solver.query.result.ListQueryResultAdapter
import androidx.room.solver.query.result.LiveDataQueryResultBinder
@@ -1305,7 +1305,7 @@
DAO_PREFIX + input.joinToString("\n") + DAO_SUFFIX
)
val commonSources = listOf(
- COMMON.LIVE_DATA, COMMON.COMPUTABLE_LIVE_DATA, COMMON.USER, COMMON.BOOK,
+ COMMON.LIVE_DATA, COMMON.COMPUTABLE_LIVE_DATA, COMMON.USER, COMMON.BOOK, COMMON.PAGE,
COMMON.NOT_AN_ENTITY, COMMON.ARTIST, COMMON.SONG, COMMON.IMAGE, COMMON.IMAGE_FORMAT,
COMMON.CONVERTER
)
@@ -1511,6 +1511,131 @@
}
@Test
+ fun testUseMapColumnWithColumnName() {
+ if (!enableVerification) {
+ return
+ }
+ singleQueryMethod<ReadQueryMethod>(
+ """
+ @SuppressWarnings(
+ {RoomWarnings.CURSOR_MISMATCH, RoomWarnings.AMBIGUOUS_COLUMN_IN_RESULT}
+ )
+ @Query("SELECT * FROM User u JOIN Book b ON u.uid == b.uid")
+ abstract Map<@MapColumn(columnName = "uid") Integer, Book> getMultimap();
+ """
+ ) { _, invocation ->
+ invocation.assertCompilationResult {
+ hasNoWarnings()
+ }
+ }
+ }
+
+ @Test
+ fun testUseMapColumnWithColumnNameWrongTableName() {
+ if (!enableVerification) {
+ return
+ }
+ singleQueryMethod<ReadQueryMethod>(
+ """
+ @SuppressWarnings(
+ {RoomWarnings.CURSOR_MISMATCH, RoomWarnings.AMBIGUOUS_COLUMN_IN_RESULT}
+ )
+ @Query("SELECT * FROM User u JOIN Book b ON u.uid == b.uid")
+ abstract Map<@MapColumn(columnName = "uid", tableName = "NoName") Integer, Book> getMultimap();
+ """
+ ) { _, invocation ->
+ invocation.assertCompilationResult {
+ hasErrorContaining(
+ "Column specified in the provided @MapColumn " +
+ "annotation must be present in the query."
+ )
+ }
+ }
+ }
+
+ @Test
+ fun testUseNestedMapColumnWithColumnName() {
+ if (!enableVerification) {
+ return
+ }
+ singleQueryMethod<ReadQueryMethod>(
+ """
+ @SuppressWarnings(
+ {RoomWarnings.CURSOR_MISMATCH, RoomWarnings.AMBIGUOUS_COLUMN_IN_RESULT}
+ )
+ @Query("SELECT * FROM User u JOIN Book b ON u.uid == b.uid JOIN Page on b.uid == pBid")
+ abstract Map<@MapColumn(columnName = "uid") Integer, Map<Book, @MapColumn(columnName = "pBid") Integer>> getMultimap();
+ """
+ ) { _, invocation ->
+ invocation.assertCompilationResult {
+ hasNoWarnings()
+ }
+ }
+ }
+
+ @Test
+ fun testUseNestedMapColumnWithNestedKeyColumnName() {
+ if (!enableVerification) {
+ return
+ }
+ singleQueryMethod<ReadQueryMethod>(
+ """
+ @SuppressWarnings(
+ {RoomWarnings.CURSOR_MISMATCH, RoomWarnings.AMBIGUOUS_COLUMN_IN_RESULT}
+ )
+ @Query("SELECT * FROM User u JOIN Book b ON u.uid == b.uid JOIN Page on b.uid == pBid")
+ abstract Map<@MapColumn(columnName = "uid") Integer, Map<@MapColumn(columnName = "bookId") Integer, @MapColumn(columnName = "pBid") Integer>> getMultimap();
+ """
+ ) { _, invocation ->
+ invocation.assertCompilationResult {
+ hasNoWarnings()
+ }
+ }
+ }
+
+ @Test
+ fun testUseMapColumnWithColumnAlias() {
+ if (!enableVerification) {
+ return
+ }
+ singleQueryMethod<ReadQueryMethod>(
+ """
+ @SuppressWarnings(RoomWarnings.CURSOR_MISMATCH)
+ @Query("SELECT name, (SELECT count(*) FROM User u JOIN Book b ON u.uid == b.uid) "
+ + "AS bookCount FROM User")
+ abstract Map<@MapColumn(columnName = "name") String, @MapColumn(columnName = "bookCount") Integer> getMultimap();
+ """
+ ) { _, invocation ->
+ invocation.assertCompilationResult {
+ hasNoWarnings()
+ }
+ }
+ }
+
+ @Test
+ fun testCannotHaveMapInfoAndMapColumn() {
+ if (!enableVerification) {
+ return
+ }
+ singleQueryMethod<ReadQueryMethod>(
+ """
+ @SuppressWarnings(
+ {RoomWarnings.CURSOR_MISMATCH, RoomWarnings.AMBIGUOUS_COLUMN_IN_RESULT}
+ )
+ @MapInfo(keyColumn = "uid", keyTable = "u")
+ @Query("SELECT * FROM User u JOIN Book b ON u.uid == b.uid")
+ abstract Map<@MapColumn(columnName = "uid") Integer, Book> getMultimap();
+ """
+ ) { _, invocation ->
+ invocation.assertCompilationResult {
+ hasErrorContaining(
+ CANNOT_USE_MAP_COLUMN_AND_MAP_INFO_SIMULTANEOUSLY
+ )
+ }
+ }
+ }
+
+ @Test
fun testDoesNotImplementEqualsAndHashcodeQuery() {
singleQueryMethod<ReadQueryMethod>(
"""
@@ -1539,7 +1664,7 @@
) { _, invocation ->
invocation.assertCompilationResult {
hasErrorContaining(
- valueMayNeedMapInfo(CommonTypeNames.STRING.canonicalName)
+ mayNeedMapColumn(STRING.canonicalName)
)
}
}
@@ -1556,7 +1681,7 @@
) { _, invocation ->
invocation.assertCompilationResult {
hasErrorContaining(
- valueMayNeedMapInfo(CommonTypeNames.STRING.canonicalName)
+ mayNeedMapColumn(STRING.canonicalName)
)
}
}
@@ -1572,7 +1697,7 @@
) { _, invocation ->
invocation.assertCompilationResult {
hasErrorContaining(
- valueMayNeedMapInfo(CommonTypeNames.STRING.canonicalName)
+ mayNeedMapColumn(STRING.canonicalName)
)
}
}
@@ -1588,7 +1713,7 @@
) { _, invocation ->
invocation.assertCompilationResult {
hasErrorContaining(
- valueMayNeedMapInfo(CommonTypeNames.STRING.canonicalName)
+ mayNeedMapColumn(STRING.canonicalName)
)
}
}
@@ -1604,7 +1729,7 @@
) { _, invocation ->
invocation.assertCompilationResult {
hasErrorContaining(
- valueMayNeedMapInfo(XTypeName.BOXED_LONG.canonicalName)
+ mayNeedMapColumn(XTypeName.BOXED_LONG.canonicalName)
)
}
}
@@ -1620,7 +1745,7 @@
) { _, invocation ->
invocation.assertCompilationResult {
hasErrorContaining(
- valueMayNeedMapInfo(XTypeName.BOXED_LONG.canonicalName)
+ mayNeedMapColumn(XTypeName.BOXED_LONG.canonicalName)
)
}
}
@@ -1636,7 +1761,7 @@
) { _, invocation ->
invocation.assertCompilationResult {
hasErrorContaining(
- valueMayNeedMapInfo(XTypeName.BOXED_LONG.canonicalName)
+ mayNeedMapColumn(XTypeName.BOXED_LONG.canonicalName)
)
}
}
@@ -1653,7 +1778,7 @@
) { _, invocation ->
invocation.assertCompilationResult {
hasErrorContaining(
- keyMayNeedMapInfo("java.util.Date")
+ mayNeedMapColumn("java.util.Date")
)
}
}
@@ -1670,7 +1795,7 @@
) { _, invocation ->
invocation.assertCompilationResult {
hasErrorContaining(
- valueMayNeedMapInfo("java.util.Date")
+ mayNeedMapColumn("java.util.Date")
)
}
}
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/processor/RawQueryMethodProcessorTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/processor/RawQueryMethodProcessorTest.kt
index 291267e..805c6ea 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/processor/RawQueryMethodProcessorTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/processor/RawQueryMethodProcessorTest.kt
@@ -423,7 +423,7 @@
) { _, invocation ->
invocation.assertCompilationResult {
hasErrorContaining(
- ProcessorErrors.valueMayNeedMapInfo(
+ ProcessorErrors.mayNeedMapColumn(
CommonTypeNames.STRING.canonicalName
)
)
@@ -441,7 +441,7 @@
) { _, invocation ->
invocation.assertCompilationResult {
hasErrorContaining(
- ProcessorErrors.valueMayNeedMapInfo(
+ ProcessorErrors.mayNeedMapColumn(
CommonTypeNames.STRING.canonicalName
)
)
@@ -459,7 +459,7 @@
) { _, invocation ->
invocation.assertCompilationResult {
hasErrorContaining(
- ProcessorErrors.valueMayNeedMapInfo(
+ ProcessorErrors.mayNeedMapColumn(
CommonTypeNames.STRING.canonicalName
)
)
@@ -477,7 +477,7 @@
) { _, invocation ->
invocation.assertCompilationResult {
hasErrorContaining(
- ProcessorErrors.valueMayNeedMapInfo(XTypeName.BOXED_LONG.canonicalName)
+ ProcessorErrors.mayNeedMapColumn(XTypeName.BOXED_LONG.canonicalName)
)
}
}
@@ -493,7 +493,7 @@
) { _, invocation ->
invocation.assertCompilationResult {
hasErrorContaining(
- ProcessorErrors.valueMayNeedMapInfo(XTypeName.BOXED_LONG.canonicalName)
+ ProcessorErrors.mayNeedMapColumn(XTypeName.BOXED_LONG.canonicalName)
)
}
}
@@ -509,7 +509,7 @@
) { _, invocation ->
invocation.assertCompilationResult {
hasErrorContaining(
- ProcessorErrors.valueMayNeedMapInfo(XTypeName.BOXED_LONG.canonicalName)
+ ProcessorErrors.mayNeedMapColumn(XTypeName.BOXED_LONG.canonicalName)
)
}
}
@@ -526,7 +526,7 @@
) { _, invocation ->
invocation.assertCompilationResult {
hasErrorContaining(
- ProcessorErrors.keyMayNeedMapInfo("java.util.Date")
+ ProcessorErrors.mayNeedMapColumn("java.util.Date")
)
}
}
@@ -543,7 +543,7 @@
) { _, invocation ->
invocation.assertCompilationResult {
hasErrorContaining(
- ProcessorErrors.valueMayNeedMapInfo("java.util.Date")
+ ProcessorErrors.mayNeedMapColumn("java.util.Date")
)
}
}
@@ -560,7 +560,7 @@
) { _, invocation ->
invocation.assertCompilationResult {
hasErrorContaining(
- ProcessorErrors.valueMayNeedMapInfo(
+ ProcessorErrors.mayNeedMapColumn(
CommonTypeNames.STRING.canonicalName
)
)
@@ -569,6 +569,47 @@
}
@Test
+ fun testUseMapColumnWithColumnName() {
+ singleQueryMethod(
+ """
+ @SuppressWarnings(
+ {RoomWarnings.CURSOR_MISMATCH, RoomWarnings.AMBIGUOUS_COLUMN_IN_RESULT}
+ )
+ @RawQuery
+ abstract Map<@MapColumn(columnName = "uid") Integer, Book> getMultimap(
+ SupportSQLiteQuery query
+ );
+ """
+ ) { _, invocation ->
+ invocation.assertCompilationResult {
+ hasNoWarnings()
+ }
+ }
+ }
+
+ @Test
+ fun testCannotHaveMapInfoAndMapColumn() {
+ singleQueryMethod(
+ """
+ @SuppressWarnings(
+ {RoomWarnings.CURSOR_MISMATCH, RoomWarnings.AMBIGUOUS_COLUMN_IN_RESULT}
+ )
+ @MapInfo(keyColumn = "uid", keyTable = "u")
+ @RawQuery
+ abstract Map<@MapColumn(columnName = "uid") Integer, Book> getMultimap(
+ SupportSQLiteQuery query
+ );
+ """
+ ) { _, invocation ->
+ invocation.assertCompilationResult {
+ hasErrorContaining(
+ ProcessorErrors.CANNOT_USE_MAP_COLUMN_AND_MAP_INFO_SIMULTANEOUSLY
+ )
+ }
+ }
+ }
+
+ @Test
fun suspendReturnsDeferredType() {
listOf(
"${RxJava2TypeNames.FLOWABLE.canonicalName}<Int>",
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/testing/test_util.kt b/room/room-compiler/src/test/kotlin/androidx/room/testing/test_util.kt
index b98bcfd6..688ff71 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/testing/test_util.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/testing/test_util.kt
@@ -78,6 +78,11 @@
val BOOK by lazy {
loadJavaCode("common/input/Book.java", "foo.bar.Book")
}
+
+ val PAGE by lazy {
+ loadJavaCode("common/input/Page.java", "foo.bar.Page")
+ }
+
val NOT_AN_ENTITY by lazy {
loadJavaCode("common/input/NotAnEntity.java", "foo.bar.NotAnEntity")
}
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/writer/DaoKotlinCodeGenTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/writer/DaoKotlinCodeGenTest.kt
index 9c2d88c..ca22005 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/writer/DaoKotlinCodeGenTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/writer/DaoKotlinCodeGenTest.kt
@@ -1483,6 +1483,7 @@
@Query("SELECT * FROM Artist JOIN Song ON Artist.artistId = Song.artistKey")
fun getArtistWithSongs(): Map<Artist, List<Song>>
+ @Suppress("DEPRECATION") // For @MapInfo
@MapInfo(valueColumn = "songCount")
@Query(
"SELECT Artist.*, COUNT(songId) as songCount " +
@@ -1492,6 +1493,7 @@
fun getArtistSongCount(): Map<Artist, Int>
@SuppressWarnings(RoomWarnings.CURSOR_MISMATCH)
+ @Suppress("DEPRECATION") // For @MapInfo
@MapInfo(valueColumn = "songId")
@Query("SELECT * FROM Artist JOIN Song ON Artist.artistId = Song.artistKey")
fun getArtistWithSongIds(): Map<Artist, List<String>>
diff --git a/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/Interface1.java b/room/room-compiler/src/test/test-data/common/input/Page.java
similarity index 61%
copy from lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/Interface1.java
copy to room/room-compiler/src/test/test-data/common/input/Page.java
index 31d0e6f..e9937cb 100644
--- a/lifecycle/lifecycle-common/src/test/java/androidx/lifecycle/observers/Interface1.java
+++ b/room/room-compiler/src/test/test-data/common/input/Page.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,14 +14,11 @@
* limitations under the License.
*/
-package androidx.lifecycle.observers;
-
-import androidx.lifecycle.Lifecycle;
-import androidx.lifecycle.LifecycleObserver;
-
-@SuppressWarnings("deprecation")
-public interface Interface1 extends LifecycleObserver {
-
- @androidx.lifecycle.OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
- void onCreate();
+package foo.bar;
+import androidx.room.*;
+@Entity
+public class Page {
+ @PrimaryKey
+ int pageId;
+ int pBid;
}
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/javac/DeletionDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/javac/DeletionDao.java
index 598d267..31272dc 100644
--- a/room/room-compiler/src/test/test-data/daoWriter/output/javac/DeletionDao.java
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/javac/DeletionDao.java
@@ -43,24 +43,24 @@
this.__deletionAdapterOfUser = new EntityDeletionOrUpdateAdapter<User>(__db) {
@Override
@NonNull
- public String createQuery() {
+ protected String createQuery() {
return "DELETE FROM `User` WHERE `uid` = ?";
}
@Override
- public void bind(@NonNull final SupportSQLiteStatement statement, final User entity) {
+ protected void bind(@NonNull final SupportSQLiteStatement statement, final User entity) {
statement.bindLong(1, entity.uid);
}
};
this.__deletionAdapterOfMultiPKeyEntity = new EntityDeletionOrUpdateAdapter<MultiPKeyEntity>(__db) {
@Override
@NonNull
- public String createQuery() {
+ protected String createQuery() {
return "DELETE FROM `MultiPKeyEntity` WHERE `name` = ? AND `lastName` = ?";
}
@Override
- public void bind(@NonNull final SupportSQLiteStatement statement,
+ protected void bind(@NonNull final SupportSQLiteStatement statement,
final MultiPKeyEntity entity) {
if (entity.name == null) {
statement.bindNull(1);
@@ -77,12 +77,12 @@
this.__deletionAdapterOfBook = new EntityDeletionOrUpdateAdapter<Book>(__db) {
@Override
@NonNull
- public String createQuery() {
+ protected String createQuery() {
return "DELETE FROM `Book` WHERE `bookId` = ?";
}
@Override
- public void bind(@NonNull final SupportSQLiteStatement statement, final Book entity) {
+ protected void bind(@NonNull final SupportSQLiteStatement statement, final Book entity) {
statement.bindLong(1, entity.bookId);
}
};
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/javac/UpdateDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/javac/UpdateDao.java
index e3b6103..5dbb7eb 100644
--- a/room/room-compiler/src/test/test-data/daoWriter/output/javac/UpdateDao.java
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/javac/UpdateDao.java
@@ -43,12 +43,12 @@
this.__updateAdapterOfUser = new EntityDeletionOrUpdateAdapter<User>(__db) {
@Override
@NonNull
- public String createQuery() {
+ protected String createQuery() {
return "UPDATE OR ABORT `User` SET `uid` = ?,`name` = ?,`lastName` = ?,`ageColumn` = ? WHERE `uid` = ?";
}
@Override
- public void bind(@NonNull final SupportSQLiteStatement statement, final User entity) {
+ protected void bind(@NonNull final SupportSQLiteStatement statement, final User entity) {
statement.bindLong(1, entity.uid);
if (entity.name == null) {
statement.bindNull(2);
@@ -67,12 +67,12 @@
this.__updateAdapterOfUser_1 = new EntityDeletionOrUpdateAdapter<User>(__db) {
@Override
@NonNull
- public String createQuery() {
+ protected String createQuery() {
return "UPDATE `User` SET `uid` = ?,`name` = ?,`lastName` = ?,`ageColumn` = ? WHERE `uid` = ?";
}
@Override
- public void bind(@NonNull final SupportSQLiteStatement statement, final User entity) {
+ protected void bind(@NonNull final SupportSQLiteStatement statement, final User entity) {
statement.bindLong(1, entity.uid);
if (entity.name == null) {
statement.bindNull(2);
@@ -91,12 +91,12 @@
this.__updateAdapterOfMultiPKeyEntity = new EntityDeletionOrUpdateAdapter<MultiPKeyEntity>(__db) {
@Override
@NonNull
- public String createQuery() {
+ protected String createQuery() {
return "UPDATE OR ABORT `MultiPKeyEntity` SET `name` = ?,`lastName` = ? WHERE `name` = ? AND `lastName` = ?";
}
@Override
- public void bind(@NonNull final SupportSQLiteStatement statement,
+ protected void bind(@NonNull final SupportSQLiteStatement statement,
final MultiPKeyEntity entity) {
if (entity.name == null) {
statement.bindNull(1);
@@ -123,12 +123,12 @@
this.__updateAdapterOfBook = new EntityDeletionOrUpdateAdapter<Book>(__db) {
@Override
@NonNull
- public String createQuery() {
+ protected String createQuery() {
return "UPDATE OR ABORT `Book` SET `bookId` = ?,`uid` = ? WHERE `bookId` = ?";
}
@Override
- public void bind(@NonNull final SupportSQLiteStatement statement, final Book entity) {
+ protected void bind(@NonNull final SupportSQLiteStatement statement, final Book entity) {
statement.bindLong(1, entity.bookId);
statement.bindLong(2, entity.uid);
statement.bindLong(3, entity.bookId);
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/javac/UpsertDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/javac/UpsertDao.java
index 1e7255b..4d4875a 100644
--- a/room/room-compiler/src/test/test-data/daoWriter/output/javac/UpsertDao.java
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/javac/UpsertDao.java
@@ -28,12 +28,12 @@
this.__upsertionAdapterOfUser = new EntityUpsertionAdapter<User>(new EntityInsertionAdapter<User>(__db) {
@Override
@NonNull
- public String createQuery() {
+ protected String createQuery() {
return "INSERT INTO `User` (`uid`,`name`,`lastName`,`ageColumn`) VALUES (?,?,?,?)";
}
@Override
- public void bind(@NonNull final SupportSQLiteStatement statement, final User entity) {
+ protected void bind(@NonNull final SupportSQLiteStatement statement, final User entity) {
statement.bindLong(1, entity.uid);
if (entity.name == null) {
statement.bindNull(2);
@@ -50,12 +50,12 @@
}, new EntityDeletionOrUpdateAdapter<User>(__db) {
@Override
@NonNull
- public String createQuery() {
+ protected String createQuery() {
return "UPDATE `User` SET `uid` = ?,`name` = ?,`lastName` = ?,`ageColumn` = ? WHERE `uid` = ?";
}
@Override
- public void bind(@NonNull final SupportSQLiteStatement statement, final User entity) {
+ protected void bind(@NonNull final SupportSQLiteStatement statement, final User entity) {
statement.bindLong(1, entity.uid);
if (entity.name == null) {
statement.bindNull(2);
@@ -74,24 +74,24 @@
this.__upsertionAdapterOfBook = new EntityUpsertionAdapter<Book>(new EntityInsertionAdapter<Book>(__db) {
@Override
@NonNull
- public String createQuery() {
+ protected String createQuery() {
return "INSERT INTO `Book` (`bookId`,`uid`) VALUES (?,?)";
}
@Override
- public void bind(@NonNull final SupportSQLiteStatement statement, final Book entity) {
+ protected void bind(@NonNull final SupportSQLiteStatement statement, final Book entity) {
statement.bindLong(1, entity.bookId);
statement.bindLong(2, entity.uid);
}
}, new EntityDeletionOrUpdateAdapter<Book>(__db) {
@Override
@NonNull
- public String createQuery() {
+ protected String createQuery() {
return "UPDATE `Book` SET `bookId` = ?,`uid` = ? WHERE `bookId` = ?";
}
@Override
- public void bind(@NonNull final SupportSQLiteStatement statement, final Book entity) {
+ protected void bind(@NonNull final SupportSQLiteStatement statement, final Book entity) {
statement.bindLong(1, entity.bookId);
statement.bindLong(2, entity.uid);
statement.bindLong(3, entity.bookId);
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/javac/WriterDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/javac/WriterDao.java
index ddbbe73..3287dd2 100644
--- a/room/room-compiler/src/test/test-data/daoWriter/output/javac/WriterDao.java
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/javac/WriterDao.java
@@ -30,12 +30,12 @@
this.__insertionAdapterOfUser = new EntityInsertionAdapter<User>(__db) {
@Override
@NonNull
- public String createQuery() {
+ protected String createQuery() {
return "INSERT OR ABORT INTO `User` (`uid`,`name`,`lastName`,`ageColumn`) VALUES (?,?,?,?)";
}
@Override
- public void bind(@NonNull final SupportSQLiteStatement statement, final User entity) {
+ protected void bind(@NonNull final SupportSQLiteStatement statement, final User entity) {
statement.bindLong(1, entity.uid);
if (entity.name == null) {
statement.bindNull(2);
@@ -53,12 +53,12 @@
this.__insertionAdapterOfUser_1 = new EntityInsertionAdapter<User>(__db) {
@Override
@NonNull
- public String createQuery() {
+ protected String createQuery() {
return "INSERT OR REPLACE INTO `User` (`uid`,`name`,`lastName`,`ageColumn`) VALUES (?,?,?,?)";
}
@Override
- public void bind(@NonNull final SupportSQLiteStatement statement, final User entity) {
+ protected void bind(@NonNull final SupportSQLiteStatement statement, final User entity) {
statement.bindLong(1, entity.uid);
if (entity.name == null) {
statement.bindNull(2);
@@ -76,12 +76,12 @@
this.__insertionAdapterOfUser_2 = new EntityInsertionAdapter<User>(__db) {
@Override
@NonNull
- public String createQuery() {
+ protected String createQuery() {
return "INSERT INTO `User` (`uid`,`name`,`lastName`,`ageColumn`) VALUES (?,?,?,?)";
}
@Override
- public void bind(@NonNull final SupportSQLiteStatement statement, final User entity) {
+ protected void bind(@NonNull final SupportSQLiteStatement statement, final User entity) {
statement.bindLong(1, entity.uid);
if (entity.name == null) {
statement.bindNull(2);
@@ -99,12 +99,12 @@
this.__insertionAdapterOfBook = new EntityInsertionAdapter<Book>(__db) {
@Override
@NonNull
- public String createQuery() {
+ protected String createQuery() {
return "INSERT OR ABORT INTO `Book` (`bookId`,`uid`) VALUES (?,?)";
}
@Override
- public void bind(@NonNull final SupportSQLiteStatement statement, final Book entity) {
+ protected void bind(@NonNull final SupportSQLiteStatement statement, final Book entity) {
statement.bindLong(1, entity.bookId);
statement.bindLong(2, entity.uid);
}
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/ksp/DeletionDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/ksp/DeletionDao.java
index 4fa7155..45fee3b 100644
--- a/room/room-compiler/src/test/test-data/daoWriter/output/ksp/DeletionDao.java
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/ksp/DeletionDao.java
@@ -43,12 +43,12 @@
this.__deletionAdapterOfUser = new EntityDeletionOrUpdateAdapter<User>(__db) {
@Override
@NonNull
- public String createQuery() {
+ protected String createQuery() {
return "DELETE FROM `User` WHERE `uid` = ?";
}
@Override
- public void bind(@NonNull final SupportSQLiteStatement statement,
+ protected void bind(@NonNull final SupportSQLiteStatement statement,
@NonNull final User entity) {
statement.bindLong(1, entity.uid);
}
@@ -56,12 +56,12 @@
this.__deletionAdapterOfMultiPKeyEntity = new EntityDeletionOrUpdateAdapter<MultiPKeyEntity>(__db) {
@Override
@NonNull
- public String createQuery() {
+ protected String createQuery() {
return "DELETE FROM `MultiPKeyEntity` WHERE `name` = ? AND `lastName` = ?";
}
@Override
- public void bind(@NonNull final SupportSQLiteStatement statement,
+ protected void bind(@NonNull final SupportSQLiteStatement statement,
@NonNull final MultiPKeyEntity entity) {
statement.bindString(1, entity.name);
statement.bindString(2, entity.lastName);
@@ -70,12 +70,12 @@
this.__deletionAdapterOfBook = new EntityDeletionOrUpdateAdapter<Book>(__db) {
@Override
@NonNull
- public String createQuery() {
+ protected String createQuery() {
return "DELETE FROM `Book` WHERE `bookId` = ?";
}
@Override
- public void bind(@NonNull final SupportSQLiteStatement statement,
+ protected void bind(@NonNull final SupportSQLiteStatement statement,
@NonNull final Book entity) {
statement.bindLong(1, entity.bookId);
}
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/ksp/UpdateDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/ksp/UpdateDao.java
index 571e245..1fb23be 100644
--- a/room/room-compiler/src/test/test-data/daoWriter/output/ksp/UpdateDao.java
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/ksp/UpdateDao.java
@@ -43,12 +43,12 @@
this.__updateAdapterOfUser = new EntityDeletionOrUpdateAdapter<User>(__db) {
@Override
@NonNull
- public String createQuery() {
+ protected String createQuery() {
return "UPDATE OR ABORT `User` SET `uid` = ?,`name` = ?,`lastName` = ?,`ageColumn` = ? WHERE `uid` = ?";
}
@Override
- public void bind(@NonNull final SupportSQLiteStatement statement,
+ protected void bind(@NonNull final SupportSQLiteStatement statement,
@NonNull final User entity) {
statement.bindLong(1, entity.uid);
statement.bindString(2, entity.name);
@@ -60,12 +60,12 @@
this.__updateAdapterOfUser_1 = new EntityDeletionOrUpdateAdapter<User>(__db) {
@Override
@NonNull
- public String createQuery() {
+ protected String createQuery() {
return "UPDATE `User` SET `uid` = ?,`name` = ?,`lastName` = ?,`ageColumn` = ? WHERE `uid` = ?";
}
@Override
- public void bind(@NonNull final SupportSQLiteStatement statement,
+ protected void bind(@NonNull final SupportSQLiteStatement statement,
@NonNull final User entity) {
statement.bindLong(1, entity.uid);
statement.bindString(2, entity.name);
@@ -77,12 +77,12 @@
this.__updateAdapterOfMultiPKeyEntity = new EntityDeletionOrUpdateAdapter<MultiPKeyEntity>(__db) {
@Override
@NonNull
- public String createQuery() {
+ protected String createQuery() {
return "UPDATE OR ABORT `MultiPKeyEntity` SET `name` = ?,`lastName` = ? WHERE `name` = ? AND `lastName` = ?";
}
@Override
- public void bind(@NonNull final SupportSQLiteStatement statement,
+ protected void bind(@NonNull final SupportSQLiteStatement statement,
@NonNull final MultiPKeyEntity entity) {
statement.bindString(1, entity.name);
statement.bindString(2, entity.lastName);
@@ -93,12 +93,12 @@
this.__updateAdapterOfBook = new EntityDeletionOrUpdateAdapter<Book>(__db) {
@Override
@NonNull
- public String createQuery() {
+ protected String createQuery() {
return "UPDATE OR ABORT `Book` SET `bookId` = ?,`uid` = ? WHERE `bookId` = ?";
}
@Override
- public void bind(@NonNull final SupportSQLiteStatement statement,
+ protected void bind(@NonNull final SupportSQLiteStatement statement,
@NonNull final Book entity) {
statement.bindLong(1, entity.bookId);
statement.bindLong(2, entity.uid);
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/ksp/UpsertDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/ksp/UpsertDao.java
index 8a1374c..80b1ba9 100644
--- a/room/room-compiler/src/test/test-data/daoWriter/output/ksp/UpsertDao.java
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/ksp/UpsertDao.java
@@ -28,12 +28,12 @@
this.__upsertionAdapterOfUser = new EntityUpsertionAdapter<User>(new EntityInsertionAdapter<User>(__db) {
@Override
@NonNull
- public String createQuery() {
+ protected String createQuery() {
return "INSERT INTO `User` (`uid`,`name`,`lastName`,`ageColumn`) VALUES (?,?,?,?)";
}
@Override
- public void bind(@NonNull final SupportSQLiteStatement statement,
+ protected void bind(@NonNull final SupportSQLiteStatement statement,
@NonNull final User entity) {
statement.bindLong(1, entity.uid);
statement.bindString(2, entity.name);
@@ -43,12 +43,12 @@
}, new EntityDeletionOrUpdateAdapter<User>(__db) {
@Override
@NonNull
- public String createQuery() {
+ protected String createQuery() {
return "UPDATE `User` SET `uid` = ?,`name` = ?,`lastName` = ?,`ageColumn` = ? WHERE `uid` = ?";
}
@Override
- public void bind(@NonNull final SupportSQLiteStatement statement,
+ protected void bind(@NonNull final SupportSQLiteStatement statement,
@NonNull final User entity) {
statement.bindLong(1, entity.uid);
statement.bindString(2, entity.name);
@@ -60,12 +60,12 @@
this.__upsertionAdapterOfBook = new EntityUpsertionAdapter<Book>(new EntityInsertionAdapter<Book>(__db) {
@Override
@NonNull
- public String createQuery() {
+ protected String createQuery() {
return "INSERT INTO `Book` (`bookId`,`uid`) VALUES (?,?)";
}
@Override
- public void bind(@NonNull final SupportSQLiteStatement statement,
+ protected void bind(@NonNull final SupportSQLiteStatement statement,
@NonNull final Book entity) {
statement.bindLong(1, entity.bookId);
statement.bindLong(2, entity.uid);
@@ -73,12 +73,12 @@
}, new EntityDeletionOrUpdateAdapter<Book>(__db) {
@Override
@NonNull
- public String createQuery() {
+ protected String createQuery() {
return "UPDATE `Book` SET `bookId` = ?,`uid` = ? WHERE `bookId` = ?";
}
@Override
- public void bind(@NonNull final SupportSQLiteStatement statement,
+ protected void bind(@NonNull final SupportSQLiteStatement statement,
@NonNull final Book entity) {
statement.bindLong(1, entity.bookId);
statement.bindLong(2, entity.uid);
diff --git a/room/room-compiler/src/test/test-data/daoWriter/output/ksp/WriterDao.java b/room/room-compiler/src/test/test-data/daoWriter/output/ksp/WriterDao.java
index 54443ae..a0601f1 100644
--- a/room/room-compiler/src/test/test-data/daoWriter/output/ksp/WriterDao.java
+++ b/room/room-compiler/src/test/test-data/daoWriter/output/ksp/WriterDao.java
@@ -30,12 +30,12 @@
this.__insertionAdapterOfUser = new EntityInsertionAdapter<User>(__db) {
@Override
@NonNull
- public String createQuery() {
+ protected String createQuery() {
return "INSERT OR ABORT INTO `User` (`uid`,`name`,`lastName`,`ageColumn`) VALUES (?,?,?,?)";
}
@Override
- public void bind(@NonNull final SupportSQLiteStatement statement,
+ protected void bind(@NonNull final SupportSQLiteStatement statement,
@NonNull final User entity) {
statement.bindLong(1, entity.uid);
statement.bindString(2, entity.name);
@@ -46,12 +46,12 @@
this.__insertionAdapterOfUser_1 = new EntityInsertionAdapter<User>(__db) {
@Override
@NonNull
- public String createQuery() {
+ protected String createQuery() {
return "INSERT OR REPLACE INTO `User` (`uid`,`name`,`lastName`,`ageColumn`) VALUES (?,?,?,?)";
}
@Override
- public void bind(@NonNull final SupportSQLiteStatement statement,
+ protected void bind(@NonNull final SupportSQLiteStatement statement,
@NonNull final User entity) {
statement.bindLong(1, entity.uid);
statement.bindString(2, entity.name);
@@ -62,12 +62,12 @@
this.__insertionAdapterOfUser_2 = new EntityInsertionAdapter<User>(__db) {
@Override
@NonNull
- public String createQuery() {
+ protected String createQuery() {
return "INSERT INTO `User` (`uid`,`name`,`lastName`,`ageColumn`) VALUES (?,?,?,?)";
}
@Override
- public void bind(@NonNull final SupportSQLiteStatement statement,
+ protected void bind(@NonNull final SupportSQLiteStatement statement,
@NonNull final User entity) {
statement.bindLong(1, entity.uid);
statement.bindString(2, entity.name);
@@ -78,12 +78,12 @@
this.__insertionAdapterOfBook = new EntityInsertionAdapter<Book>(__db) {
@Override
@NonNull
- public String createQuery() {
+ protected String createQuery() {
return "INSERT OR ABORT INTO `Book` (`bookId`,`uid`) VALUES (?,?)";
}
@Override
- public void bind(@NonNull final SupportSQLiteStatement statement,
+ protected void bind(@NonNull final SupportSQLiteStatement statement,
@NonNull final Book entity) {
statement.bindLong(1, entity.bookId);
statement.bindLong(2, entity.uid);
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/abstractClassWithParam.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/abstractClassWithParam.kt
index d306558..1caa291 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/abstractClassWithParam.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/abstractClassWithParam.kt
@@ -35,7 +35,7 @@
_tmpPk = _cursor.getInt(_cursorIndexOfPk)
_result = MyEntity(_tmpPk)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/arrayParameterAdapter.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/arrayParameterAdapter.kt
index d415b37..27b158e 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/arrayParameterAdapter.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/arrayParameterAdapter.kt
@@ -51,7 +51,7 @@
_tmpId = _cursor.getString(_cursorIndexOfId)
_result = MyEntity(_tmpId)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
@@ -88,7 +88,7 @@
_tmpId = _cursor.getString(_cursorIndexOfId)
_result = MyEntity(_tmpId)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
@@ -125,7 +125,7 @@
_tmpId = _cursor.getString(_cursorIndexOfId)
_result = MyEntity(_tmpId)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
@@ -158,7 +158,7 @@
_tmpId = _cursor.getString(_cursorIndexOfId)
_result = MyEntity(_tmpId)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
@@ -195,7 +195,7 @@
_tmpId = _cursor.getString(_cursorIndexOfId)
_result = MyEntity(_tmpId)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
@@ -228,7 +228,7 @@
_tmpId = _cursor.getString(_cursorIndexOfId)
_result = MyEntity(_tmpId)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
@@ -265,7 +265,7 @@
_tmpId = _cursor.getString(_cursorIndexOfId)
_result = MyEntity(_tmpId)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/basicParameterAdapter_string.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/basicParameterAdapter_string.kt
index 3b22d23..a8234c8 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/basicParameterAdapter_string.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/basicParameterAdapter_string.kt
@@ -37,7 +37,7 @@
_tmpString = _cursor.getString(_cursorIndexOfString)
_result = MyEntity(_tmpString)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
@@ -65,7 +65,7 @@
_tmpString = _cursor.getString(_cursorIndexOfString)
_result = MyEntity(_tmpString)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/callableQuery_rx2.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/callableQuery_rx2.kt
index fd018379..7cfc96d 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/callableQuery_rx2.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/callableQuery_rx2.kt
@@ -64,7 +64,7 @@
_tmpOther = _cursor.getString(_cursorIndexOfOther)
_result = MyEntity(_tmpPk,_tmpOther)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
@@ -110,7 +110,7 @@
_tmpOther = _cursor.getString(_cursorIndexOfOther)
_result = MyEntity(_tmpPk,_tmpOther)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/callableQuery_rx3.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/callableQuery_rx3.kt
index 48492a3..bbd0db3 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/callableQuery_rx3.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/callableQuery_rx3.kt
@@ -64,7 +64,7 @@
_tmpOther = _cursor.getString(_cursorIndexOfOther)
_result = MyEntity(_tmpPk,_tmpOther)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
@@ -110,7 +110,7 @@
_tmpOther = _cursor.getString(_cursorIndexOfOther)
_result = MyEntity(_tmpPk,_tmpOther)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/collectionParameterAdapter_string.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/collectionParameterAdapter_string.kt
index 1d1a468..3c8a547 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/collectionParameterAdapter_string.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/collectionParameterAdapter_string.kt
@@ -50,7 +50,7 @@
_tmpString = _cursor.getString(_cursorIndexOfString)
_result = MyEntity(_tmpString)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
@@ -87,7 +87,7 @@
_tmpString = _cursor.getString(_cursorIndexOfString)
_result = MyEntity(_tmpString)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
@@ -124,7 +124,7 @@
_tmpString = _cursor.getString(_cursorIndexOfString)
_result = MyEntity(_tmpString)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
@@ -157,7 +157,7 @@
_tmpString = _cursor.getString(_cursorIndexOfString)
_result = MyEntity(_tmpString)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/coroutineResultBinder.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/coroutineResultBinder.kt
index b959c1c..ef15633 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/coroutineResultBinder.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/coroutineResultBinder.kt
@@ -41,7 +41,7 @@
_tmpPk = _cursor.getInt(_cursorIndexOfPk)
_result = MyEntity(_tmpPk)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/coroutines.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/coroutines.kt
index 3cf1b4c..e09976c 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/coroutines.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/coroutines.kt
@@ -65,7 +65,7 @@
_tmpOther = _cursor.getString(_cursorIndexOfOther)
_result = MyEntity(_tmpPk,_tmpOther)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/delegatingFunctions_boxedPrimitiveBridge.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/delegatingFunctions_boxedPrimitiveBridge.kt
index e7ff72b..2f5a0cc 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/delegatingFunctions_boxedPrimitiveBridge.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/delegatingFunctions_boxedPrimitiveBridge.kt
@@ -67,7 +67,7 @@
_tmpPk = _cursor.getLong(_cursorIndexOfPk)
_result = MyEntity(_tmpPk)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/delegatingFunctions_defaultImplBridge.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/delegatingFunctions_defaultImplBridge.kt
index f2b5842..a2792d8 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/delegatingFunctions_defaultImplBridge.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/delegatingFunctions_defaultImplBridge.kt
@@ -36,7 +36,7 @@
_tmpPk = _cursor.getLong(_cursorIndexOfPk)
_result = MyEntity(_tmpPk)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/deleteOrUpdateMethodAdapter.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/deleteOrUpdateMethodAdapter.kt
index 233b23a..890f85e 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/deleteOrUpdateMethodAdapter.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/deleteOrUpdateMethodAdapter.kt
@@ -22,17 +22,17 @@
init {
this.__db = __db
this.__deletionAdapterOfMyEntity = object : EntityDeletionOrUpdateAdapter<MyEntity>(__db) {
- public override fun createQuery(): String = "DELETE FROM `MyEntity` WHERE `pk` = ?"
+ protected override fun createQuery(): String = "DELETE FROM `MyEntity` WHERE `pk` = ?"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
statement.bindLong(1, entity.pk)
}
}
this.__updateAdapterOfMyEntity = object : EntityDeletionOrUpdateAdapter<MyEntity>(__db) {
- public override fun createQuery(): String =
+ protected override fun createQuery(): String =
"UPDATE OR ABORT `MyEntity` SET `pk` = ?,`data` = ? WHERE `pk` = ?"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
statement.bindLong(1, entity.pk)
statement.bindString(2, entity.data)
statement.bindLong(3, entity.pk)
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/entityRowAdapter.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/entityRowAdapter.kt
index d00f043..bbc3dc0 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/entityRowAdapter.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/entityRowAdapter.kt
@@ -27,10 +27,10 @@
init {
this.__db = __db
this.__insertionAdapterOfMyEntity = object : EntityInsertionAdapter<MyEntity>(__db) {
- public override fun createQuery(): String =
+ protected override fun createQuery(): String =
"INSERT OR ABORT INTO `MyEntity` (`valuePrimitive`,`valueBoolean`,`valueString`,`valueNullableString`,`variablePrimitive`,`variableNullableBoolean`,`variableString`,`variableNullableString`) VALUES (?,?,?,?,?,?,?,?)"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
statement.bindLong(1, entity.valuePrimitive)
val _tmp: Int = if (entity.valueBoolean) 1 else 0
statement.bindLong(2, _tmp.toLong())
@@ -81,7 +81,7 @@
if (_cursor.moveToFirst()) {
_result = __entityCursorConverter_MyEntity(_cursor)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
@@ -117,7 +117,7 @@
}
val _tmpValueString: String
if (_cursorIndexOfValueString == -1) {
- error("Missing column 'valueString' for a non null value.")
+ error("Missing value for a NON-NULL column 'valueString', found NULL value instead.")
} else {
_tmpValueString = cursor.getString(_cursorIndexOfValueString)
}
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/guavaCallable.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/guavaCallable.kt
index 3d51fc4..70371f9 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/guavaCallable.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/guavaCallable.kt
@@ -42,26 +42,26 @@
init {
this.__db = __db
this.__insertionAdapterOfMyEntity = object : EntityInsertionAdapter<MyEntity>(__db) {
- public override fun createQuery(): String =
+ protected override fun createQuery(): String =
"INSERT OR ABORT INTO `MyEntity` (`pk`,`other`) VALUES (?,?)"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
statement.bindLong(1, entity.pk.toLong())
statement.bindString(2, entity.other)
}
}
this.__deletionAdapterOfMyEntity = object : EntityDeletionOrUpdateAdapter<MyEntity>(__db) {
- public override fun createQuery(): String = "DELETE FROM `MyEntity` WHERE `pk` = ?"
+ protected override fun createQuery(): String = "DELETE FROM `MyEntity` WHERE `pk` = ?"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
statement.bindLong(1, entity.pk.toLong())
}
}
this.__updateAdapterOfMyEntity = object : EntityDeletionOrUpdateAdapter<MyEntity>(__db) {
- public override fun createQuery(): String =
+ protected override fun createQuery(): String =
"UPDATE OR ABORT `MyEntity` SET `pk` = ?,`other` = ? WHERE `pk` = ?"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
statement.bindLong(1, entity.pk.toLong())
statement.bindString(2, entity.other)
statement.bindLong(3, entity.pk.toLong())
@@ -69,18 +69,18 @@
}
this.__upsertionAdapterOfMyEntity = EntityUpsertionAdapter<MyEntity>(object :
EntityInsertionAdapter<MyEntity>(__db) {
- public override fun createQuery(): String =
+ protected override fun createQuery(): String =
"INSERT INTO `MyEntity` (`pk`,`other`) VALUES (?,?)"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
statement.bindLong(1, entity.pk.toLong())
statement.bindString(2, entity.other)
}
}, object : EntityDeletionOrUpdateAdapter<MyEntity>(__db) {
- public override fun createQuery(): String =
+ protected override fun createQuery(): String =
"UPDATE `MyEntity` SET `pk` = ?,`other` = ? WHERE `pk` = ?"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
statement.bindLong(1, entity.pk.toLong())
statement.bindString(2, entity.other)
statement.bindLong(3, entity.pk.toLong())
@@ -181,7 +181,7 @@
_tmpOther = _cursor.getString(_cursorIndexOfOther)
_result = MyEntity(_tmpPk,_tmpOther)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/insertOrUpsertMethodAdapter.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/insertOrUpsertMethodAdapter.kt
index 246af07..f7ce132 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/insertOrUpsertMethodAdapter.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/insertOrUpsertMethodAdapter.kt
@@ -25,28 +25,28 @@
init {
this.__db = __db
this.__insertionAdapterOfMyEntity = object : EntityInsertionAdapter<MyEntity>(__db) {
- public override fun createQuery(): String =
+ protected override fun createQuery(): String =
"INSERT OR ABORT INTO `MyEntity` (`pk`,`data`) VALUES (?,?)"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
statement.bindLong(1, entity.pk)
statement.bindString(2, entity.data)
}
}
this.__upsertionAdapterOfMyEntity = EntityUpsertionAdapter<MyEntity>(object :
EntityInsertionAdapter<MyEntity>(__db) {
- public override fun createQuery(): String =
+ protected override fun createQuery(): String =
"INSERT INTO `MyEntity` (`pk`,`data`) VALUES (?,?)"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
statement.bindLong(1, entity.pk)
statement.bindString(2, entity.data)
}
}, object : EntityDeletionOrUpdateAdapter<MyEntity>(__db) {
- public override fun createQuery(): String =
+ protected override fun createQuery(): String =
"UPDATE `MyEntity` SET `pk` = ?,`data` = ? WHERE `pk` = ?"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
statement.bindLong(1, entity.pk)
statement.bindString(2, entity.data)
statement.bindLong(3, entity.pk)
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_boolean.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_boolean.kt
index 0c57f0e..f587b69 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_boolean.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_boolean.kt
@@ -26,10 +26,10 @@
init {
this.__db = __db
this.__insertionAdapterOfMyEntity = object : EntityInsertionAdapter<MyEntity>(__db) {
- public override fun createQuery(): String =
+ protected override fun createQuery(): String =
"INSERT OR ABORT INTO `MyEntity` (`pk`,`boolean`,`nullableBoolean`) VALUES (?,?,?)"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
statement.bindLong(1, entity.pk.toLong())
val _tmp: Int = if (entity.boolean) 1 else 0
statement.bindLong(2, _tmp.toLong())
@@ -82,7 +82,7 @@
_tmpNullableBoolean = _tmp_1?.let { it != 0 }
_result = MyEntity(_tmpPk,_tmpBoolean,_tmpNullableBoolean)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_byteArray.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_byteArray.kt
index f369507..5c83c9e 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_byteArray.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_byteArray.kt
@@ -26,10 +26,10 @@
init {
this.__db = __db
this.__insertionAdapterOfMyEntity = object : EntityInsertionAdapter<MyEntity>(__db) {
- public override fun createQuery(): String =
+ protected override fun createQuery(): String =
"INSERT OR ABORT INTO `MyEntity` (`pk`,`byteArray`,`nullableByteArray`) VALUES (?,?,?)"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
statement.bindLong(1, entity.pk.toLong())
statement.bindBlob(2, entity.byteArray)
val _tmpNullableByteArray: ByteArray? = entity.nullableByteArray
@@ -76,7 +76,7 @@
}
_result = MyEntity(_tmpPk,_tmpByteArray,_tmpNullableByteArray)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter.kt
index 6e7794e..e509e2e 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter.kt
@@ -27,10 +27,10 @@
init {
this.__db = __db
this.__insertionAdapterOfMyEntity = object : EntityInsertionAdapter<MyEntity>(__db) {
- public override fun createQuery(): String =
+ protected override fun createQuery(): String =
"INSERT OR ABORT INTO `MyEntity` (`pk`,`foo`) VALUES (?,?)"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
statement.bindLong(1, entity.pk.toLong())
val _tmp: String = __fooConverter.toString(entity.foo)
statement.bindString(2, _tmp)
@@ -67,7 +67,7 @@
_tmpFoo = __fooConverter.fromString(_tmp)
_result = MyEntity(_tmpPk,_tmpFoo)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter_composite.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter_composite.kt
index 96566e6..d88a2e0 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter_composite.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter_composite.kt
@@ -25,10 +25,10 @@
init {
this.__db = __db
this.__insertionAdapterOfMyEntity = object : EntityInsertionAdapter<MyEntity>(__db) {
- public override fun createQuery(): String =
+ protected override fun createQuery(): String =
"INSERT OR ABORT INTO `MyEntity` (`pk`,`bar`) VALUES (?,?)"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
statement.bindLong(1, entity.pk.toLong())
val _tmp: Foo = FooBarConverter.toFoo(entity.bar)
val _tmp_1: String = FooBarConverter.toString(_tmp)
@@ -67,7 +67,7 @@
_tmpBar = FooBarConverter.fromFoo(_tmp_1)
_result = MyEntity(_tmpPk,_tmpBar)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter_internalVisibility.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter_internalVisibility.kt
index 0ba6f82..d5da42b 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter_internalVisibility.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter_internalVisibility.kt
@@ -27,10 +27,10 @@
init {
this.__db = __db
this.__insertionAdapterOfMyEntity = object : EntityInsertionAdapter<MyEntity>(__db) {
- public override fun createQuery(): String =
+ protected override fun createQuery(): String =
"INSERT OR ABORT INTO `MyEntity` (`pk`,`foo`) VALUES (?,?)"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
statement.bindLong(1, entity.pk.toLong())
val _tmp: String = __fooConverter.toString(entity.foo)
statement.bindString(2, _tmp)
@@ -67,7 +67,7 @@
_tmpFoo = __fooConverter.fromString(_tmp)
_result = MyEntity(_tmpPk,_tmpFoo)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter_nullAware.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter_nullAware.kt
index 0ea0420..5480240 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter_nullAware.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter_nullAware.kt
@@ -25,10 +25,10 @@
init {
this.__db = __db
this.__insertionAdapterOfMyEntity = object : EntityInsertionAdapter<MyEntity>(__db) {
- public override fun createQuery(): String =
+ protected override fun createQuery(): String =
"INSERT OR ABORT INTO `MyEntity` (`pk`,`foo`,`bar`) VALUES (?,?,?)"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
statement.bindLong(1, entity.pk.toLong())
val _tmp: String? = FooBarConverter.toString(entity.foo)
if (_tmp == null) {
@@ -80,7 +80,7 @@
}
val _tmp_1: Foo? = FooBarConverter.fromString(_tmp)
if (_tmp_1 == null) {
- error("Expected non-null Foo, but it was null.")
+ error("Expected NON-NULL 'Foo', but it was NULL.")
} else {
_tmpFoo = _tmp_1
}
@@ -99,13 +99,13 @@
_tmp_4 = FooBarConverter.fromFoo(_tmp_3)
}
if (_tmp_4 == null) {
- error("Expected non-null Bar, but it was null.")
+ error("Expected NON-NULL 'Bar', but it was NULL.")
} else {
_tmpBar = _tmp_4
}
_result = MyEntity(_tmpPk,_tmpFoo,_tmpBar)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter_provided.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter_provided.kt
index 3895395..e6115de 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter_provided.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_customTypeConverter_provided.kt
@@ -31,10 +31,10 @@
init {
this.__db = __db
this.__insertionAdapterOfMyEntity = object : EntityInsertionAdapter<MyEntity>(__db) {
- public override fun createQuery(): String =
+ protected override fun createQuery(): String =
"INSERT OR ABORT INTO `MyEntity` (`pk`,`foo`) VALUES (?,?)"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
statement.bindLong(1, entity.pk.toLong())
val _tmp: String = __fooConverter().toString(entity.foo)
statement.bindString(2, _tmp)
@@ -71,7 +71,7 @@
_tmpFoo = __fooConverter().fromString(_tmp)
_result = MyEntity(_tmpPk,_tmpFoo)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_embedded.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_embedded.kt
index b9a01bc..6a0e2c6 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_embedded.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_embedded.kt
@@ -26,10 +26,10 @@
init {
this.__db = __db
this.__insertionAdapterOfMyEntity = object : EntityInsertionAdapter<MyEntity>(__db) {
- public override fun createQuery(): String =
+ protected override fun createQuery(): String =
"INSERT OR ABORT INTO `MyEntity` (`pk`,`numberData`,`stringData`,`nullablenumberData`,`nullablestringData`) VALUES (?,?,?,?,?)"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
statement.bindLong(1, entity.pk.toLong())
val _tmpFoo: Foo = entity.foo
statement.bindLong(2, _tmpFoo.numberData)
@@ -91,7 +91,7 @@
}
_result = MyEntity(_tmpPk,_tmpFoo,_tmpNullableFoo)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_enum.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_enum.kt
index a6f83a7..7dc5a23 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_enum.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_enum.kt
@@ -26,10 +26,10 @@
init {
this.__db = __db
this.__insertionAdapterOfMyEntity = object : EntityInsertionAdapter<MyEntity>(__db) {
- public override fun createQuery(): String =
+ protected override fun createQuery(): String =
"INSERT OR ABORT INTO `MyEntity` (`pk`,`enum`,`nullableEnum`) VALUES (?,?,?)"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
statement.bindLong(1, entity.pk.toLong())
statement.bindString(2, __Fruit_enumToString(entity.enum))
val _tmpNullableEnum: Fruit? = entity.nullableEnum
@@ -76,7 +76,7 @@
}
_result = MyEntity(_tmpPk,_tmpEnum,_tmpNullableEnum)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_internalVisibility.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_internalVisibility.kt
index db1f98c..3b6eb74 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_internalVisibility.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_internalVisibility.kt
@@ -26,10 +26,10 @@
init {
this.__db = __db
this.__insertionAdapterOfMyEntity = object : EntityInsertionAdapter<MyEntity>(__db) {
- public override fun createQuery(): String =
+ protected override fun createQuery(): String =
"INSERT OR ABORT INTO `MyEntity` (`pk`,`internalVal`,`internalVar`,`internalSetterVar`) VALUES (?,?,?,?)"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
statement.bindLong(1, entity.pk.toLong())
statement.bindLong(2, entity.internalVal)
statement.bindLong(3, entity.internalVar)
@@ -69,7 +69,7 @@
_result.internalVar = _cursor.getLong(_cursorIndexOfInternalVar)
_result.internalSetterVar = _cursor.getLong(_cursorIndexOfInternalSetterVar)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_otherModule.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_otherModule.kt
index 320a33b..138324c 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_otherModule.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_otherModule.kt
@@ -26,10 +26,10 @@
init {
this.__db = __db
this.__insertionAdapterOfMyEntity = object : EntityInsertionAdapter<MyEntity>(__db) {
- public override fun createQuery(): String =
+ protected override fun createQuery(): String =
"INSERT OR ABORT INTO `MyEntity` (`pk`,`primitive`,`string`,`nullableString`,`fieldString`,`nullableFieldString`,`variablePrimitive`,`variableString`,`variableNullableString`,`variableFieldString`,`variableNullableFieldString`) VALUES (?,?,?,?,?,?,?,?,?,?,?)"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
statement.bindLong(1, entity.pk.toLong())
statement.bindLong(2, entity.primitive)
statement.bindString(3, entity.string)
@@ -136,7 +136,7 @@
_cursor.getString(_cursorIndexOfVariableNullableFieldString)
}
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_primitives.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_primitives.kt
index 520ecbb..45ad899 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_primitives.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_primitives.kt
@@ -31,10 +31,10 @@
init {
this.__db = __db
this.__insertionAdapterOfMyEntity = object : EntityInsertionAdapter<MyEntity>(__db) {
- public override fun createQuery(): String =
+ protected override fun createQuery(): String =
"INSERT OR ABORT INTO `MyEntity` (`int`,`short`,`byte`,`long`,`char`,`float`,`double`) VALUES (?,?,?,?,?,?,?)"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
statement.bindLong(1, entity.int.toLong())
statement.bindLong(2, entity.short.toLong())
statement.bindLong(3, entity.byte.toLong())
@@ -88,7 +88,7 @@
_tmpDouble = _cursor.getDouble(_cursorIndexOfDouble)
_result = MyEntity(_tmpInt,_tmpShort,_tmpByte,_tmpLong,_tmpChar,_tmpFloat,_tmpDouble)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_primitives_nullable.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_primitives_nullable.kt
index 109df70..a970c59 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_primitives_nullable.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_primitives_nullable.kt
@@ -31,10 +31,10 @@
init {
this.__db = __db
this.__insertionAdapterOfMyEntity = object : EntityInsertionAdapter<MyEntity>(__db) {
- public override fun createQuery(): String =
+ protected override fun createQuery(): String =
"INSERT OR ABORT INTO `MyEntity` (`int`,`short`,`byte`,`long`,`char`,`float`,`double`) VALUES (?,?,?,?,?,?,?)"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
val _tmpInt: Int? = entity.int
if (_tmpInt == null) {
statement.bindNull(1)
@@ -151,7 +151,7 @@
}
_result = MyEntity(_tmpInt,_tmpShort,_tmpByte,_tmpLong,_tmpChar,_tmpFloat,_tmpDouble)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_string.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_string.kt
index 213539a..982d042 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_string.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_string.kt
@@ -25,10 +25,10 @@
init {
this.__db = __db
this.__insertionAdapterOfMyEntity = object : EntityInsertionAdapter<MyEntity>(__db) {
- public override fun createQuery(): String =
+ protected override fun createQuery(): String =
"INSERT OR ABORT INTO `MyEntity` (`string`,`nullableString`) VALUES (?,?)"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
statement.bindString(1, entity.string)
val _tmpNullableString: String? = entity.nullableString
if (_tmpNullableString == null) {
@@ -71,7 +71,7 @@
}
_result = MyEntity(_tmpString,_tmpNullableString)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_uuid.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_uuid.kt
index a4770df..27d03c4 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_uuid.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_uuid.kt
@@ -28,10 +28,10 @@
init {
this.__db = __db
this.__insertionAdapterOfMyEntity = object : EntityInsertionAdapter<MyEntity>(__db) {
- public override fun createQuery(): String =
+ protected override fun createQuery(): String =
"INSERT OR ABORT INTO `MyEntity` (`pk`,`uuid`,`nullableUuid`) VALUES (?,?,?)"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
statement.bindLong(1, entity.pk.toLong())
statement.bindBlob(2, convertUUIDToByte(entity.uuid))
val _tmpNullableUuid: UUID? = entity.nullableUuid
@@ -78,7 +78,7 @@
}
_result = MyEntity(_tmpPk,_tmpUuid,_tmpNullableUuid)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_valueClassConverter.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_valueClassConverter.kt
index 6ea9026..51844206 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_valueClassConverter.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_valueClassConverter.kt
@@ -29,15 +29,17 @@
init {
this.__db = __db
this.__insertionAdapterOfMyEntity = object : EntityInsertionAdapter<MyEntity>(__db) {
- public override fun createQuery(): String =
+ protected override fun createQuery(): String =
"INSERT OR ABORT INTO `MyEntity` (`pk`,`uuidData`,`nullableUuidData`,`nullableLongData`,`doubleNullableLongData`,`genericData`) VALUES (?,?,?,?,?,?)"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
val _data: Long = checkNotNull(entity.pk.data) {
- "Cannot bind nullable value of inline class to a NOT NULL column." }
+ "Cannot bind NULLABLE value 'data' of inline class 'LongValueClass' to a NOT NULL column."
+ }
statement.bindLong(1, _data)
val _data_1: UUID = checkNotNull(entity.uuidData.data) {
- "Cannot bind nullable value of inline class to a NOT NULL column." }
+ "Cannot bind NULLABLE value 'data' of inline class 'UUIDValueClass' to a NOT NULL column."
+ }
statement.bindBlob(2, convertUUIDToByte(_data_1))
val _tmpNullableUuidData: UUIDValueClass? = entity.nullableUuidData
val _data_2: UUID? = _tmpNullableUuidData?.data
@@ -47,7 +49,8 @@
statement.bindBlob(3, convertUUIDToByte(_data_2))
}
val _data_3: Long = checkNotNull(entity.nullableLongData.data) {
- "Cannot bind nullable value of inline class to a NOT NULL column." }
+ "Cannot bind NULLABLE value 'data' of inline class 'NullableLongValueClass' to a NOT NULL column."
+ }
statement.bindLong(4, _data_3)
val _tmpDoubleNullableLongData: NullableLongValueClass? = entity.doubleNullableLongData
val _data_4: Long? = _tmpDoubleNullableLongData?.data
@@ -57,7 +60,8 @@
statement.bindLong(5, _data_4)
}
val _password: String = checkNotNull(entity.genericData.password) {
- "Cannot bind nullable value of inline class to a NOT NULL column." }
+ "Cannot bind NULLABLE value 'password' of inline class 'GenericValueClass<String>' to a NOT NULL column."
+ }
statement.bindString(6, _password)
}
}
@@ -124,7 +128,7 @@
_result =
MyEntity(_tmpPk,_tmpUuidData,_tmpNullableUuidData,_tmpNullableLongData,_tmpDoubleNullableLongData,_tmpGenericData)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_variableProperty.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_variableProperty.kt
index 6276d06..ef617b5 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_variableProperty.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_variableProperty.kt
@@ -25,10 +25,10 @@
init {
this.__db = __db
this.__insertionAdapterOfMyEntity = object : EntityInsertionAdapter<MyEntity>(__db) {
- public override fun createQuery(): String =
+ protected override fun createQuery(): String =
"INSERT OR ABORT INTO `MyEntity` (`pk`,`variablePrimitive`,`variableString`,`variableNullableString`) VALUES (?,?,?,?)"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
statement.bindLong(1, entity.pk.toLong())
statement.bindLong(2, entity.variablePrimitive)
statement.bindString(3, entity.variableString)
@@ -77,7 +77,7 @@
_result.variableNullableString = _cursor.getString(_cursorIndexOfVariableNullableString)
}
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_variableProperty_java.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_variableProperty_java.kt
index 127eb24..a2eb766 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_variableProperty_java.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/pojoRowAdapter_variableProperty_java.kt
@@ -26,10 +26,10 @@
init {
this.__db = __db
this.__insertionAdapterOfMyEntity = object : EntityInsertionAdapter<MyEntity>(__db) {
- public override fun createQuery(): String =
+ protected override fun createQuery(): String =
"INSERT OR ABORT INTO `MyEntity` (`mValue`,`mNullableValue`) VALUES (?,?)"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
statement.bindLong(1, entity.getValue())
val _tmpMNullableValue: String? = entity.getNullableValue()
if (_tmpMNullableValue == null) {
@@ -74,7 +74,7 @@
}
_result.setNullableValue(_tmpMNullableValue)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_guavaImmutableMap.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_guavaImmutableMap.kt
index 43a58d3..7e67d4e 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_guavaImmutableMap.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_guavaImmutableMap.kt
@@ -43,7 +43,7 @@
_tmpArtistKey = _cursor.getString(_cursorIndexOfArtistKey)
_key = Song(_tmpSongId,_tmpArtistKey)
if (_cursor.isNull(_cursorIndexOfArtistId)) {
- error("Missing value for a key.")
+ error("The column(s) of the map value object of type 'Artist' are NULL but the map's value type argument expect it to be NON-NULL")
}
val _value: Artist
val _tmpArtistId: String
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_map.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_map.kt
index f42651e..6f80a49 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_map.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_map.kt
@@ -45,7 +45,7 @@
_tmpArtistKey = _cursor.getString(_cursorIndexOfArtistKey)
_key = Song(_tmpSongId,_tmpArtistKey)
if (_cursor.isNull(_cursorIndexOfArtistId)) {
- error("Missing value for a key.")
+ error("The column(s) of the map value object of type 'Artist' are NULL but the map's value type argument expect it to be NON-NULL")
}
val _value: Artist
val _tmpArtistId: String
@@ -119,7 +119,7 @@
_tmpArtistId = _cursor.getString(_cursorIndexOfArtistId)
_key = Artist(_tmpArtistId)
if (_cursor.isNull(_columnIndexOfSongCount)) {
- error("Missing value for a key.")
+ error("The column(s) of the map value object of type 'Int' are NULL but the map's value type argument expect it to be NON-NULL")
}
val _value: Int
val _tmp: Int
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_map_ambiguousIndexAdapter.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_map_ambiguousIndexAdapter.kt
index 1c0720a..1f33090 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_map_ambiguousIndexAdapter.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/queryResultAdapter_map_ambiguousIndexAdapter.kt
@@ -174,7 +174,7 @@
}
val _tmpName: String
if (_cursorIndexOfName == -1) {
- error("Missing column 'name' for a non null value.")
+ error("Missing value for a NON-NULL column 'name', found NULL value instead.")
} else {
_tmpName = cursor.getString(_cursorIndexOfName)
}
@@ -201,7 +201,7 @@
}
val _tmpText: String
if (_cursorIndexOfText == -1) {
- error("Missing column 'text' for a non null value.")
+ error("Missing value for a NON-NULL column 'text', found NULL value instead.")
} else {
_tmpText = cursor.getString(_cursorIndexOfText)
}
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/rawQuery.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/rawQuery.kt
index 63a7af9..2361e59 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/rawQuery.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/rawQuery.kt
@@ -29,7 +29,7 @@
if (_cursor.moveToFirst()) {
_result = __entityCursorConverter_MyEntity(_cursor)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <MyEntity>.")
}
return _result
} finally {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/relations.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/relations.kt
index cf8c543..dee390f 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/relations.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/relations.kt
@@ -60,11 +60,11 @@
_tmpKey_1 = _cursor.getLong(_cursorIndexOfArtistKey)
_tmpArtist = _collectionArtist.get(_tmpKey_1)
if (_tmpArtist == null) {
- error("Missing relationship item.")
+ error("Relationship item 'artist' was expected to be NON-NULL but is NULL in @Relation involving a parent column named 'artistKey' and entityColumn named 'artistId'.")
}
_result = SongWithArtist(_tmpSong,_tmpArtist)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <SongWithArtist>.")
}
return _result
} finally {
@@ -102,7 +102,7 @@
_tmpSongsCollection = _collectionSongs.getValue(_tmpKey_1)
_result = ArtistAndSongs(_tmpArtist,_tmpSongsCollection)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <ArtistAndSongs>.")
}
return _result
} finally {
@@ -140,7 +140,7 @@
_tmpSongsCollection = _collectionSongs.getValue(_tmpKey_1)
_result = PlaylistAndSongs(_tmpPlaylist,_tmpSongsCollection)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <PlaylistAndSongs>.")
}
return _result
} finally {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_arrayMap.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_arrayMap.kt
index 7d2189f..a62f18e 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_arrayMap.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_arrayMap.kt
@@ -60,11 +60,11 @@
_tmpKey_1 = _cursor.getLong(_cursorIndexOfArtistKey)
_tmpArtist = _collectionArtist.get(_tmpKey_1)
if (_tmpArtist == null) {
- error("Missing relationship item.")
+ error("Relationship item 'artist' was expected to be NON-NULL but is NULL in @Relation involving a parent column named 'artistKey' and entityColumn named 'artistId'.")
}
_result = SongWithArtist(_tmpSong,_tmpArtist)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <SongWithArtist>.")
}
return _result
} finally {
@@ -102,7 +102,7 @@
_tmpSongsCollection = _collectionSongs.getValue(_tmpKey_1)
_result = ArtistAndSongs(_tmpArtist,_tmpSongsCollection)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <ArtistAndSongs>.")
}
return _result
} finally {
@@ -140,7 +140,7 @@
_tmpSongsCollection = _collectionSongs.getValue(_tmpKey_1)
_result = PlaylistAndSongs(_tmpPlaylist,_tmpSongsCollection)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <PlaylistAndSongs>.")
}
return _result
} finally {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_byteBufferKey.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_byteBufferKey.kt
index c8e642f..56c0b4e 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_byteBufferKey.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_byteBufferKey.kt
@@ -61,11 +61,11 @@
_tmpKey_1 = ByteBuffer.wrap(_cursor.getBlob(_cursorIndexOfArtistKey))
_tmpArtist = _collectionArtist.get(_tmpKey_1)
if (_tmpArtist == null) {
- error("Missing relationship item.")
+ error("Relationship item 'artist' was expected to be NON-NULL but is NULL in @Relation involving a parent column named 'artistKey' and entityColumn named 'artistId'.")
}
_result = SongWithArtist(_tmpSong,_tmpArtist)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <SongWithArtist>.")
}
return _result
} finally {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_longSparseArray.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_longSparseArray.kt
index 0a73c03..7a48aed 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_longSparseArray.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_longSparseArray.kt
@@ -59,11 +59,11 @@
_tmpKey_1 = _cursor.getLong(_cursorIndexOfArtistKey)
_tmpArtist = _collectionArtist.get(_tmpKey_1)
if (_tmpArtist == null) {
- error("Missing relationship item.")
+ error("Relationship item 'artist' was expected to be NON-NULL but is NULL in @Relation involving a parent column named 'artistKey' and entityColumn named 'artistId'.")
}
_result = SongWithArtist(_tmpSong,_tmpArtist)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <SongWithArtist>.")
}
return _result
} finally {
@@ -101,7 +101,7 @@
_tmpSongsCollection = checkNotNull(_collectionSongs.get(_tmpKey_1))
_result = ArtistAndSongs(_tmpArtist,_tmpSongsCollection)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <ArtistAndSongs>.")
}
return _result
} finally {
@@ -139,7 +139,7 @@
_tmpSongsCollection = checkNotNull(_collectionSongs.get(_tmpKey_1))
_result = PlaylistAndSongs(_tmpPlaylist,_tmpSongsCollection)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <PlaylistAndSongs>.")
}
return _result
} finally {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_nullable.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_nullable.kt
index 4618226..9cb1cc6 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_nullable.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/relations_nullable.kt
@@ -79,7 +79,7 @@
}
_result = SongWithArtist(_tmpSong,_tmpArtist)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <SongWithArtist>.")
}
return _result
} finally {
@@ -117,7 +117,7 @@
_tmpSongsCollection = _collectionSongs.getValue(_tmpKey_1)
_result = ArtistAndSongs(_tmpArtist,_tmpSongsCollection)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <ArtistAndSongs>.")
}
return _result
} finally {
@@ -155,7 +155,7 @@
_tmpSongsCollection = _collectionSongs.getValue(_tmpKey_1)
_result = PlaylistAndSongs(_tmpPlaylist,_tmpSongsCollection)
} else {
- error("Cursor was empty, but expected a single item.")
+ error("The query result was empty, but expected a single row to return a NON-NULL object of type <PlaylistAndSongs>.")
}
return _result
} finally {
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/shortcutMethods_rx2.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/shortcutMethods_rx2.kt
index dd1a9ca..94244ec 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/shortcutMethods_rx2.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/shortcutMethods_rx2.kt
@@ -33,26 +33,26 @@
init {
this.__db = __db
this.__insertionAdapterOfMyEntity = object : EntityInsertionAdapter<MyEntity>(__db) {
- public override fun createQuery(): String =
+ protected override fun createQuery(): String =
"INSERT OR ABORT INTO `MyEntity` (`pk`,`other`) VALUES (?,?)"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
statement.bindLong(1, entity.pk.toLong())
statement.bindString(2, entity.other)
}
}
this.__deletionAdapterOfMyEntity = object : EntityDeletionOrUpdateAdapter<MyEntity>(__db) {
- public override fun createQuery(): String = "DELETE FROM `MyEntity` WHERE `pk` = ?"
+ protected override fun createQuery(): String = "DELETE FROM `MyEntity` WHERE `pk` = ?"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
statement.bindLong(1, entity.pk.toLong())
}
}
this.__updateAdapterOfMyEntity = object : EntityDeletionOrUpdateAdapter<MyEntity>(__db) {
- public override fun createQuery(): String =
+ protected override fun createQuery(): String =
"UPDATE OR ABORT `MyEntity` SET `pk` = ?,`other` = ? WHERE `pk` = ?"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
statement.bindLong(1, entity.pk.toLong())
statement.bindString(2, entity.other)
statement.bindLong(3, entity.pk.toLong())
@@ -60,18 +60,18 @@
}
this.__upsertionAdapterOfMyEntity = EntityUpsertionAdapter<MyEntity>(object :
EntityInsertionAdapter<MyEntity>(__db) {
- public override fun createQuery(): String =
+ protected override fun createQuery(): String =
"INSERT INTO `MyEntity` (`pk`,`other`) VALUES (?,?)"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
statement.bindLong(1, entity.pk.toLong())
statement.bindString(2, entity.other)
}
}, object : EntityDeletionOrUpdateAdapter<MyEntity>(__db) {
- public override fun createQuery(): String =
+ protected override fun createQuery(): String =
"UPDATE `MyEntity` SET `pk` = ?,`other` = ? WHERE `pk` = ?"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
statement.bindLong(1, entity.pk.toLong())
statement.bindString(2, entity.other)
statement.bindLong(3, entity.pk.toLong())
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/shortcutMethods_rx3.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/shortcutMethods_rx3.kt
index bbab573..1e0ae5b 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/shortcutMethods_rx3.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/shortcutMethods_rx3.kt
@@ -33,26 +33,26 @@
init {
this.__db = __db
this.__insertionAdapterOfMyEntity = object : EntityInsertionAdapter<MyEntity>(__db) {
- public override fun createQuery(): String =
+ protected override fun createQuery(): String =
"INSERT OR ABORT INTO `MyEntity` (`pk`,`other`) VALUES (?,?)"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
statement.bindLong(1, entity.pk.toLong())
statement.bindString(2, entity.other)
}
}
this.__deletionAdapterOfMyEntity = object : EntityDeletionOrUpdateAdapter<MyEntity>(__db) {
- public override fun createQuery(): String = "DELETE FROM `MyEntity` WHERE `pk` = ?"
+ protected override fun createQuery(): String = "DELETE FROM `MyEntity` WHERE `pk` = ?"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
statement.bindLong(1, entity.pk.toLong())
}
}
this.__updateAdapterOfMyEntity = object : EntityDeletionOrUpdateAdapter<MyEntity>(__db) {
- public override fun createQuery(): String =
+ protected override fun createQuery(): String =
"UPDATE OR ABORT `MyEntity` SET `pk` = ?,`other` = ? WHERE `pk` = ?"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
statement.bindLong(1, entity.pk.toLong())
statement.bindString(2, entity.other)
statement.bindLong(3, entity.pk.toLong())
@@ -60,18 +60,18 @@
}
this.__upsertionAdapterOfMyEntity = EntityUpsertionAdapter<MyEntity>(object :
EntityInsertionAdapter<MyEntity>(__db) {
- public override fun createQuery(): String =
+ protected override fun createQuery(): String =
"INSERT INTO `MyEntity` (`pk`,`other`) VALUES (?,?)"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
statement.bindLong(1, entity.pk.toLong())
statement.bindString(2, entity.other)
}
}, object : EntityDeletionOrUpdateAdapter<MyEntity>(__db) {
- public override fun createQuery(): String =
+ protected override fun createQuery(): String =
"UPDATE `MyEntity` SET `pk` = ?,`other` = ? WHERE `pk` = ?"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
statement.bindLong(1, entity.pk.toLong())
statement.bindString(2, entity.other)
statement.bindLong(3, entity.pk.toLong())
diff --git a/room/room-compiler/src/test/test-data/kotlinCodeGen/shortcutMethods_suspend.kt b/room/room-compiler/src/test/test-data/kotlinCodeGen/shortcutMethods_suspend.kt
index 3658985..8c4ef1a 100644
--- a/room/room-compiler/src/test/test-data/kotlinCodeGen/shortcutMethods_suspend.kt
+++ b/room/room-compiler/src/test/test-data/kotlinCodeGen/shortcutMethods_suspend.kt
@@ -31,26 +31,26 @@
init {
this.__db = __db
this.__insertionAdapterOfMyEntity = object : EntityInsertionAdapter<MyEntity>(__db) {
- public override fun createQuery(): String =
+ protected override fun createQuery(): String =
"INSERT OR ABORT INTO `MyEntity` (`pk`,`other`) VALUES (?,?)"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
statement.bindLong(1, entity.pk.toLong())
statement.bindString(2, entity.other)
}
}
this.__deletionAdapterOfMyEntity = object : EntityDeletionOrUpdateAdapter<MyEntity>(__db) {
- public override fun createQuery(): String = "DELETE FROM `MyEntity` WHERE `pk` = ?"
+ protected override fun createQuery(): String = "DELETE FROM `MyEntity` WHERE `pk` = ?"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
statement.bindLong(1, entity.pk.toLong())
}
}
this.__updateAdapterOfMyEntity = object : EntityDeletionOrUpdateAdapter<MyEntity>(__db) {
- public override fun createQuery(): String =
+ protected override fun createQuery(): String =
"UPDATE OR ABORT `MyEntity` SET `pk` = ?,`other` = ? WHERE `pk` = ?"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
statement.bindLong(1, entity.pk.toLong())
statement.bindString(2, entity.other)
statement.bindLong(3, entity.pk.toLong())
@@ -58,18 +58,18 @@
}
this.__upsertionAdapterOfMyEntity = EntityUpsertionAdapter<MyEntity>(object :
EntityInsertionAdapter<MyEntity>(__db) {
- public override fun createQuery(): String =
+ protected override fun createQuery(): String =
"INSERT INTO `MyEntity` (`pk`,`other`) VALUES (?,?)"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
statement.bindLong(1, entity.pk.toLong())
statement.bindString(2, entity.other)
}
}, object : EntityDeletionOrUpdateAdapter<MyEntity>(__db) {
- public override fun createQuery(): String =
+ protected override fun createQuery(): String =
"UPDATE `MyEntity` SET `pk` = ?,`other` = ? WHERE `pk` = ?"
- public override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
+ protected override fun bind(statement: SupportSQLiteStatement, entity: MyEntity) {
statement.bindLong(1, entity.pk.toLong())
statement.bindString(2, entity.other)
statement.bindLong(3, entity.pk.toLong())
diff --git a/settings.gradle b/settings.gradle
index 34dec2f..5e4699c 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -906,6 +906,7 @@
includeProject(":privacysandbox:plugins:plugins-privacysandbox-library", [BuildType.MAIN])
includeProject(":privacysandbox:sdkruntime:sdkruntime-client", [BuildType.MAIN])
includeProject(":privacysandbox:sdkruntime:sdkruntime-core", [BuildType.MAIN])
+includeProject(":privacysandbox:sdkruntime:test-sdks:current", [BuildType.MAIN])
includeProject(":privacysandbox:tools:tools", [BuildType.MAIN])
includeProject(":privacysandbox:tools:tools-apicompiler", [BuildType.MAIN])
includeProject(":privacysandbox:tools:tools-apigenerator", [BuildType.MAIN])
diff --git a/slidingpanelayout/slidingpanelayout/build.gradle b/slidingpanelayout/slidingpanelayout/build.gradle
index 18bea17..58cbeb8 100644
--- a/slidingpanelayout/slidingpanelayout/build.gradle
+++ b/slidingpanelayout/slidingpanelayout/build.gradle
@@ -14,7 +14,6 @@
implementation("androidx.transition:transition:1.4.1")
androidTestImplementation(libs.testExtJunit)
- androidTestImplementation(libs.testRules)
androidTestImplementation(libs.testRunner)
androidTestImplementation(libs.espressoCore, excludes.espresso)
androidTestImplementation(libs.kotlinStdlib)
diff --git a/slidingpanelayout/slidingpanelayout/lint-baseline.xml b/slidingpanelayout/slidingpanelayout/lint-baseline.xml
deleted file mode 100644
index 1557792..0000000
--- a/slidingpanelayout/slidingpanelayout/lint-baseline.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.1.0-beta05" type="baseline" client="gradle" dependencies="false" name="AGP (8.1.0-beta05)" variant="all" version="8.1.0-beta05">
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 28; however, the containing class androidx.slidingpanelayout.widget.SlidingPaneLayout is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" if (panel.getAccessibilityPaneTitle() != null) {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/slidingpanelayout/widget/SlidingPaneLayout.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 28; however, the containing class androidx.slidingpanelayout.widget.SlidingPaneLayout is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" panel.getAccessibilityPaneTitle().toString());"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/slidingpanelayout/widget/SlidingPaneLayout.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 19; however, the containing class androidx.slidingpanelayout.widget.SlidingPaneLayout is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" event.setContentChangeTypes(contentType);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/slidingpanelayout/widget/SlidingPaneLayout.java"/>
- </issue>
-
- <issue
- id="ClassVerificationFailure"
- message="This call references a method added in API level 16; however, the containing class androidx.slidingpanelayout.widget.SlidingPaneLayout is reachable from earlier API levels and will fail run-time class verification."
- errorLine1=" panel.performAccessibilityAction("
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/slidingpanelayout/widget/SlidingPaneLayout.java"/>
- </issue>
-
-</issues>
diff --git a/slidingpanelayout/slidingpanelayout/src/androidTest/java/androidx/slidingpanelayout/widget/SlidingPaneLayoutAccessibilityTest.kt b/slidingpanelayout/slidingpanelayout/src/androidTest/java/androidx/slidingpanelayout/widget/SlidingPaneLayoutAccessibilityTest.kt
deleted file mode 100644
index af31cec..0000000
--- a/slidingpanelayout/slidingpanelayout/src/androidTest/java/androidx/slidingpanelayout/widget/SlidingPaneLayoutAccessibilityTest.kt
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.slidingpanelayout.widget
-
-import android.app.UiAutomation
-import android.os.Build
-import android.view.View
-import android.view.accessibility.AccessibilityEvent
-import android.widget.TextView
-import androidx.core.view.ViewCompat
-import androidx.slidingpanelayout.test.R
-import androidx.slidingpanelayout.widget.helpers.TestActivity
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.MediumTest
-import androidx.test.filters.SdkSuppress
-import androidx.test.platform.app.InstrumentationRegistry
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(AndroidJUnit4::class)
-@MediumTest
-@SdkSuppress(minSdkVersion = 19)
-class SlidingPaneLayoutAccessibilityTest {
-
- @Suppress("DEPRECATION")
- @get:Rule
- val mActivityTestRule = androidx.test.rule.ActivityTestRule(
- TestActivity::class.java
- )
-
- private val timeout = 5000L
-
- private lateinit var uiAutomation: UiAutomation
- private lateinit var slidingPaneLayout: SlidingPaneLayout
-
- private lateinit var listPane: TextView
- private lateinit var detailPane: TextView
-
- @Before
- fun setup() {
- val instrumentation = InstrumentationRegistry.getInstrumentation()
- uiAutomation = instrumentation.uiAutomation
-
- val activity = mActivityTestRule.activity
- mActivityTestRule.runOnUiThread {
- slidingPaneLayout = activity.findViewById(R.id.sliding_pane_layout) as SlidingPaneLayout
- listPane = activity.findViewById(R.id.list_pane) as TextView
- detailPane = activity.findViewById(R.id.detail_pane) as TextView
- // On KitKat, some delegate methods aren't called for non-important views
- ViewCompat.setImportantForAccessibility(
- slidingPaneLayout, View.IMPORTANT_FOR_ACCESSIBILITY_YES)
- }
- }
-
- @Test
- fun testPaneOpening() {
- ViewCompat.setAccessibilityPaneTitle(detailPane, "Detail Pane")
- ViewCompat.setAccessibilityPaneTitle(listPane, "List Pane")
- mActivityTestRule.runOnUiThread {
- detailPane.visibility = View.INVISIBLE
- detailPane.viewTreeObserver.dispatchOnGlobalLayout()
- }
- uiAutomation.executeAndWaitForEvent(
- {
- try {
- mActivityTestRule.runOnUiThread {
- slidingPaneLayout.openPane()
- }
- } catch (throwable: Throwable) {
- throwable.printStackTrace()
- }
- },
- { event ->
- val isWindowStateChanged =
- event.eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
- val isPaneTitle: Int = (event.contentChangeTypes
- and AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_APPEARED)
- (isWindowStateChanged && isPaneTitle != 0)
- }, timeout
- )
- }
-
- @Test
- fun testPaneClosing() {
- ViewCompat.setAccessibilityPaneTitle(detailPane, "Detail Pane")
- ViewCompat.setAccessibilityPaneTitle(listPane, "List Pane")
- mActivityTestRule.runOnUiThread {
- detailPane.visibility = View.INVISIBLE
- detailPane.viewTreeObserver.dispatchOnGlobalLayout()
- slidingPaneLayout.openPane()
- detailPane.visibility = View.VISIBLE
- if (Build.VERSION.SDK_INT < 28) {
- detailPane.viewTreeObserver.dispatchOnGlobalLayout()
- }
- }
- uiAutomation.executeAndWaitForEvent(
- {
- try {
- mActivityTestRule.runOnUiThread {
- slidingPaneLayout.closePane()
- }
- } catch (throwable: Throwable) {
- throwable.printStackTrace()
- }
- },
- { event ->
- val isWindowStateChanged =
- event.eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
- val isPaneTitle: Int = (event.contentChangeTypes
- and AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED)
- (isWindowStateChanged && isPaneTitle != 0)
- }, timeout
- )
- }
-}
diff --git a/slidingpanelayout/slidingpanelayout/src/main/java/androidx/slidingpanelayout/widget/SlidingPaneLayout.java b/slidingpanelayout/slidingpanelayout/src/main/java/androidx/slidingpanelayout/widget/SlidingPaneLayout.java
index a8533c2..4c826d2 100644
--- a/slidingpanelayout/slidingpanelayout/src/main/java/androidx/slidingpanelayout/widget/SlidingPaneLayout.java
+++ b/slidingpanelayout/slidingpanelayout/src/main/java/androidx/slidingpanelayout/widget/SlidingPaneLayout.java
@@ -503,58 +503,14 @@
for (PanelSlideListener listener : mPanelSlideListeners) {
listener.onPanelOpened(panel);
}
- sendAccessibilityPaneTitleEvent(panel);
- sendChangedFocusAccessibilityEvents(panel,
- AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_APPEARED);
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
}
void dispatchOnPanelClosed(@NonNull View panel) {
for (PanelSlideListener listener : mPanelSlideListeners) {
listener.onPanelClosed(panel);
}
- View parentPanel = getChildAt(0);
- sendAccessibilityPaneTitleEvent(parentPanel);
- sendChangedFocusAccessibilityEvents(parentPanel,
- AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED);
- }
-
- /**
- * Sets the Accessibility title when the API is above API 28. This logic is required due to the
- * opening and closing drawer logic and the title must be updated for each open and close.
- * @param panel Panel with the current fovus
- */
- private void sendAccessibilityPaneTitleEvent(final View panel) {
- if (panel == null) return;
-
- if (Build.VERSION.SDK_INT >= 28) {
- if (panel.getAccessibilityPaneTitle() != null) {
- ViewCompat.setAccessibilityPaneTitle(panel,
- panel.getAccessibilityPaneTitle().toString());
- }
- }
- }
-
- @SuppressWarnings("deprecation")
- private void sendChangedFocusAccessibilityEvents(final View panel, int contentType) {
- if (panel == null) return;
-
- if (Build.VERSION.SDK_INT >= 19) {
- AccessibilityEvent event = AccessibilityEvent.obtain(
- AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
- event.setContentChangeTypes(contentType);
- sendAccessibilityEventUnchecked(event);
- } else {
- sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
- }
-
- // If a view can slide, the previous view will be completely hidden
- // This sets the focus for the root view of the child when shown.
- // TODO(224450896): We should not force accessibility focus. Remove when screen reader
- // behavior around panes is defined.
- if (mCanSlide && Build.VERSION.SDK_INT >= 16) {
- panel.performAccessibilityAction(
- AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS, null);
- }
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
}
void updateObscuredViewsVisibility(View panel) {
diff --git a/testutils/testutils-macrobenchmark/src/main/java/androidx/testutils/MacrobenchUtils.kt b/testutils/testutils-macrobenchmark/src/main/java/androidx/testutils/MacrobenchUtils.kt
index 0bc25f4..01a897c 100644
--- a/testutils/testutils-macrobenchmark/src/main/java/androidx/testutils/MacrobenchUtils.kt
+++ b/testutils/testutils-macrobenchmark/src/main/java/androidx/testutils/MacrobenchUtils.kt
@@ -25,6 +25,7 @@
import androidx.benchmark.macro.Metric
import androidx.benchmark.macro.StartupMode
import androidx.benchmark.macro.StartupTimingMetric
+import androidx.benchmark.macro.TraceSectionMetric
import androidx.benchmark.macro.isSupportedWithVmSettings
import androidx.benchmark.macro.junit4.MacrobenchmarkRule
@@ -67,7 +68,11 @@
*/
@OptIn(ExperimentalMetricApi::class)
fun getStartupMetrics() =
- listOf(StartupTimingMetric(), MemoryUsageMetric(MemoryUsageMetric.Mode.Last))
+ listOf(
+ StartupTimingMetric(),
+ TraceSectionMetric("StartupTracingInitializer"),
+ MemoryUsageMetric(MemoryUsageMetric.Mode.Last)
+ )
fun MacrobenchmarkRule.measureStartup(
compilationMode: CompilationMode,
diff --git a/tracing/tracing-perfetto/src/main/AndroidManifest.xml b/tracing/tracing-perfetto/src/main/AndroidManifest.xml
index 7402b3a..c955790 100644
--- a/tracing/tracing-perfetto/src/main/AndroidManifest.xml
+++ b/tracing/tracing-perfetto/src/main/AndroidManifest.xml
@@ -32,8 +32,6 @@
</intent-filter>
</receiver>
- <!-- TODO(283953019): enable when feature complete (i.e. after measuring perf impact) -->
- <!--
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
@@ -43,6 +41,12 @@
android:name="androidx.tracing.perfetto.StartupTracingInitializer"
android:value="androidx.startup" />
</provider>
- -->
+ <receiver
+ android:name="androidx.tracing.perfetto.StartupTracingConfigStoreIsEnabledGate"
+ android:enabled="false"
+ android:exported="false"
+ android:directBootAware="false"
+ tools:targetApi="n">
+ </receiver>
</application>
</manifest>
diff --git a/tracing/tracing-perfetto/src/main/java/androidx/tracing/perfetto/StartupTracingConfig.kt b/tracing/tracing-perfetto/src/main/java/androidx/tracing/perfetto/StartupTracingConfig.kt
index e83611c..fd34d99 100644
--- a/tracing/tracing-perfetto/src/main/java/androidx/tracing/perfetto/StartupTracingConfig.kt
+++ b/tracing/tracing-perfetto/src/main/java/androidx/tracing/perfetto/StartupTracingConfig.kt
@@ -16,10 +16,13 @@
package androidx.tracing.perfetto
-import androidx.annotation.RestrictTo
-import androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP
+import android.content.BroadcastReceiver
+import android.content.ComponentName
+import android.content.Context
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED
+import android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED
import java.io.File
-import java.io.Writer
import java.util.Properties
/**
@@ -28,10 +31,39 @@
* @param libFilePath Path to the optionally sideloaded `libtracing_perfetto.so` file
* @param isPersistent Determines whether tracing should remain enabled (sticky) between app runs
*/
-@RestrictTo(LIBRARY_GROUP)
internal data class StartupTracingConfig(val libFilePath: String?, val isPersistent: Boolean)
-@RestrictTo(LIBRARY_GROUP)
+/**
+ * Hack used by [StartupTracingConfigStore] to perform a fast check whether there is
+ * a [StartupTracingConfig] present. Relies on [PackageManager.getComponentEnabledSetting] and a
+ * dummy [BroadcastReceiver] component.
+ */
+private abstract class StartupTracingConfigStoreIsEnabledGate : BroadcastReceiver() {
+ companion object {
+ fun enable(context: Context) = setEnabledSetting(context, true)
+
+ fun disable(context: Context) = setEnabledSetting(context, false)
+
+ private fun setEnabledSetting(context: Context, enabled: Boolean) {
+ context.packageManager.setComponentEnabledSetting(
+ context.componentName,
+ if (enabled) COMPONENT_ENABLED_STATE_ENABLED else COMPONENT_ENABLED_STATE_DISABLED,
+ PackageManager.DONT_KILL_APP
+ )
+ }
+
+ fun isEnabled(context: Context): Boolean =
+ context.packageManager.getComponentEnabledSetting(context.componentName) ==
+ COMPONENT_ENABLED_STATE_ENABLED
+
+ private val Context.componentName
+ get() = ComponentName(
+ this,
+ StartupTracingConfigStoreIsEnabledGate::class.java.name
+ )
+ }
+}
+
internal object StartupTracingConfigStore {
private const val KEY_IS_PERSISTENT = "isPersistent"
private const val KEY_LIB_FILE_PATH = "libtracingPerfettoFilePath"
@@ -41,9 +73,12 @@
File("/sdcard/Android/media/$packageName/$STARTUP_CONFIG_FILE_NAME")
/** Loads the config */
- fun load(packageName: String): StartupTracingConfig? {
+ fun load(context: Context): StartupTracingConfig? {
+ // use the fast-check-gate value
+ if (!StartupTracingConfigStoreIsEnabledGate.isEnabled(context)) return null
+
// read the config from file
- val propertiesFile = startupConfigFileForPackageName(packageName)
+ val propertiesFile = startupConfigFileForPackageName(context.packageName)
if (!propertiesFile.exists()) return null
val properties = Properties()
propertiesFile.reader().use { properties.load(it) }
@@ -54,24 +89,21 @@
}
/** Stores the config */
- fun StartupTracingConfig.store(packageName: String): Unit =
- startupConfigFileForPackageName(packageName)
+ fun StartupTracingConfig.store(context: Context) {
+ startupConfigFileForPackageName(context.packageName)
.bufferedWriter()
- .use { store(it) }
-
- /**
- * Stores the config as a [Properties] string
- *
- * The caller is responsible for closing the passed-in [Writer]
- */
- private fun StartupTracingConfig.store(writer: Writer) =
- Properties().also {
- it.setProperty(KEY_LIB_FILE_PATH, libFilePath)
- it.setProperty(KEY_IS_PERSISTENT, isPersistent.toString())
- }.store(writer, null)
+ .use { writer ->
+ Properties().also {
+ it.setProperty(KEY_LIB_FILE_PATH, libFilePath)
+ it.setProperty(KEY_IS_PERSISTENT, isPersistent.toString())
+ }.store(writer, null)
+ }
+ StartupTracingConfigStoreIsEnabledGate.enable(context) // update the fast-check-gate value
+ }
/** Deletes the config */
- fun clear(packageName: String) {
- startupConfigFileForPackageName(packageName).delete()
+ fun clear(context: Context) {
+ StartupTracingConfigStoreIsEnabledGate.disable(context) // update the fast-check-gate value
+ startupConfigFileForPackageName(context.packageName).delete()
}
}
diff --git a/tracing/tracing-perfetto/src/main/java/androidx/tracing/perfetto/StartupTracingInitializer.kt b/tracing/tracing-perfetto/src/main/java/androidx/tracing/perfetto/StartupTracingInitializer.kt
index ab2d6fd..ee2ea0d 100644
--- a/tracing/tracing-perfetto/src/main/java/androidx/tracing/perfetto/StartupTracingInitializer.kt
+++ b/tracing/tracing-perfetto/src/main/java/androidx/tracing/perfetto/StartupTracingInitializer.kt
@@ -18,6 +18,7 @@
import android.content.Context
import android.os.Build
+import android.os.StrictMode
import android.util.Log
import androidx.startup.Initializer
import androidx.tracing.perfetto.internal.handshake.protocol.Response
@@ -33,27 +34,38 @@
// TODO(234351579): Support API < 30
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) return
- // read startup tracing config file if present
- val packageName = context.applicationInfo.packageName
- val config = StartupTracingConfigStore.load(packageName)
- ?: return // early exit if no config is found
+ suppressStrictModeDiskWrites {
+ // read startup tracing config if present
+ val config = StartupTracingConfigStore.load(context)
+ ?: return // early exit if no config is found
- // delete config file if not meant to be preserved between runs
- if (!config.isPersistent) StartupTracingConfigStore.clear(packageName)
+ // delete config if not meant to be preserved between runs
+ if (!config.isPersistent) StartupTracingConfigStore.clear(context)
- // enable tracing
- val libFilePath = config.libFilePath
- val enableTracingResponse =
- if (libFilePath == null) PerfettoSdkTrace.enable()
- else PerfettoSdkTrace.enable(File(libFilePath), context)
+ // enable tracing
+ val libFilePath = config.libFilePath
+ val enableTracingResponse =
+ if (libFilePath == null) PerfettoSdkTrace.enable()
+ else PerfettoSdkTrace.enable(File(libFilePath), context)
- // log the result for debuggability
- Log.d(TAG, "${Response::class.java.name}: { " +
- "resultCode: ${enableTracingResponse.resultCode}, " +
- "message: ${enableTracingResponse.message}, " +
- "requiredVersion: ${enableTracingResponse.requiredVersion} " +
- "}")
+ // log the result for debuggability
+ Log.d(TAG, "${Response::class.java.name}: { " +
+ "resultCode: ${enableTracingResponse.resultCode}, " +
+ "message: ${enableTracingResponse.message}, " +
+ "requiredVersion: ${enableTracingResponse.requiredVersion} " +
+ "}")
+ }
}
override fun dependencies(): List<Class<out Initializer<*>>> = emptyList()
+
+ // TODO(245426369): test in TrivialStartupTracingBenchmark
+ private inline fun <R> suppressStrictModeDiskWrites(block: () -> R): R {
+ val oldPolicy = StrictMode.allowThreadDiskWrites()
+ try {
+ return block()
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy)
+ }
+ }
}
diff --git a/tracing/tracing-perfetto/src/main/java/androidx/tracing/perfetto/TracingReceiver.kt b/tracing/tracing-perfetto/src/main/java/androidx/tracing/perfetto/TracingReceiver.kt
index 2bfa12e..6f45b92 100644
--- a/tracing/tracing-perfetto/src/main/java/androidx/tracing/perfetto/TracingReceiver.kt
+++ b/tracing/tracing-perfetto/src/main/java/androidx/tracing/perfetto/TracingReceiver.kt
@@ -134,13 +134,13 @@
RESULT_CODE_ERROR_OTHER,
"Cannot set up cold start tracing without a Context instance."
)
- config.store(context.applicationInfo.packageName)
+ config.store(context)
}
}
private fun disableTracingColdStart(context: Context?): Response = when {
context != null -> {
- StartupTracingConfigStore.clear(context.applicationInfo.packageName)
+ StartupTracingConfigStore.clear(context)
Response(RESULT_CODE_SUCCESS)
}
else ->
diff --git a/tv/samples/src/main/java/androidx/tv/samples/NavigationDrawerSamples.kt b/tv/samples/src/main/java/androidx/tv/samples/NavigationDrawerSamples.kt
index 345adc6..dc061ec 100644
--- a/tv/samples/src/main/java/androidx/tv/samples/NavigationDrawerSamples.kt
+++ b/tv/samples/src/main/java/androidx/tv/samples/NavigationDrawerSamples.kt
@@ -32,6 +32,7 @@
import androidx.compose.material3.Button
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
@@ -80,7 +81,7 @@
@OptIn(ExperimentalTvMaterial3Api::class)
@Sampled
@Composable
-fun SampleModalNavigationDrawer() {
+fun SampleModalNavigationDrawerWithSolidScrim() {
val navigationRow: @Composable (drawerValue: DrawerValue, color: Color, text: String) -> Unit =
{ drawerValue, color, text ->
Row(Modifier.padding(10.dp).focusable()) {
@@ -112,3 +113,40 @@
}
}
}
+
+@OptIn(ExperimentalTvMaterial3Api::class)
+@Sampled
+@Composable
+fun SampleModalNavigationDrawerWithGradientScrim() {
+ val navigationRow: @Composable (drawerValue: DrawerValue, color: Color, text: String) -> Unit =
+ { drawerValue, color, text ->
+ Row(Modifier.padding(10.dp).focusable()) {
+ Box(Modifier.size(50.dp).background(color).padding(end = 20.dp))
+ AnimatedVisibility(visible = drawerValue == DrawerValue.Open) {
+ Text(
+ text = text,
+ softWrap = false,
+ modifier = Modifier.padding(15.dp).width(50.dp),
+ textAlign = TextAlign.Center
+ )
+ }
+ }
+ }
+
+ androidx.tv.material3.ModalNavigationDrawer(
+ drawerContent = {
+ Column(Modifier.fillMaxHeight()) {
+ navigationRow(it, Color.Red, "Red")
+ navigationRow(it, Color.Blue, "Blue")
+ navigationRow(it, Color.Yellow, "Yellow")
+ }
+ },
+ scrimBrush = Brush.horizontalGradient(listOf(Color.DarkGray, Color.Transparent))
+ ) {
+ Button(modifier = Modifier
+ .height(100.dp)
+ .fillMaxWidth(), onClick = {}) {
+ Text("BUTTON")
+ }
+ }
+}
diff --git a/tv/tv-foundation/src/main/java/androidx/tv/foundation/lazy/grid/LazyGrid.kt b/tv/tv-foundation/src/main/java/androidx/tv/foundation/lazy/grid/LazyGrid.kt
index b95b687..5cf5964 100644
--- a/tv/tv-foundation/src/main/java/androidx/tv/foundation/lazy/grid/LazyGrid.kt
+++ b/tv/tv-foundation/src/main/java/androidx/tv/foundation/lazy/grid/LazyGrid.kt
@@ -127,7 +127,7 @@
),
prefetchState = state.prefetchState,
measurePolicy = measurePolicy,
- itemProvider = itemProvider
+ itemProvider = { itemProvider }
)
}
diff --git a/tv/tv-foundation/src/main/java/androidx/tv/foundation/lazy/list/LazyList.kt b/tv/tv-foundation/src/main/java/androidx/tv/foundation/lazy/list/LazyList.kt
index 224af43..34254cc 100644
--- a/tv/tv-foundation/src/main/java/androidx/tv/foundation/lazy/list/LazyList.kt
+++ b/tv/tv-foundation/src/main/java/androidx/tv/foundation/lazy/list/LazyList.kt
@@ -131,7 +131,7 @@
),
prefetchState = state.prefetchState,
measurePolicy = measurePolicy,
- itemProvider = itemProvider
+ itemProvider = { itemProvider }
)
}
diff --git a/tv/tv-material/api/current.txt b/tv/tv-material/api/current.txt
index f1485a9..3dca4bb 100644
--- a/tv/tv-material/api/current.txt
+++ b/tv/tv-material/api/current.txt
@@ -578,7 +578,7 @@
}
public final class NavigationDrawerKt {
- method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void ModalNavigationDrawer(kotlin.jvm.functions.Function1<? super androidx.tv.material3.DrawerValue,kotlin.Unit> drawerContent, optional androidx.compose.ui.Modifier modifier, optional androidx.tv.material3.DrawerState drawerState, optional long scrimColor, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void ModalNavigationDrawer(kotlin.jvm.functions.Function1<? super androidx.tv.material3.DrawerValue,kotlin.Unit> drawerContent, optional androidx.compose.ui.Modifier modifier, optional androidx.tv.material3.DrawerState drawerState, optional androidx.compose.ui.graphics.Brush scrimBrush, kotlin.jvm.functions.Function0<kotlin.Unit> content);
method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void NavigationDrawer(kotlin.jvm.functions.Function1<? super androidx.tv.material3.DrawerValue,kotlin.Unit> drawerContent, optional androidx.compose.ui.Modifier modifier, optional androidx.tv.material3.DrawerState drawerState, kotlin.jvm.functions.Function0<kotlin.Unit> content);
method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static androidx.tv.material3.DrawerState rememberDrawerState(androidx.tv.material3.DrawerValue initialValue);
}
diff --git a/tv/tv-material/api/restricted_current.txt b/tv/tv-material/api/restricted_current.txt
index f1485a9..3dca4bb 100644
--- a/tv/tv-material/api/restricted_current.txt
+++ b/tv/tv-material/api/restricted_current.txt
@@ -578,7 +578,7 @@
}
public final class NavigationDrawerKt {
- method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void ModalNavigationDrawer(kotlin.jvm.functions.Function1<? super androidx.tv.material3.DrawerValue,kotlin.Unit> drawerContent, optional androidx.compose.ui.Modifier modifier, optional androidx.tv.material3.DrawerState drawerState, optional long scrimColor, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void ModalNavigationDrawer(kotlin.jvm.functions.Function1<? super androidx.tv.material3.DrawerValue,kotlin.Unit> drawerContent, optional androidx.compose.ui.Modifier modifier, optional androidx.tv.material3.DrawerState drawerState, optional androidx.compose.ui.graphics.Brush scrimBrush, kotlin.jvm.functions.Function0<kotlin.Unit> content);
method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static void NavigationDrawer(kotlin.jvm.functions.Function1<? super androidx.tv.material3.DrawerValue,kotlin.Unit> drawerContent, optional androidx.compose.ui.Modifier modifier, optional androidx.tv.material3.DrawerState drawerState, kotlin.jvm.functions.Function0<kotlin.Unit> content);
method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.tv.material3.ExperimentalTvMaterial3Api public static androidx.tv.material3.DrawerState rememberDrawerState(androidx.tv.material3.DrawerValue initialValue);
}
diff --git a/tv/tv-material/src/main/java/androidx/tv/material3/NavigationDrawer.kt b/tv/tv-material/src/main/java/androidx/tv/material3/NavigationDrawer.kt
index ac34cff..a3f7e6d 100644
--- a/tv/tv-material/src/main/java/androidx/tv/material3/NavigationDrawer.kt
+++ b/tv/tv-material/src/main/java/androidx/tv/material3/NavigationDrawer.kt
@@ -40,7 +40,8 @@
import androidx.compose.ui.focus.FocusState
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.focus.onFocusChanged
-import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Brush
+import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Dp
@@ -59,7 +60,8 @@
* layout grid.
*
* Example:
- * @sample androidx.tv.samples.SampleModalNavigationDrawer
+ * @sample androidx.tv.samples.SampleModalNavigationDrawerWithSolidScrim
+ * @sample androidx.tv.samples.SampleModalNavigationDrawerWithGradientScrim
*
* @param drawerContent Content that needs to be displayed on the drawer based on whether the drawer
* is [DrawerValue.Open] or [DrawerValue.Closed].
@@ -72,7 +74,7 @@
*
* @param modifier the [Modifier] to be applied to this drawer
* @param drawerState state of the drawer
- * @param scrimColor color of the scrim that obscures content when the drawer is open
+ * @param scrimBrush brush to paint the scrim that obscures content when the drawer is open
* @param content content of the rest of the UI
*/
@ExperimentalTvMaterial3Api
@@ -81,7 +83,7 @@
drawerContent: @Composable (DrawerValue) -> Unit,
modifier: Modifier = Modifier,
drawerState: DrawerState = rememberDrawerState(DrawerValue.Closed),
- scrimColor: Color = LocalColorScheme.current.scrim.copy(alpha = 0.5f),
+ scrimBrush: Brush = SolidColor(LocalColorScheme.current.scrim.copy(alpha = 0.5f)),
content: @Composable () -> Unit
) {
val localDensity = LocalDensity.current
@@ -113,14 +115,14 @@
content = drawerContent
)
+ if (drawerState.currentValue == DrawerValue.Open) {
+ // Scrim
+ Canvas(Modifier.fillMaxSize()) {
+ drawRect(scrimBrush)
+ }
+ }
Box(Modifier.padding(start = closedDrawerWidth.value ?: ClosedDrawerWidth.dp)) {
content()
- if (drawerState.currentValue == DrawerValue.Open) {
- // Scrim
- Canvas(Modifier.fillMaxSize()) {
- drawRect(scrimColor)
- }
- }
}
}
}
diff --git a/tvprovider/tvprovider/api/api_lint.ignore b/tvprovider/tvprovider/api/api_lint.ignore
index 60a676a..16230a6 100644
--- a/tvprovider/tvprovider/api/api_lint.ignore
+++ b/tvprovider/tvprovider/api/api_lint.ignore
@@ -81,13 +81,9 @@
Public class androidx.tvprovider.media.tv.WatchNextProgram stripped of unavailable superclass androidx.tvprovider.media.tv.BasePreviewProgram
-IntentName: androidx.tvprovider.media.tv.TvContractCompat.PreviewPrograms#COLUMN_INTERACTION_COUNT:
+IntentName: androidx.tvprovider.media.tv.TvContractCompat.PreviewProgramColumns#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:
+IntentName: androidx.tvprovider.media.tv.TvContractCompat.PreviewProgramColumns#COLUMN_INTERACTION_TYPE:
Intent action constant name must be ACTION_FOO: COLUMN_INTERACTION_TYPE
diff --git a/tvprovider/tvprovider/api/current.txt b/tvprovider/tvprovider/api/current.txt
index c82fb20..1ca7bb2 100644
--- a/tvprovider/tvprovider/api/current.txt
+++ b/tvprovider/tvprovider/api/current.txt
@@ -195,6 +195,7 @@
method public boolean isSearchable();
method public boolean isTransient();
method public android.content.ContentValues! toContentValues();
+ field public static final String![] PROJECTION;
}
public static final class PreviewProgram.Builder {
@@ -458,7 +459,7 @@
field public static final String CONTENT_DIRECTORY = "logo";
}
- public static final class TvContractCompat.PreviewPrograms implements androidx.tvprovider.media.tv.TvContractCompat.BaseTvColumns {
+ public static interface TvContractCompat.PreviewProgramColumns {
field public static final int ASPECT_RATIO_16_9 = 0; // 0x0
field public static final int ASPECT_RATIO_1_1 = 3; // 0x3
field public static final int ASPECT_RATIO_2_3 = 4; // 0x4
@@ -472,62 +473,33 @@
field public static final int AVAILABILITY_FREE_WITH_SUBSCRIPTION = 1; // 0x1
field public static final int AVAILABILITY_PAID_CONTENT = 2; // 0x2
field public static final int AVAILABILITY_PURCHASED = 3; // 0x3
- field public static final String COLUMN_AUDIO_LANGUAGE = "audio_language";
field public static final String COLUMN_AUTHOR = "author";
field public static final String COLUMN_AVAILABILITY = "availability";
field public static final String COLUMN_BROWSABLE = "browsable";
- field public static final String COLUMN_CANONICAL_GENRE = "canonical_genre";
- field public static final String COLUMN_CHANNEL_ID = "channel_id";
field public static final String COLUMN_CONTENT_ID = "content_id";
- field public static final String COLUMN_CONTENT_RATING = "content_rating";
field public static final String COLUMN_DURATION_MILLIS = "duration_millis";
field public static final String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis";
- field public static final String COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number";
- field public static final String COLUMN_EPISODE_TITLE = "episode_title";
field public static final String COLUMN_GENRE = "genre";
field public static final String COLUMN_INTENT_URI = "intent_uri";
field public static final String COLUMN_INTERACTION_COUNT = "interaction_count";
field public static final String COLUMN_INTERACTION_TYPE = "interaction_type";
- field public static final String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
- field public static final String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
- field public static final String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
- field public static final String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
- field public static final String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
field public static final String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id";
field public static final String COLUMN_ITEM_COUNT = "item_count";
field public static final String COLUMN_LAST_PLAYBACK_POSITION_MILLIS = "last_playback_position_millis";
field public static final String COLUMN_LIVE = "live";
field public static final String COLUMN_LOGO_CONTENT_DESCRIPTION = "logo_content_description";
field public static final String COLUMN_LOGO_URI = "logo_uri";
- field public static final String COLUMN_LONG_DESCRIPTION = "long_description";
field public static final String COLUMN_OFFER_PRICE = "offer_price";
field public static final String COLUMN_POSTER_ART_ASPECT_RATIO = "poster_art_aspect_ratio";
- field public static final String COLUMN_POSTER_ART_URI = "poster_art_uri";
field public static final String COLUMN_PREVIEW_AUDIO_URI = "preview_audio_uri";
field public static final String COLUMN_PREVIEW_VIDEO_URI = "preview_video_uri";
field public static final String COLUMN_RELEASE_DATE = "release_date";
- field public static final String COLUMN_REVIEW_RATING = "review_rating";
- field public static final String COLUMN_REVIEW_RATING_STYLE = "review_rating_style";
- field public static final String COLUMN_SEARCHABLE = "searchable";
- field public static final String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number";
- field public static final String COLUMN_SEASON_TITLE = "season_title";
- field public static final String COLUMN_SERIES_ID = "series_id";
- field public static final String COLUMN_SHORT_DESCRIPTION = "short_description";
field public static final String COLUMN_STARTING_PRICE = "starting_price";
field public static final String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis";
field public static final String COLUMN_THUMBNAIL_ASPECT_RATIO = "poster_thumbnail_aspect_ratio";
- field public static final String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
- field public static final String COLUMN_TITLE = "title";
field public static final String COLUMN_TRANSIENT = "transient";
field public static final String COLUMN_TV_SERIES_ITEM_TYPE = "tv_series_item_type";
field public static final String COLUMN_TYPE = "type";
- field public static final String COLUMN_VERSION_NUMBER = "version_number";
- field public static final String COLUMN_VIDEO_HEIGHT = "video_height";
- field public static final String COLUMN_VIDEO_WIDTH = "video_width";
- field public static final String COLUMN_WEIGHT = "weight";
- field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/preview_program";
- field public static final String CONTENT_TYPE = "vnd.android.cursor.dir/preview_program";
- field public static final android.net.Uri! CONTENT_URI;
field public static final int INTERACTION_TYPE_FANS = 3; // 0x3
field public static final int INTERACTION_TYPE_FOLLOWERS = 2; // 0x2
field public static final int INTERACTION_TYPE_LIKES = 4; // 0x4
@@ -535,9 +507,6 @@
field public static final int INTERACTION_TYPE_THUMBS = 5; // 0x5
field public static final int INTERACTION_TYPE_VIEWERS = 6; // 0x6
field public static final int INTERACTION_TYPE_VIEWS = 0; // 0x0
- field public static final int REVIEW_RATING_STYLE_PERCENTAGE = 2; // 0x2
- field public static final int REVIEW_RATING_STYLE_STARS = 0; // 0x0
- field public static final int REVIEW_RATING_STYLE_THUMBS_UP_DOWN = 1; // 0x1
field public static final int TV_SERIES_ITEM_TYPE_CHAPTER = 1; // 0x1
field public static final int TV_SERIES_ITEM_TYPE_EPISODE = 0; // 0x0
field public static final int TYPE_ALBUM = 8; // 0x8
@@ -555,6 +524,41 @@
field public static final int TYPE_TV_SERIES = 1; // 0x1
}
+ public static final class TvContractCompat.PreviewPrograms implements androidx.tvprovider.media.tv.TvContractCompat.BaseTvColumns androidx.tvprovider.media.tv.TvContractCompat.PreviewProgramColumns {
+ field public static final String COLUMN_AUDIO_LANGUAGE = "audio_language";
+ field public static final String COLUMN_CANONICAL_GENRE = "canonical_genre";
+ field public static final String COLUMN_CHANNEL_ID = "channel_id";
+ field public static final String COLUMN_CONTENT_RATING = "content_rating";
+ field public static final String COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number";
+ field public static final String COLUMN_EPISODE_TITLE = "episode_title";
+ field public static final String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
+ field public static final String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
+ field public static final String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
+ field public static final String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
+ field public static final String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+ field public static final String COLUMN_LONG_DESCRIPTION = "long_description";
+ field public static final String COLUMN_POSTER_ART_URI = "poster_art_uri";
+ field public static final String COLUMN_REVIEW_RATING = "review_rating";
+ field public static final String COLUMN_REVIEW_RATING_STYLE = "review_rating_style";
+ field public static final String COLUMN_SEARCHABLE = "searchable";
+ field public static final String COLUMN_SEASON_DISPLAY_NUMBER = "season_display_number";
+ field public static final String COLUMN_SEASON_TITLE = "season_title";
+ field public static final String COLUMN_SERIES_ID = "series_id";
+ field public static final String COLUMN_SHORT_DESCRIPTION = "short_description";
+ field public static final String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
+ field public static final String COLUMN_TITLE = "title";
+ field public static final String COLUMN_VERSION_NUMBER = "version_number";
+ field public static final String COLUMN_VIDEO_HEIGHT = "video_height";
+ field public static final String COLUMN_VIDEO_WIDTH = "video_width";
+ field public static final String COLUMN_WEIGHT = "weight";
+ field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/preview_program";
+ field public static final String CONTENT_TYPE = "vnd.android.cursor.dir/preview_program";
+ field public static final android.net.Uri! CONTENT_URI;
+ field public static final int REVIEW_RATING_STYLE_PERCENTAGE = 2; // 0x2
+ field public static final int REVIEW_RATING_STYLE_STARS = 0; // 0x0
+ field public static final int REVIEW_RATING_STYLE_THUMBS_UP_DOWN = 1; // 0x1
+ }
+
public static final class TvContractCompat.Programs implements androidx.tvprovider.media.tv.TvContractCompat.BaseTvColumns {
field public static final String COLUMN_AUDIO_LANGUAGE = "audio_language";
field public static final String COLUMN_BROADCAST_GENRE = "broadcast_genre";
@@ -662,54 +666,20 @@
field public static final int REVIEW_RATING_STYLE_THUMBS_UP_DOWN = 1; // 0x1
}
- public static final class TvContractCompat.WatchNextPrograms implements androidx.tvprovider.media.tv.TvContractCompat.BaseTvColumns {
- field public static final int ASPECT_RATIO_16_9 = 0; // 0x0
- field public static final int ASPECT_RATIO_1_1 = 3; // 0x3
- field public static final int ASPECT_RATIO_2_3 = 4; // 0x4
- field public static final int ASPECT_RATIO_3_2 = 1; // 0x1
- field public static final int ASPECT_RATIO_3_4 = 6; // 0x6
- field public static final int ASPECT_RATIO_4_3 = 2; // 0x2
- field public static final int ASPECT_RATIO_MOVIE_POSTER = 5; // 0x5
- field public static final int AVAILABILITY_AVAILABLE = 0; // 0x0
- field public static final int AVAILABILITY_FREE = 4; // 0x4
- field public static final int AVAILABILITY_FREE_WITH_ADS = 5; // 0x5
- field public static final int AVAILABILITY_FREE_WITH_SUBSCRIPTION = 1; // 0x1
- field public static final int AVAILABILITY_PAID_CONTENT = 2; // 0x2
- field public static final int AVAILABILITY_PURCHASED = 3; // 0x3
+ public static final class TvContractCompat.WatchNextPrograms implements androidx.tvprovider.media.tv.TvContractCompat.BaseTvColumns androidx.tvprovider.media.tv.TvContractCompat.PreviewProgramColumns {
field public static final String COLUMN_AUDIO_LANGUAGE = "audio_language";
- field public static final String COLUMN_AUTHOR = "author";
- field public static final String COLUMN_AVAILABILITY = "availability";
- field public static final String COLUMN_BROWSABLE = "browsable";
field public static final String COLUMN_CANONICAL_GENRE = "canonical_genre";
- field public static final String COLUMN_CONTENT_ID = "content_id";
field public static final String COLUMN_CONTENT_RATING = "content_rating";
- field public static final String COLUMN_DURATION_MILLIS = "duration_millis";
- field public static final String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis";
field public static final String COLUMN_EPISODE_DISPLAY_NUMBER = "episode_display_number";
field public static final String COLUMN_EPISODE_TITLE = "episode_title";
- field public static final String COLUMN_GENRE = "genre";
- field public static final String COLUMN_INTENT_URI = "intent_uri";
- field public static final String COLUMN_INTERACTION_COUNT = "interaction_count";
- field public static final String COLUMN_INTERACTION_TYPE = "interaction_type";
field public static final String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
field public static final String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
field public static final String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
field public static final String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
field public static final String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
- field public static final String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id";
- field public static final String COLUMN_ITEM_COUNT = "item_count";
field public static final String COLUMN_LAST_ENGAGEMENT_TIME_UTC_MILLIS = "last_engagement_time_utc_millis";
- field public static final String COLUMN_LAST_PLAYBACK_POSITION_MILLIS = "last_playback_position_millis";
- field public static final String COLUMN_LIVE = "live";
- field public static final String COLUMN_LOGO_CONTENT_DESCRIPTION = "logo_content_description";
- field public static final String COLUMN_LOGO_URI = "logo_uri";
field public static final String COLUMN_LONG_DESCRIPTION = "long_description";
- field public static final String COLUMN_OFFER_PRICE = "offer_price";
- field public static final String COLUMN_POSTER_ART_ASPECT_RATIO = "poster_art_aspect_ratio";
field public static final String COLUMN_POSTER_ART_URI = "poster_art_uri";
- field public static final String COLUMN_PREVIEW_AUDIO_URI = "preview_audio_uri";
- field public static final String COLUMN_PREVIEW_VIDEO_URI = "preview_video_uri";
- field public static final String COLUMN_RELEASE_DATE = "release_date";
field public static final String COLUMN_REVIEW_RATING = "review_rating";
field public static final String COLUMN_REVIEW_RATING_STYLE = "review_rating_style";
field public static final String COLUMN_SEARCHABLE = "searchable";
@@ -717,14 +687,8 @@
field public static final String COLUMN_SEASON_TITLE = "season_title";
field public static final String COLUMN_SERIES_ID = "series_id";
field public static final String COLUMN_SHORT_DESCRIPTION = "short_description";
- field public static final String COLUMN_STARTING_PRICE = "starting_price";
- field public static final String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis";
- field public static final String COLUMN_THUMBNAIL_ASPECT_RATIO = "poster_thumbnail_aspect_ratio";
field public static final String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
field public static final String COLUMN_TITLE = "title";
- field public static final String COLUMN_TRANSIENT = "transient";
- field public static final String COLUMN_TV_SERIES_ITEM_TYPE = "tv_series_item_type";
- field public static final String COLUMN_TYPE = "type";
field public static final String COLUMN_VERSION_NUMBER = "version_number";
field public static final String COLUMN_VIDEO_HEIGHT = "video_height";
field public static final String COLUMN_VIDEO_WIDTH = "video_width";
@@ -732,31 +696,9 @@
field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/watch_next_program";
field public static final String CONTENT_TYPE = "vnd.android.cursor.dir/watch_next_program";
field public static final android.net.Uri! CONTENT_URI;
- field public static final int INTERACTION_TYPE_FANS = 3; // 0x3
- field public static final int INTERACTION_TYPE_FOLLOWERS = 2; // 0x2
- field public static final int INTERACTION_TYPE_LIKES = 4; // 0x4
- field public static final int INTERACTION_TYPE_LISTENS = 1; // 0x1
- field public static final int INTERACTION_TYPE_THUMBS = 5; // 0x5
- field public static final int INTERACTION_TYPE_VIEWERS = 6; // 0x6
- field public static final int INTERACTION_TYPE_VIEWS = 0; // 0x0
field public static final int REVIEW_RATING_STYLE_PERCENTAGE = 2; // 0x2
field public static final int REVIEW_RATING_STYLE_STARS = 0; // 0x0
field public static final int REVIEW_RATING_STYLE_THUMBS_UP_DOWN = 1; // 0x1
- field public static final int TV_SERIES_ITEM_TYPE_CHAPTER = 1; // 0x1
- field public static final int TV_SERIES_ITEM_TYPE_EPISODE = 0; // 0x0
- field public static final int TYPE_ALBUM = 8; // 0x8
- field public static final int TYPE_ARTIST = 9; // 0x9
- field public static final int TYPE_CHANNEL = 6; // 0x6
- field public static final int TYPE_CLIP = 4; // 0x4
- field public static final int TYPE_EVENT = 5; // 0x5
- field public static final int TYPE_GAME = 12; // 0xc
- field public static final int TYPE_MOVIE = 0; // 0x0
- field public static final int TYPE_PLAYLIST = 10; // 0xa
- field public static final int TYPE_STATION = 11; // 0xb
- field public static final int TYPE_TRACK = 7; // 0x7
- field public static final int TYPE_TV_EPISODE = 3; // 0x3
- field public static final int TYPE_TV_SEASON = 2; // 0x2
- field public static final int TYPE_TV_SERIES = 1; // 0x1
field public static final int WATCH_NEXT_TYPE_CONTINUE = 0; // 0x0
field public static final int WATCH_NEXT_TYPE_NEW = 2; // 0x2
field public static final int WATCH_NEXT_TYPE_NEXT = 1; // 0x1
@@ -821,6 +763,7 @@
method public boolean isSearchable();
method public boolean isTransient();
method public android.content.ContentValues! toContentValues();
+ field public static final String![] PROJECTION;
field public static final int WATCH_NEXT_TYPE_UNKNOWN = -1; // 0xffffffff
}
diff --git a/tvprovider/tvprovider/api/restricted_current.txt b/tvprovider/tvprovider/api/restricted_current.txt
index aa21de7..217e357 100644
--- a/tvprovider/tvprovider/api/restricted_current.txt
+++ b/tvprovider/tvprovider/api/restricted_current.txt
@@ -224,7 +224,7 @@
method public boolean isTransient();
method public android.content.ContentValues! toContentValues();
method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.ContentValues! toContentValues(boolean);
- field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static final String![]! PROJECTION;
+ field public static final String![] PROJECTION;
}
public static final class PreviewProgram.Builder {
@@ -517,7 +517,7 @@
@RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @StringDef({androidx.tvprovider.media.tv.TvContractCompat.Channels.VIDEO_RESOLUTION_SD, androidx.tvprovider.media.tv.TvContractCompat.Channels.VIDEO_RESOLUTION_ED, androidx.tvprovider.media.tv.TvContractCompat.Channels.VIDEO_RESOLUTION_HD, androidx.tvprovider.media.tv.TvContractCompat.Channels.VIDEO_RESOLUTION_FHD, androidx.tvprovider.media.tv.TvContractCompat.Channels.VIDEO_RESOLUTION_UHD}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface TvContractCompat.Channels.VideoResolution {
}
- @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static interface TvContractCompat.PreviewProgramColumns {
+ public static interface TvContractCompat.PreviewProgramColumns {
field public static final int ASPECT_RATIO_16_9 = 0; // 0x0
field public static final int ASPECT_RATIO_1_1 = 3; // 0x3
field public static final int ASPECT_RATIO_2_3 = 4; // 0x4
@@ -829,7 +829,7 @@
method public boolean isTransient();
method public android.content.ContentValues! toContentValues();
method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.content.ContentValues! toContentValues(boolean);
- field @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public static final String![]! PROJECTION;
+ field public static final String![] PROJECTION;
field public static final int WATCH_NEXT_TYPE_UNKNOWN = -1; // 0xffffffff
}
diff --git a/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/PreviewProgram.java b/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/PreviewProgram.java
index afe4160..9a6037a 100644
--- a/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/PreviewProgram.java
+++ b/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/PreviewProgram.java
@@ -21,6 +21,7 @@
import android.database.Cursor;
import android.os.Build;
+import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
import androidx.tvprovider.media.tv.TvContractCompat.PreviewPrograms;
@@ -76,8 +77,12 @@
*/
public final class PreviewProgram extends BasePreviewProgram {
/**
+ * The projection for a {@link PreviewProgram} query.
+ * <p> This provides a array of strings containing the columns to be used in the
+ * query and in creating a Cursor object, which is used to iterate through the rows in the
+ * table.
*/
- @RestrictTo(LIBRARY_GROUP_PREFIX)
+ @NonNull
public static final String[] PROJECTION = getProjection();
private static final long INVALID_LONG_VALUE = -1;
diff --git a/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/TvContractCompat.java b/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/TvContractCompat.java
index 7d72b57..2725306 100644
--- a/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/TvContractCompat.java
+++ b/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/TvContractCompat.java
@@ -914,7 +914,6 @@
/**
* Common columns for the tables of preview programs.
*/
- @RestrictTo(LIBRARY_GROUP_PREFIX)
public interface PreviewProgramColumns {
/**
* The program type for movie.
diff --git a/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/WatchNextProgram.java b/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/WatchNextProgram.java
index edcb4d1..475aa87 100644
--- a/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/WatchNextProgram.java
+++ b/tvprovider/tvprovider/src/main/java/androidx/tvprovider/media/tv/WatchNextProgram.java
@@ -22,6 +22,7 @@
import android.os.Build;
import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
import androidx.tvprovider.media.tv.TvContractCompat.WatchNextPrograms;
@@ -79,8 +80,12 @@
*/
public final class WatchNextProgram extends BasePreviewProgram {
/**
+ * The projection for a {@link WatchNextProgram} query.
+ * <p> This provides a array of strings containing the columns to be used in the
+ * query and in creating a Cursor object, which is used to iterate through the rows in the
+ * table.
*/
- @RestrictTo(LIBRARY_GROUP_PREFIX)
+ @NonNull
public static final String[] PROJECTION = getProjection();
private static final long INVALID_LONG_VALUE = -1;
diff --git a/wear/compose/compose-foundation/api/current.txt b/wear/compose/compose-foundation/api/current.txt
index 2730513d..d723269 100644
--- a/wear/compose/compose-foundation/api/current.txt
+++ b/wear/compose/compose-foundation/api/current.txt
@@ -139,7 +139,7 @@
public final class CurvedParentDataKt {
method public static androidx.wear.compose.foundation.CurvedModifier parentDataModifier(androidx.wear.compose.foundation.CurvedModifier, kotlin.jvm.functions.Function1<java.lang.Object,?> modifyParentData);
- method public static androidx.wear.compose.foundation.CurvedModifier weight(androidx.wear.compose.foundation.CurvedModifier, float weight);
+ method public static androidx.wear.compose.foundation.CurvedModifier weight(androidx.wear.compose.foundation.CurvedModifier, @FloatRange(from=0.0, fromInclusive=false) float weight);
}
public final class CurvedRowKt {
@@ -153,8 +153,8 @@
method public static androidx.wear.compose.foundation.CurvedModifier angularSize(androidx.wear.compose.foundation.CurvedModifier, float sweepDegrees);
method public static androidx.wear.compose.foundation.CurvedModifier angularSizeDp(androidx.wear.compose.foundation.CurvedModifier, float angularWidth);
method public static androidx.wear.compose.foundation.CurvedModifier radialSize(androidx.wear.compose.foundation.CurvedModifier, float thickness);
- method public static androidx.wear.compose.foundation.CurvedModifier size(androidx.wear.compose.foundation.CurvedModifier, float sweepDegrees, float thickness);
- method public static androidx.wear.compose.foundation.CurvedModifier sizeIn(androidx.wear.compose.foundation.CurvedModifier, optional float minSweepDegrees, optional float maxSweepDegrees, optional float minThickness, optional float maxThickness);
+ method public static androidx.wear.compose.foundation.CurvedModifier size(androidx.wear.compose.foundation.CurvedModifier, @FloatRange(from=0.0, to=360.0) float sweepDegrees, float thickness);
+ method public static androidx.wear.compose.foundation.CurvedModifier sizeIn(androidx.wear.compose.foundation.CurvedModifier, optional @FloatRange(from=0.0, to=360.0) float minSweepDegrees, optional @FloatRange(from=0.0, to=360.0) float maxSweepDegrees, optional float minThickness, optional float maxThickness);
}
public final class CurvedTextStyle {
@@ -357,9 +357,9 @@
}
@androidx.compose.runtime.Stable @androidx.wear.compose.foundation.lazy.ScalingLazyScopeMarker public sealed interface ScalingLazyListItemScope {
- method public androidx.compose.ui.Modifier fillParentMaxHeight(androidx.compose.ui.Modifier, optional float fraction);
- method public androidx.compose.ui.Modifier fillParentMaxSize(androidx.compose.ui.Modifier, optional float fraction);
- method public androidx.compose.ui.Modifier fillParentMaxWidth(androidx.compose.ui.Modifier, optional float fraction);
+ method public androidx.compose.ui.Modifier fillParentMaxHeight(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
+ method public androidx.compose.ui.Modifier fillParentMaxSize(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
+ method public androidx.compose.ui.Modifier fillParentMaxWidth(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
}
public sealed interface ScalingLazyListLayoutInfo {
@@ -426,20 +426,20 @@
}
@androidx.compose.runtime.Stable public interface ScalingParams {
- method public float getEdgeAlpha();
- method public float getEdgeScale();
- method public float getMaxElementHeight();
- method public float getMaxTransitionArea();
- method public float getMinElementHeight();
- method public float getMinTransitionArea();
+ method @FloatRange(from=0.0, to=1.0) public float getEdgeAlpha();
+ method @FloatRange(from=0.0, to=1.0) public float getEdgeScale();
+ method @FloatRange(from=0.0, to=1.0) public float getMaxElementHeight();
+ method @FloatRange(from=0.0, to=1.0) public float getMaxTransitionArea();
+ method @FloatRange(from=0.0, to=1.0) public float getMinElementHeight();
+ method @FloatRange(from=0.0, to=1.0) public float getMinTransitionArea();
method public androidx.compose.animation.core.Easing getScaleInterpolator();
method public int resolveViewportVerticalOffset(long viewportConstraints);
- property public abstract float edgeAlpha;
- property public abstract float edgeScale;
- property public abstract float maxElementHeight;
- property public abstract float maxTransitionArea;
- property public abstract float minElementHeight;
- property public abstract float minTransitionArea;
+ property @FloatRange(from=0.0, to=1.0) public abstract float edgeAlpha;
+ property @FloatRange(from=0.0, to=1.0) public abstract float edgeScale;
+ property @FloatRange(from=0.0, to=1.0) public abstract float maxElementHeight;
+ property @FloatRange(from=0.0, to=1.0) public abstract float maxTransitionArea;
+ property @FloatRange(from=0.0, to=1.0) public abstract float minElementHeight;
+ property @FloatRange(from=0.0, to=1.0) public abstract float minTransitionArea;
property public abstract androidx.compose.animation.core.Easing scaleInterpolator;
}
diff --git a/wear/compose/compose-foundation/api/restricted_current.txt b/wear/compose/compose-foundation/api/restricted_current.txt
index 2730513d..d723269 100644
--- a/wear/compose/compose-foundation/api/restricted_current.txt
+++ b/wear/compose/compose-foundation/api/restricted_current.txt
@@ -139,7 +139,7 @@
public final class CurvedParentDataKt {
method public static androidx.wear.compose.foundation.CurvedModifier parentDataModifier(androidx.wear.compose.foundation.CurvedModifier, kotlin.jvm.functions.Function1<java.lang.Object,?> modifyParentData);
- method public static androidx.wear.compose.foundation.CurvedModifier weight(androidx.wear.compose.foundation.CurvedModifier, float weight);
+ method public static androidx.wear.compose.foundation.CurvedModifier weight(androidx.wear.compose.foundation.CurvedModifier, @FloatRange(from=0.0, fromInclusive=false) float weight);
}
public final class CurvedRowKt {
@@ -153,8 +153,8 @@
method public static androidx.wear.compose.foundation.CurvedModifier angularSize(androidx.wear.compose.foundation.CurvedModifier, float sweepDegrees);
method public static androidx.wear.compose.foundation.CurvedModifier angularSizeDp(androidx.wear.compose.foundation.CurvedModifier, float angularWidth);
method public static androidx.wear.compose.foundation.CurvedModifier radialSize(androidx.wear.compose.foundation.CurvedModifier, float thickness);
- method public static androidx.wear.compose.foundation.CurvedModifier size(androidx.wear.compose.foundation.CurvedModifier, float sweepDegrees, float thickness);
- method public static androidx.wear.compose.foundation.CurvedModifier sizeIn(androidx.wear.compose.foundation.CurvedModifier, optional float minSweepDegrees, optional float maxSweepDegrees, optional float minThickness, optional float maxThickness);
+ method public static androidx.wear.compose.foundation.CurvedModifier size(androidx.wear.compose.foundation.CurvedModifier, @FloatRange(from=0.0, to=360.0) float sweepDegrees, float thickness);
+ method public static androidx.wear.compose.foundation.CurvedModifier sizeIn(androidx.wear.compose.foundation.CurvedModifier, optional @FloatRange(from=0.0, to=360.0) float minSweepDegrees, optional @FloatRange(from=0.0, to=360.0) float maxSweepDegrees, optional float minThickness, optional float maxThickness);
}
public final class CurvedTextStyle {
@@ -357,9 +357,9 @@
}
@androidx.compose.runtime.Stable @androidx.wear.compose.foundation.lazy.ScalingLazyScopeMarker public sealed interface ScalingLazyListItemScope {
- method public androidx.compose.ui.Modifier fillParentMaxHeight(androidx.compose.ui.Modifier, optional float fraction);
- method public androidx.compose.ui.Modifier fillParentMaxSize(androidx.compose.ui.Modifier, optional float fraction);
- method public androidx.compose.ui.Modifier fillParentMaxWidth(androidx.compose.ui.Modifier, optional float fraction);
+ method public androidx.compose.ui.Modifier fillParentMaxHeight(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
+ method public androidx.compose.ui.Modifier fillParentMaxSize(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
+ method public androidx.compose.ui.Modifier fillParentMaxWidth(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
}
public sealed interface ScalingLazyListLayoutInfo {
@@ -426,20 +426,20 @@
}
@androidx.compose.runtime.Stable public interface ScalingParams {
- method public float getEdgeAlpha();
- method public float getEdgeScale();
- method public float getMaxElementHeight();
- method public float getMaxTransitionArea();
- method public float getMinElementHeight();
- method public float getMinTransitionArea();
+ method @FloatRange(from=0.0, to=1.0) public float getEdgeAlpha();
+ method @FloatRange(from=0.0, to=1.0) public float getEdgeScale();
+ method @FloatRange(from=0.0, to=1.0) public float getMaxElementHeight();
+ method @FloatRange(from=0.0, to=1.0) public float getMaxTransitionArea();
+ method @FloatRange(from=0.0, to=1.0) public float getMinElementHeight();
+ method @FloatRange(from=0.0, to=1.0) public float getMinTransitionArea();
method public androidx.compose.animation.core.Easing getScaleInterpolator();
method public int resolveViewportVerticalOffset(long viewportConstraints);
- property public abstract float edgeAlpha;
- property public abstract float edgeScale;
- property public abstract float maxElementHeight;
- property public abstract float maxTransitionArea;
- property public abstract float minElementHeight;
- property public abstract float minTransitionArea;
+ property @FloatRange(from=0.0, to=1.0) public abstract float edgeAlpha;
+ property @FloatRange(from=0.0, to=1.0) public abstract float edgeScale;
+ property @FloatRange(from=0.0, to=1.0) public abstract float maxElementHeight;
+ property @FloatRange(from=0.0, to=1.0) public abstract float maxTransitionArea;
+ property @FloatRange(from=0.0, to=1.0) public abstract float minElementHeight;
+ property @FloatRange(from=0.0, to=1.0) public abstract float minTransitionArea;
property public abstract androidx.compose.animation.core.Easing scaleInterpolator;
}
diff --git a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/CurvedParentData.kt b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/CurvedParentData.kt
index aedafea..14d4b39 100644
--- a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/CurvedParentData.kt
+++ b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/CurvedParentData.kt
@@ -16,6 +16,8 @@
package androidx.wear.compose.foundation
+import androidx.annotation.FloatRange
+
/**
* A [CurvedModifier] that provides data to the parent layout.
* The parent data is commonly used to inform the parent how the child Layout should be measured
@@ -41,7 +43,7 @@
* all weighted siblings. Must be positive.
*/
public fun CurvedModifier.weight(
- /* @FloatRange(from = 0f, fromInclusive = false) */
+ @FloatRange(from = 0.0, fromInclusive = false)
weight: Float
) = parentDataModifier { parentData ->
require(weight > 0f) { "Weights must be positive." }
diff --git a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/CurvedSize.kt b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/CurvedSize.kt
index 8e0408c..6c90d7a 100644
--- a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/CurvedSize.kt
+++ b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/CurvedSize.kt
@@ -16,6 +16,7 @@
package androidx.wear.compose.foundation
+import androidx.annotation.FloatRange
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.layout.Measurable
import androidx.compose.ui.unit.Dp
@@ -30,9 +31,9 @@
* @param maxThickness the maximum thickness (radial size) for the content.
*/
public fun CurvedModifier.sizeIn(
- /* @FloatRange(from = 0f, to = 360f) */
+ @FloatRange(from = 0.0, to = 360.0)
minSweepDegrees: Float = 0f,
- /* @FloatRange(from = 0f, to = 360f) */
+ @FloatRange(from = 0.0, to = 360.0)
maxSweepDegrees: Float = 360f,
minThickness: Dp = 0.dp,
maxThickness: Dp = Dp.Infinity,
@@ -54,10 +55,12 @@
* @param sweepDegrees Indicates the sweep (angular size) of the content.
* @param thickness Indicates the thickness (radial size) of the content.
*/
-public fun CurvedModifier.size(sweepDegrees: Float, thickness: Dp) = sizeIn(
- /* @FloatRange(from = 0f, to = 360f) */
+public fun CurvedModifier.size(
+ @FloatRange(from = 0.0, to = 360.0)
+ sweepDegrees: Float,
+ thickness: Dp
+) = sizeIn(
minSweepDegrees = sweepDegrees,
- /* @FloatRange(from = 0f, to = 360f) */
maxSweepDegrees = sweepDegrees,
minThickness = thickness,
maxThickness = thickness
diff --git a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/SwipeToReveal.kt b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/SwipeToReveal.kt
index 2f4d683..7662003 100644
--- a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/SwipeToReveal.kt
+++ b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/SwipeToReveal.kt
@@ -260,10 +260,10 @@
*
* @param action The mandatory action that needs to be added to the component.
* @param modifier Optional [Modifier] for this component.
- * @param state The [RevealState] of this component. It can be used to customise the anchors
- * and threshold config of the swipeable modifier which is applied.
* @param onFullSwipe An optional lambda which will be triggered when a full swipe from either of
* the anchors is performed.
+ * @param state The [RevealState] of this component. It can be used to customise the anchors
+ * and threshold config of the swipeable modifier which is applied.
* @param additionalAction The optional action that can be added to the component.
* @param undoAction The optional undo action that will be applied to the component once the
* mandatory action has been performed.
@@ -388,12 +388,13 @@
* anchor for [RevealValue.Revealing].
* If there is no such anchor defined for [RevealValue.Revealing], it returns 0.0f.
*/
+ /* @FloatRange(from = 0.0) */
public val revealOffset: Float
}
@OptIn(ExperimentalWearFoundationApi::class)
private class RevealScopeImpl constructor(
- private val revealState: RevealState
+ val revealState: RevealState,
) : RevealScope {
/**
@@ -430,7 +431,9 @@
content: @Composable RevealScope.() -> Unit
) {
Box(
- modifier = modifier.fillMaxHeight().weight(weight),
+ modifier = modifier
+ .fillMaxHeight()
+ .weight(weight),
contentAlignment = Alignment.Center
) {
with(revealScope) {
diff --git a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/SwipeableV2.kt b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/SwipeableV2.kt
index 15a2dde..ff97315 100644
--- a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/SwipeableV2.kt
+++ b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/SwipeableV2.kt
@@ -16,6 +16,7 @@
package androidx.wear.compose.foundation
+import androidx.annotation.FloatRange
import androidx.annotation.RestrictTo
import androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP
import androidx.compose.animation.core.AnimationSpec
@@ -296,7 +297,7 @@
* The fraction of the progress going from [currentValue] to [targetValue], within [0f..1f]
* bounds.
*/
- /*@FloatRange(from = 0f, to = 1f)*/
+ @get:FloatRange(from = 0.0, to = 1.0)
val progress: Float by derivedStateOf {
val a = anchors[currentValue] ?: 0f
val b = anchors[targetValue] ?: 0f
diff --git a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/ScalingLazyColumnMeasure.kt b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/ScalingLazyColumnMeasure.kt
index 9f65cce..7584d68 100644
--- a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/ScalingLazyColumnMeasure.kt
+++ b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/ScalingLazyColumnMeasure.kt
@@ -16,6 +16,7 @@
package androidx.wear.compose.foundation.lazy
+import androidx.annotation.FloatRange
import androidx.annotation.RestrictTo
import androidx.compose.animation.core.Easing
import androidx.compose.foundation.gestures.Orientation
@@ -100,9 +101,7 @@
* scaled, e.g. at the edge of the viewport. A value between [0f,1f], so a value of 0.2f
* means to scale an item to 20% of its normal size.
*/
-// @FloatRange(
-// fromInclusive = true, from = 0.0, toInclusive = true, to = 1.0
-// )
+ @get:FloatRange(from = 0.0, to = 1.0)
val edgeScale: Float
/**
@@ -110,9 +109,7 @@
* when closest to the edge of the screen. A value between [0f,1f], so a value of
* 0.2f means to set the alpha of an item to 20% of its normal value.
*/
-// @FloatRange(
-// fromInclusive = true, from = 0.0, toInclusive = true, to = 1.0
-// )
+ @get:FloatRange(from = 0.0, to = 1.0)
val edgeAlpha: Float
/**
@@ -122,9 +119,7 @@
* will be treated as if [maxElementHeight]. Must be greater than or equal to
* [minElementHeight].
*/
-// @FloatRange(
-// fromInclusive = true, from = 0.0, toInclusive = true, to = 1.0
-// )
+ @get:FloatRange(from = 0.0, to = 1.0)
val minElementHeight: Float
/**
@@ -134,9 +129,7 @@
* will be treated as if [maxElementHeight]. Must be greater than or equal to
* [minElementHeight].
*/
-// @FloatRange(
-// fromInclusive = true, from = 0.0, toInclusive = true, to = 1.0
-// )
+ @get:FloatRange(from = 0.0, to = 1.0)
val maxElementHeight: Float
/**
@@ -153,9 +146,7 @@
* list items exist. Depending on the size of the list item the specific point in the area is
* calculated.
*/
-// @FloatRange(
-// fromInclusive = true, from = 0.0, toInclusive = true, to = 1.0
-// )
+ @get:FloatRange(from = 0.0, to = 1.0)
val minTransitionArea: Float
/**
@@ -172,9 +163,7 @@
* list items exist. Depending on the size of the list item the specific point in the area is
* calculated.
*/
-// @FloatRange(
-// fromInclusive = true, from = 0.0, toInclusive = true, to = 1.0
-// )
+ @get:FloatRange(from = 0.0, to = 1.0)
val maxTransitionArea: Float
/**
diff --git a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/ScalingLazyListItemScope.kt b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/ScalingLazyListItemScope.kt
index 042e928..aac594e 100644
--- a/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/ScalingLazyListItemScope.kt
+++ b/wear/compose/compose-foundation/src/main/java/androidx/wear/compose/foundation/lazy/ScalingLazyListItemScope.kt
@@ -15,6 +15,7 @@
*/
package androidx.wear.compose.foundation.lazy
+import androidx.annotation.FloatRange
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
@@ -42,7 +43,7 @@
* measured with [Constraints.Infinity] as the constraints for the main axis.
*/
fun Modifier.fillParentMaxSize(
- /*@FloatRange(from = 0.0, to = 1.0)*/
+ @FloatRange(from = 0.0, to = 1.0)
fraction: Float = 1f
): Modifier
@@ -57,7 +58,7 @@
* items are measured with [Constraints.Infinity] as the constraints for the main axis.
*/
fun Modifier.fillParentMaxWidth(
- /*@FloatRange(from = 0.0, to = 1.0)*/
+ @FloatRange(from = 0.0, to = 1.0)
fraction: Float = 1f
): Modifier
@@ -72,7 +73,7 @@
* items are measured with [Constraints.Infinity] as the constraints for the main axis.
*/
fun Modifier.fillParentMaxHeight(
- /*@FloatRange(from = 0.0, to = 1.0)*/
+ @FloatRange(from = 0.0, to = 1.0)
fraction: Float = 1f
): Modifier
}
diff --git a/wear/compose/compose-material-core/src/androidTest/kotlin/androidx/wear/compose/materialcore/ToggleButtonTest.kt b/wear/compose/compose-material-core/src/androidTest/kotlin/androidx/wear/compose/materialcore/ToggleButtonTest.kt
index 1b93f3a..6bd0e3c 100644
--- a/wear/compose/compose-material-core/src/androidTest/kotlin/androidx/wear/compose/materialcore/ToggleButtonTest.kt
+++ b/wear/compose/compose-material-core/src/androidTest/kotlin/androidx/wear/compose/materialcore/ToggleButtonTest.kt
@@ -18,6 +18,7 @@
import android.os.Build
import androidx.annotation.RequiresApi
+import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
@@ -25,9 +26,14 @@
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.requiredSize
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.CornerSize
+import androidx.compose.foundation.shape.CutCornerShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -35,16 +41,21 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.compositeOver
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.semantics.SemanticsProperties
import androidx.compose.ui.test.SemanticsMatcher
import androidx.compose.ui.test.assert
import androidx.compose.ui.test.assertHasClickAction
+import androidx.compose.ui.test.assertHeightIsEqualTo
import androidx.compose.ui.test.assertIsEnabled
import androidx.compose.ui.test.assertIsNotEnabled
import androidx.compose.ui.test.assertIsOff
import androidx.compose.ui.test.assertIsOn
+import androidx.compose.ui.test.assertTouchHeightIsEqualTo
+import androidx.compose.ui.test.assertTouchWidthIsEqualTo
+import androidx.compose.ui.test.assertWidthIsEqualTo
import androidx.compose.ui.test.captureToImage
import androidx.compose.ui.test.isToggleable
import androidx.compose.ui.test.junit4.createComposeRule
@@ -53,7 +64,9 @@
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
+import org.junit.Assert
import org.junit.Rule
import org.junit.Test
@@ -61,8 +74,353 @@
@get:Rule
val rule = createComposeRule()
+ /* Round Toggle buttons */
@Test
- fun supports_testtag() {
+ fun round_toggle_button_supports_testTag() {
+ rule.setContent {
+ RoundToggleButtonWithDefaults(
+ modifier = Modifier.testTag(TEST_TAG),
+ )
+ }
+
+ rule.onNodeWithTag(TEST_TAG).assertExists()
+ }
+
+ @Test
+ fun round_toggle_button_is_toggleable() {
+ rule.setContent {
+ RoundToggleButtonWithDefaults(
+ enabled = true,
+ modifier = Modifier.testTag(TEST_TAG)
+ )
+ }
+
+ rule.onNode(isToggleable()).assertExists()
+ }
+
+ @Test
+ fun round_toggle_button_has_click_action_when_enabled() {
+ rule.setContent {
+ RoundToggleButtonWithDefaults(
+ enabled = true,
+ modifier = Modifier.testTag(TEST_TAG)
+ )
+ }
+
+ rule.onNodeWithTag(TEST_TAG).assertHasClickAction()
+ }
+
+ @Test
+ fun round_toggle_button_has_click_action_when_disabled() {
+ rule.setContent {
+ RoundToggleButtonWithDefaults(
+ enabled = false,
+ modifier = Modifier.testTag(TEST_TAG)
+ )
+ }
+
+ rule.onNodeWithTag(TEST_TAG).assertHasClickAction()
+ }
+
+ @Test
+ fun round_toggle_button_is_correctly_enabled() {
+ rule.setContent {
+ RoundToggleButtonWithDefaults(
+ enabled = true,
+ modifier = Modifier.testTag(TEST_TAG)
+ )
+ }
+
+ rule.onNodeWithTag(TEST_TAG).assertIsEnabled()
+ }
+
+ @Test
+ fun round_toggle_button_is_correctly_disabled() {
+ rule.setContent {
+ RoundToggleButtonWithDefaults(
+ enabled = false,
+ modifier = Modifier.testTag(TEST_TAG)
+ )
+ }
+
+ rule.onNodeWithTag(TEST_TAG).assertIsNotEnabled()
+ }
+
+ @Test
+ fun round_toggle_button_is_on_when_checked() {
+ rule.setContent {
+ RoundToggleButtonWithDefaults(
+ enabled = true,
+ checked = true,
+ modifier = Modifier.testTag(TEST_TAG)
+ )
+ }
+
+ rule.onNodeWithTag(TEST_TAG).assertIsOn()
+ }
+
+ @Test
+ fun round_toggle_button_is_off_when_unchecked() {
+ rule.setContent {
+ RoundToggleButtonWithDefaults(
+ enabled = true,
+ checked = false,
+ modifier = Modifier.testTag(TEST_TAG)
+ )
+ }
+
+ rule.onNodeWithTag(TEST_TAG).assertIsOff()
+ }
+
+ @Test
+ fun round_toggle_button_toggles_when_enabled() {
+ var clicked = false
+
+ rule.setContent {
+ RoundToggleButtonWithDefaults(
+ enabled = true,
+ onCheckedChange = { clicked = true },
+ modifier = Modifier.testTag(TEST_TAG)
+ )
+ }
+
+ rule.onNodeWithTag(TEST_TAG).performClick()
+
+ rule.runOnIdle {
+ Assert.assertEquals(true, clicked)
+ }
+ }
+
+ @Test
+ fun round_toggle_button_responds_to_toggle_on() {
+ rule.setContent {
+ val (checked, onCheckedChange) = remember { mutableStateOf(false) }
+ RoundToggleButtonWithDefaults(
+ content = { TestImage() },
+ checked = checked,
+ onCheckedChange = onCheckedChange,
+ enabled = true,
+ modifier = Modifier.testTag(TEST_TAG)
+ )
+ }
+
+ rule
+ .onNodeWithTag(TEST_TAG)
+ .assertIsOff()
+ .performClick()
+ .assertIsOn()
+ }
+
+ @Test
+ fun round_toggle_button_responds_to_toggle_off() {
+ rule.setContent {
+ val (checked, onCheckedChange) = remember { mutableStateOf(true) }
+ RoundToggleButtonWithDefaults(
+ content = { TestImage() },
+ checked = checked,
+ onCheckedChange = onCheckedChange,
+ enabled = true,
+ modifier = Modifier.testTag(TEST_TAG)
+ )
+ }
+
+ rule
+ .onNodeWithTag(TEST_TAG)
+ .assertIsOn()
+ .performClick()
+ .assertIsOff()
+ }
+
+ @Test
+ fun round_toggle_button_does_not_respond_to_click_when_disabled() {
+ var clicked = false
+
+ rule.setContent {
+ RoundToggleButtonWithDefaults(
+ onCheckedChange = { clicked = true },
+ enabled = false,
+ modifier = Modifier.testTag(TEST_TAG)
+ )
+ }
+
+ rule.onNodeWithTag(TEST_TAG).performClick()
+
+ rule.runOnIdle {
+ Assert.assertEquals(false, clicked)
+ }
+ }
+
+ @Test
+ fun round_toggle_button_has_role_checkbox() {
+ rule.setContent {
+ RoundToggleButtonWithDefaults(
+ modifier = Modifier.testTag(TEST_TAG)
+ )
+ }
+
+ rule.onNodeWithTag(TEST_TAG)
+ .assert(
+ SemanticsMatcher.expectValue(
+ SemanticsProperties.Role,
+ Role.Checkbox
+ )
+ )
+ }
+
+ @Test
+ fun round_toggle_button_supports_circle_shape_under_ltr() =
+ rule.isShape(CircleShape, LayoutDirection.Ltr) {
+ RoundToggleButtonWithDefaults(
+ modifier = Modifier.testTag(TEST_TAG),
+ ) { }
+ }
+
+ @Test
+ fun round_toggle_button_supports_circle_shape_under_rtl() =
+ rule.isShape(CircleShape, LayoutDirection.Rtl) {
+ RoundToggleButtonWithDefaults(
+ modifier = Modifier.testTag(TEST_TAG),
+ ) { }
+ }
+
+ @Test
+ fun extra_small_round_toggle_button_meets_accessibility_tapSize() {
+ verifyTapSize(48.dp) {
+ RoundToggleButtonWithDefaults(
+ modifier = Modifier
+ .testTag(TEST_TAG)
+ .size(32.dp)
+ )
+ }
+ }
+
+ @Test
+ fun extra_small_round_toggle_button_has_correct_visible_size() {
+ verifyVisibleSize(32.dp) {
+ RoundToggleButtonWithDefaults(
+ modifier = Modifier
+ .testTag(TEST_TAG)
+ .requiredSize(32.dp)
+ )
+ }
+ }
+
+ @Test
+ fun default_round_toggle_button_has_correct_tapSize() {
+ // Tap size for Button should be 52.dp.
+ verifyTapSize(52.dp) {
+ RoundToggleButtonWithDefaults(
+ modifier = Modifier
+ .testTag(TEST_TAG)
+ .size(52.dp)
+ )
+ }
+ }
+
+ @Test
+ fun default_round_toggle_button_has_correct_visible_size() {
+ // Tap size for Button should be 52.dp.
+ verifyVisibleSize(52.dp) {
+ RoundToggleButtonWithDefaults(
+ modifier = Modifier
+ .testTag(TEST_TAG)
+ .size(52.dp)
+ )
+ }
+ }
+
+ @Test
+ fun round_toggle_button_allows_custom_shape_override() {
+ val shape = CutCornerShape(4.dp)
+
+ rule.isShape(shape, LayoutDirection.Ltr) {
+ RoundToggleButtonWithDefaults(
+ shape = shape,
+ modifier = Modifier.testTag(TEST_TAG)
+ ) { }
+ }
+ }
+
+ @RequiresApi(Build.VERSION_CODES.O)
+ @Test
+ fun round_toggle_button_gives_correct_colors_when_enabled() =
+ verifyToggleButtonColors(
+ enabled = true,
+ checked = false,
+ { enabled, _ -> remember { mutableStateOf(if (enabled) Color.Green else Color.Red) } },
+ { enabled, _ ->
+ remember { mutableStateOf(if (enabled) Color.Blue else Color.Yellow) }
+ },
+ Color.Green,
+ Color.Blue
+ )
+
+ @RequiresApi(Build.VERSION_CODES.O)
+ @Test
+ fun round_toggle_button_gives_correct_colors_when_disabled() =
+ verifyToggleButtonColors(
+ enabled = false,
+ checked = false,
+ { enabled, _ -> remember { mutableStateOf(if (enabled) Color.Green else Color.Red) } },
+ { enabled, _ ->
+ remember { mutableStateOf(if (enabled) Color.Blue else Color.Yellow) }
+ },
+ Color.Red,
+ Color.Yellow,
+ )
+
+ @RequiresApi(Build.VERSION_CODES.O)
+ @Test
+ fun round_toggle_button_gives_correct_colors_when_checked() =
+ verifyToggleButtonColors(
+ enabled = true,
+ checked = true,
+ { _, checked -> remember { mutableStateOf(if (checked) Color.Green else Color.Red) } },
+ { _, checked ->
+ remember { mutableStateOf(if (checked) Color.Blue else Color.Yellow) }
+ },
+ Color.Green,
+ Color.Blue,
+ )
+
+ @RequiresApi(Build.VERSION_CODES.O)
+ @Test
+ fun round_toggle_button_gives_correct_colors_when_unchecked() =
+ verifyToggleButtonColors(
+ enabled = true,
+ checked = false,
+ { _, checked -> remember { mutableStateOf(if (checked) Color.Green else Color.Red) } },
+ { _, checked ->
+ remember { mutableStateOf(if (checked) Color.Blue else Color.Yellow) }
+ },
+ Color.Red,
+ Color.Yellow,
+ )
+
+ @Test
+ fun round_toggle_button_obeys_content_provider_values() {
+ var data = -1
+
+ rule.setContent {
+ Box(modifier = Modifier.fillMaxSize()) {
+ RoundToggleButtonWithDefaults(
+ content = {
+ CompositionLocalProvider(
+ LocalContentTestData provides EXPECTED_LOCAL_TEST_DATA
+ ) {
+ data = LocalContentTestData.current
+ }
+ }
+ )
+ }
+ }
+
+ Assert.assertEquals(data, EXPECTED_LOCAL_TEST_DATA)
+ }
+
+ /* Toggle button */
+ @Test
+ fun toggle_button_supports_testTag() {
rule.setContent {
ToggleButtonWithDefaults(
modifier = Modifier.testTag(TEST_TAG)
@@ -73,18 +431,7 @@
}
@Test
- fun split_button_supports_testtag() {
- rule.setContent {
- SplitToggleButtonWithDefaults(
- modifier = Modifier.testTag(TEST_TAG)
- )
- }
-
- rule.onNodeWithTag(TEST_TAG).assertExists()
- }
-
- @Test
- fun has_clickaction_when_enabled() {
+ fun toggle_button_has_click_action_when_enabled() {
rule.setContent {
ToggleButtonWithDefaults(
enabled = true,
@@ -96,19 +443,7 @@
}
@Test
- fun split_button_has_clickaction_when_enabled() {
- rule.setContent {
- SplitToggleButtonWithDefaults(
- enabled = true,
- modifier = Modifier.testTag(TEST_TAG)
- )
- }
-
- rule.onNodeWithTag(TEST_TAG).onChildAt(0).assertHasClickAction()
- }
-
- @Test
- fun has_clickaction_when_disabled() {
+ fun toggle_button_has_click_action_when_disabled() {
rule.setContent {
ToggleButtonWithDefaults(
enabled = false,
@@ -120,19 +455,7 @@
}
@Test
- fun split_button_has_clickaction_when_disabled() {
- rule.setContent {
- SplitToggleButtonWithDefaults(
- enabled = false,
- modifier = Modifier.testTag(TEST_TAG)
- )
- }
-
- rule.onNodeWithTag(TEST_TAG).onChildAt(0).assertHasClickAction()
- }
-
- @Test
- fun is_toggleable() {
+ fun toggle_button_is_toggleable() {
rule.setContent {
ToggleButtonWithDefaults(
modifier = Modifier.testTag(TEST_TAG)
@@ -143,52 +466,7 @@
}
@Test
- fun split_button_is_toggleable() {
- rule.setContent {
- SplitToggleButtonWithDefaults(
- modifier = Modifier.testTag(TEST_TAG)
- )
- }
-
- rule.onNode(isToggleable()).assertExists()
- }
-
- @Test
- fun split_button_is_clickable() {
- rule.setContent {
- SplitToggleButtonWithDefaults(
- modifier = Modifier.testTag(TEST_TAG)
- )
- }
- rule.onNodeWithTag(TEST_TAG).onChildAt(0).assertHasClickAction()
- }
-
- @Test
- fun is_correctly_enabled() {
- rule.setContent {
- ToggleButtonWithDefaults(
- enabled = true,
- modifier = Modifier.testTag(TEST_TAG)
- )
- }
-
- rule.onNodeWithTag(TEST_TAG).assertIsEnabled()
- }
-
- @Test
- fun split_button_is_correctly_enabled() {
- rule.setContent {
- SplitToggleButtonWithDefaults(
- enabled = true,
- modifier = Modifier.testTag(TEST_TAG)
- )
- }
-
- rule.onNodeWithTag(TEST_TAG).assertIsEnabled()
- }
-
- @Test
- fun is_correctly_disabled() {
+ fun toggle_button_is_correctly_disabled() {
rule.setContent {
ToggleButtonWithDefaults(
enabled = false,
@@ -200,19 +478,19 @@
}
@Test
- fun split_button_is_correctly_disabled() {
+ fun toggle_button_is_correctly_enabled() {
rule.setContent {
- SplitToggleButtonWithDefaults(
- enabled = false,
+ ToggleButtonWithDefaults(
+ enabled = true,
modifier = Modifier.testTag(TEST_TAG)
)
}
- rule.onNodeWithTag(TEST_TAG).onChildAt(0).assertIsNotEnabled()
+ rule.onNodeWithTag(TEST_TAG).assertIsEnabled()
}
@Test
- fun is_on_when_checked() {
+ fun toggle_button_is_on_when_checked() {
rule.setContent {
ToggleButtonWithDefaults(
checked = true,
@@ -224,19 +502,7 @@
}
@Test
- fun split_button_is_on_when_checked() {
- rule.setContent {
- SplitToggleButtonWithDefaults(
- checked = true,
- modifier = Modifier.testTag(TEST_TAG)
- )
- }
-
- rule.onNodeWithTag(TEST_TAG).onChildAt(1).assertIsOn()
- }
-
- @Test
- fun is_off_when_unchecked() {
+ fun toggle_button_is_off_when_unchecked() {
rule.setContent {
ToggleButtonWithDefaults(
checked = false,
@@ -248,19 +514,7 @@
}
@Test
- fun split_button_is_off_when_unchecked() {
- rule.setContent {
- SplitToggleButtonWithDefaults(
- checked = false,
- modifier = Modifier.testTag(TEST_TAG)
- )
- }
-
- rule.onNodeWithTag(TEST_TAG).onChildAt(1).assertIsOff()
- }
-
- @Test
- fun responds_to_toggle_on() {
+ fun toggle_button_responds_to_toggle_on() {
rule.setContent {
val (checked, onCheckedChange) = remember { mutableStateOf(false) }
ToggleButtonWithDefaults(
@@ -279,27 +533,7 @@
}
@Test
- fun split_button_responds_to_toggle_on() {
- rule.setContent {
- val (checked, onCheckedChange) = remember { mutableStateOf(false) }
- SplitToggleButtonWithDefaults(
- checked = checked,
- onCheckedChange = onCheckedChange,
- enabled = true,
- modifier = Modifier.testTag(TEST_TAG)
- )
- }
-
- rule
- .onNodeWithTag(TEST_TAG)
- .onChildAt(1)
- .assertIsOff()
- .performClick()
- .assertIsOn()
- }
-
- @Test
- fun responds_to_toggle_off() {
+ fun toggle_button_responds_to_toggle_off() {
rule.setContent {
val (checked, onCheckedChange) = remember { mutableStateOf(true) }
ToggleButtonWithDefaults(
@@ -318,27 +552,7 @@
}
@Test
- fun split_button_responds_to_toggle_off() {
- rule.setContent {
- val (checked, onCheckedChange) = remember { mutableStateOf(true) }
- SplitToggleButtonWithDefaults(
- checked = checked,
- onCheckedChange = onCheckedChange,
- enabled = true,
- modifier = Modifier.testTag(TEST_TAG)
- )
- }
-
- rule
- .onNodeWithTag(TEST_TAG)
- .onChildAt(1)
- .assertIsOn()
- .performClick()
- .assertIsOff()
- }
-
- @Test
- fun does_not_toggle_when_disabled() {
+ fun toggle_button_does_not_toggle_when_disabled() {
rule.setContent {
val (checked, onCheckedChange) = remember { mutableStateOf(false) }
ToggleButtonWithDefaults(
@@ -357,27 +571,7 @@
}
@Test
- fun split_button_does_not_toggle_when_disabled() {
- rule.setContent {
- val (checked, onCheckedChange) = remember { mutableStateOf(false) }
- SplitToggleButtonWithDefaults(
- checked = checked,
- onCheckedChange = onCheckedChange,
- enabled = false,
- modifier = Modifier.testTag(TEST_TAG)
- )
- }
-
- rule
- .onNodeWithTag(TEST_TAG)
- .onChildAt(1)
- .assertIsOff()
- .performClick()
- .assertIsOff()
- }
-
- @Test
- fun has_role_checkbox() {
+ fun toggle_button_has_role_checkbox() {
rule.setContent {
ToggleButtonWithDefaults(
modifier = Modifier.testTag(TEST_TAG)
@@ -394,32 +588,7 @@
}
@Test
- fun split_button_has_roles_button_and_checkbox() {
- rule.setContent {
- SplitToggleButtonWithDefaults(
- modifier = Modifier.testTag(TEST_TAG)
- )
- }
-
- rule.onNodeWithTag(TEST_TAG).onChildAt(0)
- .assert(
- SemanticsMatcher.expectValue(
- SemanticsProperties.Role,
- Role.Button
- )
- )
-
- rule.onNodeWithTag(TEST_TAG).onChildAt(1)
- .assert(
- SemanticsMatcher.expectValue(
- SemanticsProperties.Role,
- Role.Checkbox
- )
- )
- }
-
- @Test
- fun displays_label_content() {
+ fun toggle_button_displays_label_content() {
val textContent = "abc"
rule.setContent {
@@ -435,23 +604,6 @@
rule.onNodeWithText(textContent).assertExists()
}
- @Test
- fun split_button_displays_label_content() {
- val textContent = "abc"
-
- rule.setContent {
- SplitToggleButtonWithDefaults(
- checked = true,
- onCheckedChange = {},
- label = {
- TestText(text = textContent)
- }
- )
- }
-
- rule.onNodeWithText(textContent).assertExists()
- }
-
@RequiresApi(Build.VERSION_CODES.O)
@Test
fun toggle_button_allows_checked_background_color_override() =
@@ -488,6 +640,214 @@
expectedColor = DISABLED_UNCHECKED_COLOR
)
+ /* Split toggle buttons */
+
+ @Test
+ fun split_button_supports_testTag() {
+ rule.setContent {
+ SplitToggleButtonWithDefaults(
+ modifier = Modifier.testTag(TEST_TAG)
+ )
+ }
+
+ rule.onNodeWithTag(TEST_TAG).assertExists()
+ }
+
+ @Test
+ fun split_button_has_click_action_when_enabled() {
+ rule.setContent {
+ SplitToggleButtonWithDefaults(
+ enabled = true,
+ modifier = Modifier.testTag(TEST_TAG)
+ )
+ }
+
+ rule.onNodeWithTag(TEST_TAG).onChildAt(0).assertHasClickAction()
+ }
+
+ @Test
+ fun split_button_has_click_action_when_disabled() {
+ rule.setContent {
+ SplitToggleButtonWithDefaults(
+ enabled = false,
+ modifier = Modifier.testTag(TEST_TAG)
+ )
+ }
+
+ rule.onNodeWithTag(TEST_TAG).onChildAt(0).assertHasClickAction()
+ }
+
+ @Test
+ fun split_button_is_toggleable() {
+ rule.setContent {
+ SplitToggleButtonWithDefaults(
+ modifier = Modifier.testTag(TEST_TAG)
+ )
+ }
+
+ rule.onNode(isToggleable()).assertExists()
+ }
+
+ @Test
+ fun split_button_is_clickable() {
+ rule.setContent {
+ SplitToggleButtonWithDefaults(
+ modifier = Modifier.testTag(TEST_TAG)
+ )
+ }
+ rule.onNodeWithTag(TEST_TAG).onChildAt(0).assertHasClickAction()
+ }
+
+ @Test
+ fun split_button_is_correctly_enabled() {
+ rule.setContent {
+ SplitToggleButtonWithDefaults(
+ enabled = true,
+ modifier = Modifier.testTag(TEST_TAG)
+ )
+ }
+
+ rule.onNodeWithTag(TEST_TAG).assertIsEnabled()
+ }
+
+ @Test
+ fun split_button_is_correctly_disabled() {
+ rule.setContent {
+ SplitToggleButtonWithDefaults(
+ enabled = false,
+ modifier = Modifier.testTag(TEST_TAG)
+ )
+ }
+
+ rule.onNodeWithTag(TEST_TAG).onChildAt(0).assertIsNotEnabled()
+ }
+
+ @Test
+ fun split_button_is_off_when_unchecked() {
+ rule.setContent {
+ SplitToggleButtonWithDefaults(
+ checked = false,
+ modifier = Modifier.testTag(TEST_TAG)
+ )
+ }
+
+ rule.onNodeWithTag(TEST_TAG).onChildAt(1).assertIsOff()
+ }
+
+ @Test
+ fun split_button_is_on_when_checked() {
+ rule.setContent {
+ SplitToggleButtonWithDefaults(
+ checked = true,
+ modifier = Modifier.testTag(TEST_TAG)
+ )
+ }
+
+ rule.onNodeWithTag(TEST_TAG).onChildAt(1).assertIsOn()
+ }
+
+ @Test
+ fun split_button_responds_to_toggle_on() {
+ rule.setContent {
+ val (checked, onCheckedChange) = remember { mutableStateOf(false) }
+ SplitToggleButtonWithDefaults(
+ checked = checked,
+ onCheckedChange = onCheckedChange,
+ enabled = true,
+ modifier = Modifier.testTag(TEST_TAG)
+ )
+ }
+
+ rule
+ .onNodeWithTag(TEST_TAG)
+ .onChildAt(1)
+ .assertIsOff()
+ .performClick()
+ .assertIsOn()
+ }
+
+ @Test
+ fun split_button_responds_to_toggle_off() {
+ rule.setContent {
+ val (checked, onCheckedChange) = remember { mutableStateOf(true) }
+ SplitToggleButtonWithDefaults(
+ checked = checked,
+ onCheckedChange = onCheckedChange,
+ enabled = true,
+ modifier = Modifier.testTag(TEST_TAG)
+ )
+ }
+
+ rule
+ .onNodeWithTag(TEST_TAG)
+ .onChildAt(1)
+ .assertIsOn()
+ .performClick()
+ .assertIsOff()
+ }
+
+ @Test
+ fun split_button_does_not_toggle_when_disabled() {
+ rule.setContent {
+ val (checked, onCheckedChange) = remember { mutableStateOf(false) }
+ SplitToggleButtonWithDefaults(
+ checked = checked,
+ onCheckedChange = onCheckedChange,
+ enabled = false,
+ modifier = Modifier.testTag(TEST_TAG)
+ )
+ }
+
+ rule
+ .onNodeWithTag(TEST_TAG)
+ .onChildAt(1)
+ .assertIsOff()
+ .performClick()
+ .assertIsOff()
+ }
+
+ @Test
+ fun split_button_has_roles_button_and_checkbox() {
+ rule.setContent {
+ SplitToggleButtonWithDefaults(
+ modifier = Modifier.testTag(TEST_TAG)
+ )
+ }
+
+ rule.onNodeWithTag(TEST_TAG).onChildAt(0)
+ .assert(
+ SemanticsMatcher.expectValue(
+ SemanticsProperties.Role,
+ Role.Button
+ )
+ )
+
+ rule.onNodeWithTag(TEST_TAG).onChildAt(1)
+ .assert(
+ SemanticsMatcher.expectValue(
+ SemanticsProperties.Role,
+ Role.Checkbox
+ )
+ )
+ }
+
+ @Test
+ fun split_button_displays_label_content() {
+ val textContent = "abc"
+
+ rule.setContent {
+ SplitToggleButtonWithDefaults(
+ checked = true,
+ onCheckedChange = {},
+ label = {
+ TestText(text = textContent)
+ }
+ )
+ }
+
+ rule.onNodeWithText(textContent).assertExists()
+ }
+
@RequiresApi(Build.VERSION_CODES.O)
@Test
fun split_toggle_button_allows_checked_background_color_override() =
@@ -586,6 +946,109 @@
.captureToImage()
.assertContainsColor(expectedColor)
}
+
+ @RequiresApi(Build.VERSION_CODES.O)
+ private fun verifyToggleButtonColors(
+ enabled: Boolean,
+ checked: Boolean,
+ backgroundColor: @Composable (Boolean, Boolean) -> State<Color>,
+ borderColor: @Composable (Boolean, Boolean) -> State<Color>,
+ expectedBackgroundColor: Color,
+ expectedBorderColor: Color,
+ backgroundThreshold: Float = 50.0f,
+ borderThreshold: Float = 1.0f,
+ ) {
+ val testBackground = Color.White
+ val expectedColor = { color: Color ->
+ if (color != Color.Transparent)
+ color.compositeOver(testBackground)
+ else
+ testBackground
+ }
+
+ rule.setContent {
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .background(testBackground)
+ ) {
+ val actualBorderColor = borderColor(enabled, checked).value
+ val border = remember { mutableStateOf(BorderStroke(2.dp, actualBorderColor)) }
+ RoundToggleButtonWithDefaults(
+ backgroundColor = backgroundColor,
+ border = { _, _ -> return@RoundToggleButtonWithDefaults border },
+ enabled = enabled,
+ checked = checked,
+ modifier = Modifier.testTag(TEST_TAG)
+ ) {
+ }
+ }
+ }
+
+ rule.onNodeWithTag(TEST_TAG)
+ .captureToImage()
+ .assertContainsColor(expectedColor(expectedBackgroundColor), backgroundThreshold)
+ rule.onNodeWithTag(TEST_TAG)
+ .captureToImage()
+ .assertContainsColor(expectedColor(expectedBorderColor), borderThreshold)
+ }
+
+ private fun verifyTapSize(
+ expected: Dp,
+ content: @Composable () -> Unit
+ ) {
+ rule.setContent {
+ content()
+ }
+
+ rule.onNodeWithTag(TEST_TAG)
+ .assertTouchHeightIsEqualTo(expected)
+ .assertTouchWidthIsEqualTo(expected)
+ }
+
+ private fun verifyVisibleSize(
+ expected: Dp,
+ content: @Composable () -> Unit
+ ) {
+ rule.setContent {
+ content()
+ }
+
+ rule.onNodeWithTag(TEST_TAG)
+ .assertHeightIsEqualTo(expected)
+ .assertWidthIsEqualTo(expected)
+ }
+}
+
+@Composable
+private fun RoundToggleButtonWithDefaults(
+ modifier: Modifier = Modifier,
+ checked: Boolean = true,
+ onCheckedChange: (Boolean) -> Unit = {},
+ enabled: Boolean = true,
+ backgroundColor: @Composable (enabled: Boolean, checked: Boolean) -> State<Color> =
+ { _, _ -> rememberUpdatedState(DEFAULT_SHAPE_COLOR) },
+ border: @Composable (enabled: Boolean, checked: Boolean) -> State<BorderStroke?>? =
+ { _, _ -> null },
+ toggleButtonSize: Dp = 52.dp,
+ interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+ shape: Shape = CircleShape,
+ content: @Composable BoxScope.() -> Unit = {
+ TestText(text = "Label")
+ }
+) {
+ ToggleButton(
+ checked = checked,
+ onCheckedChange = onCheckedChange,
+ modifier = modifier,
+ enabled = enabled,
+ backgroundColor = backgroundColor,
+ border = border,
+ toggleButtonSize = toggleButtonSize,
+ interactionSource = interactionSource,
+ shape = shape,
+ content = content
+ )
}
@Composable
diff --git a/wear/compose/compose-material/api/current.txt b/wear/compose/compose-material/api/current.txt
index c1b45d8..099285e 100644
--- a/wear/compose/compose-material/api/current.txt
+++ b/wear/compose/compose-material/api/current.txt
@@ -207,9 +207,9 @@
}
@SuppressCompatibility @androidx.compose.runtime.Immutable @androidx.wear.compose.material.ExperimentalWearMaterialApi public final class FractionalThreshold implements androidx.wear.compose.material.ThresholdConfig {
- ctor public FractionalThreshold(float fraction);
+ ctor public FractionalThreshold(@FloatRange(from=0.0, to=1.0) float fraction);
method public float computeThreshold(androidx.compose.ui.unit.Density, float fromValue, float toValue);
- method public androidx.wear.compose.material.FractionalThreshold copy(float fraction);
+ method public androidx.wear.compose.material.FractionalThreshold copy(@FloatRange(from=0.0, to=1.0) float fraction);
}
public final class HorizontalPageIndicatorKt {
@@ -326,10 +326,10 @@
}
public final class PickerKt {
- method @Deprecated @androidx.compose.runtime.Composable public static void Picker(androidx.wear.compose.material.PickerState state, optional androidx.compose.ui.Modifier modifier, optional boolean readOnly, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? readOnlyLabel, optional androidx.wear.compose.material.ScalingParams scalingParams, optional float separation, optional float gradientRatio, optional long gradientColor, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior, kotlin.jvm.functions.Function2<? super androidx.wear.compose.material.PickerScope,? super java.lang.Integer,kotlin.Unit> option);
- method @androidx.compose.runtime.Composable public static void Picker(androidx.wear.compose.material.PickerState state, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional boolean readOnly, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? readOnlyLabel, optional kotlin.jvm.functions.Function0<kotlin.Unit> onSelected, optional androidx.wear.compose.foundation.lazy.ScalingParams scalingParams, optional float separation, optional float gradientRatio, optional long gradientColor, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior, optional boolean userScrollEnabled, kotlin.jvm.functions.Function2<? super androidx.wear.compose.material.PickerScope,? super java.lang.Integer,kotlin.Unit> option);
- method @Deprecated @androidx.compose.runtime.Composable public static void Picker(androidx.wear.compose.material.PickerState state, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional boolean readOnly, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? readOnlyLabel, optional kotlin.jvm.functions.Function0<kotlin.Unit> onSelected, optional androidx.wear.compose.material.ScalingParams scalingParams, optional float separation, optional float gradientRatio, optional long gradientColor, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior, optional boolean userScrollEnabled, kotlin.jvm.functions.Function2<? super androidx.wear.compose.material.PickerScope,? super java.lang.Integer,kotlin.Unit> option);
- method @Deprecated @androidx.compose.runtime.Composable public static void Picker(androidx.wear.compose.material.PickerState state, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional boolean readOnly, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? readOnlyLabel, optional kotlin.jvm.functions.Function0<kotlin.Unit> onSelected, optional androidx.wear.compose.material.ScalingParams scalingParams, optional float separation, optional float gradientRatio, optional long gradientColor, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior, kotlin.jvm.functions.Function2<? super androidx.wear.compose.material.PickerScope,? super java.lang.Integer,kotlin.Unit> option);
+ method @Deprecated @androidx.compose.runtime.Composable public static void Picker(androidx.wear.compose.material.PickerState state, optional androidx.compose.ui.Modifier modifier, optional boolean readOnly, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? readOnlyLabel, optional androidx.wear.compose.material.ScalingParams scalingParams, optional float separation, optional @FloatRange(from=0.0, to=0.5) float gradientRatio, optional long gradientColor, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior, kotlin.jvm.functions.Function2<? super androidx.wear.compose.material.PickerScope,? super java.lang.Integer,kotlin.Unit> option);
+ method @androidx.compose.runtime.Composable public static void Picker(androidx.wear.compose.material.PickerState state, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional boolean readOnly, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? readOnlyLabel, optional kotlin.jvm.functions.Function0<kotlin.Unit> onSelected, optional androidx.wear.compose.foundation.lazy.ScalingParams scalingParams, optional float separation, optional @FloatRange(from=0.0, to=0.5) float gradientRatio, optional long gradientColor, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior, optional boolean userScrollEnabled, kotlin.jvm.functions.Function2<? super androidx.wear.compose.material.PickerScope,? super java.lang.Integer,kotlin.Unit> option);
+ method @Deprecated @androidx.compose.runtime.Composable public static void Picker(androidx.wear.compose.material.PickerState state, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional boolean readOnly, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? readOnlyLabel, optional kotlin.jvm.functions.Function0<kotlin.Unit> onSelected, optional androidx.wear.compose.material.ScalingParams scalingParams, optional float separation, optional @FloatRange(from=0.0, to=0.5) float gradientRatio, optional long gradientColor, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior, optional boolean userScrollEnabled, kotlin.jvm.functions.Function2<? super androidx.wear.compose.material.PickerScope,? super java.lang.Integer,kotlin.Unit> option);
+ method @Deprecated @androidx.compose.runtime.Composable public static void Picker(androidx.wear.compose.material.PickerState state, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional boolean readOnly, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? readOnlyLabel, optional kotlin.jvm.functions.Function0<kotlin.Unit> onSelected, optional androidx.wear.compose.material.ScalingParams scalingParams, optional float separation, optional @FloatRange(from=0.0, to=0.5) float gradientRatio, optional long gradientColor, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior, kotlin.jvm.functions.Function2<? super androidx.wear.compose.material.PickerScope,? super java.lang.Integer,kotlin.Unit> option);
method @androidx.compose.runtime.Composable public static androidx.wear.compose.material.PickerState rememberPickerState(int initialNumberOfOptions, optional int initiallySelectedOption, optional boolean repeatItems);
}
@@ -412,10 +412,10 @@
}
@androidx.compose.runtime.Stable public interface PositionIndicatorState {
- method public float getPositionFraction();
- method public float sizeFraction(float scrollableContainerSizePx);
- method public int visibility(float scrollableContainerSizePx);
- property public abstract float positionFraction;
+ method @FloatRange(from=0.0, to=1.0) public float getPositionFraction();
+ method @FloatRange(from=0.0, to=1.0) public float sizeFraction(@FloatRange(from=0.0) float scrollableContainerSizePx);
+ method public int visibility(@FloatRange(from=0.0) float scrollableContainerSizePx);
+ property @FloatRange(from=0.0, to=1.0) public abstract float positionFraction;
}
@kotlin.jvm.JvmInline public final value class PositionIndicatorVisibility {
@@ -441,7 +441,7 @@
public final class ProgressIndicatorKt {
method @androidx.compose.runtime.Composable public static void CircularProgressIndicator(optional androidx.compose.ui.Modifier modifier, optional float startAngle, optional long indicatorColor, optional long trackColor, optional float strokeWidth);
- method @androidx.compose.runtime.Composable public static void CircularProgressIndicator(float progress, optional androidx.compose.ui.Modifier modifier, optional float startAngle, optional float endAngle, optional long indicatorColor, optional long trackColor, optional float strokeWidth);
+ method @androidx.compose.runtime.Composable public static void CircularProgressIndicator(@FloatRange(from=0.0, to=1.0) float progress, optional androidx.compose.ui.Modifier modifier, optional float startAngle, optional float endAngle, optional long indicatorColor, optional long trackColor, optional float strokeWidth);
}
@androidx.compose.runtime.Stable public interface RadioButtonColors {
@@ -455,7 +455,7 @@
}
@SuppressCompatibility @androidx.compose.runtime.Immutable @androidx.wear.compose.material.ExperimentalWearMaterialApi public final class ResistanceConfig {
- ctor public ResistanceConfig(float basis, optional float factorAtMin, optional float factorAtMax);
+ ctor public ResistanceConfig(@FloatRange(from=0.0, fromInclusive=false) float basis, optional @FloatRange(from=0.0) float factorAtMin, optional @FloatRange(from=0.0) float factorAtMax);
method public float computeResistance(float overflow);
method public float getBasis();
method public float getFactorAtMax();
@@ -514,9 +514,9 @@
}
@Deprecated @androidx.compose.runtime.Stable @androidx.wear.compose.material.ScalingLazyScopeMarker public sealed interface ScalingLazyListItemScope {
- method @Deprecated public androidx.compose.ui.Modifier fillParentMaxHeight(androidx.compose.ui.Modifier, optional float fraction);
- method @Deprecated public androidx.compose.ui.Modifier fillParentMaxSize(androidx.compose.ui.Modifier, optional float fraction);
- method @Deprecated public androidx.compose.ui.Modifier fillParentMaxWidth(androidx.compose.ui.Modifier, optional float fraction);
+ method @Deprecated public androidx.compose.ui.Modifier fillParentMaxHeight(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
+ method @Deprecated public androidx.compose.ui.Modifier fillParentMaxSize(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
+ method @Deprecated public androidx.compose.ui.Modifier fillParentMaxWidth(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
}
@Deprecated public sealed interface ScalingLazyListLayoutInfo {
@@ -581,20 +581,20 @@
}
@Deprecated @androidx.compose.runtime.Stable public interface ScalingParams {
- method @Deprecated public float getEdgeAlpha();
- method @Deprecated public float getEdgeScale();
- method @Deprecated public float getMaxElementHeight();
- method @Deprecated public float getMaxTransitionArea();
- method @Deprecated public float getMinElementHeight();
- method @Deprecated public float getMinTransitionArea();
+ method @Deprecated @FloatRange(from=0.0, to=1.0) public float getEdgeAlpha();
+ method @Deprecated @FloatRange(from=0.0, to=1.0) public float getEdgeScale();
+ method @Deprecated @FloatRange(from=0.0, to=1.0) public float getMaxElementHeight();
+ method @Deprecated @FloatRange(from=0.0, to=1.0) public float getMaxTransitionArea();
+ method @Deprecated @FloatRange(from=0.0, to=1.0) public float getMinElementHeight();
+ method @Deprecated @FloatRange(from=0.0, to=1.0) public float getMinTransitionArea();
method @Deprecated public androidx.compose.animation.core.Easing getScaleInterpolator();
method @Deprecated public int resolveViewportVerticalOffset(long viewportConstraints);
- property @Deprecated public abstract float edgeAlpha;
- property @Deprecated public abstract float edgeScale;
- property @Deprecated public abstract float maxElementHeight;
- property @Deprecated public abstract float maxTransitionArea;
- property @Deprecated public abstract float minElementHeight;
- property @Deprecated public abstract float minTransitionArea;
+ property @Deprecated @FloatRange(from=0.0, to=1.0) public abstract float edgeAlpha;
+ property @Deprecated @FloatRange(from=0.0, to=1.0) public abstract float edgeScale;
+ property @Deprecated @FloatRange(from=0.0, to=1.0) public abstract float maxElementHeight;
+ property @Deprecated @FloatRange(from=0.0, to=1.0) public abstract float maxTransitionArea;
+ property @Deprecated @FloatRange(from=0.0, to=1.0) public abstract float minElementHeight;
+ property @Deprecated @FloatRange(from=0.0, to=1.0) public abstract float minTransitionArea;
property @Deprecated public abstract androidx.compose.animation.core.Easing scaleInterpolator;
}
@@ -645,7 +645,7 @@
}
@SuppressCompatibility @androidx.compose.runtime.Immutable @androidx.wear.compose.material.ExperimentalWearMaterialApi public final class SwipeProgress<T> {
- ctor public SwipeProgress(T from, T to, float fraction);
+ ctor public SwipeProgress(T from, T to, @FloatRange(from=0.0, to=1.0) float fraction);
method public float getFraction();
method public T getFrom();
method public T getTo();
@@ -694,6 +694,55 @@
enum_constant public static final androidx.wear.compose.material.SwipeToDismissValue Dismissed;
}
+ @SuppressCompatibility @androidx.wear.compose.material.ExperimentalWearMaterialApi public final class SwipeToRevealAction {
+ ctor public SwipeToRevealAction(kotlin.jvm.functions.Function0<kotlin.Unit>? icon, kotlin.jvm.functions.Function0<kotlin.Unit>? label, androidx.compose.ui.Modifier modifier, androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> onClick);
+ method public kotlin.jvm.functions.Function0<kotlin.Unit>? getIcon();
+ method public androidx.compose.foundation.interaction.MutableInteractionSource getInteractionSource();
+ method public kotlin.jvm.functions.Function0<kotlin.Unit>? getLabel();
+ method public androidx.compose.ui.Modifier getModifier();
+ method public kotlin.jvm.functions.Function0<kotlin.Unit> getOnClick();
+ property public final kotlin.jvm.functions.Function0<kotlin.Unit>? icon;
+ property public final androidx.compose.foundation.interaction.MutableInteractionSource interactionSource;
+ property public final kotlin.jvm.functions.Function0<kotlin.Unit>? label;
+ property public final androidx.compose.ui.Modifier modifier;
+ property public final kotlin.jvm.functions.Function0<kotlin.Unit> onClick;
+ }
+
+ @SuppressCompatibility @androidx.wear.compose.material.ExperimentalWearMaterialApi public final class SwipeToRevealActionColors {
+ ctor public SwipeToRevealActionColors(long actionBackgroundColor, long actionContentColor, long additionalActionBackgroundColor, long additionalActionContentColor, long undoActionBackgroundColor, long undoActionContentColor);
+ method public long getActionBackgroundColor();
+ method public long getActionContentColor();
+ method public long getAdditionalActionBackgroundColor();
+ method public long getAdditionalActionContentColor();
+ method public long getUndoActionBackgroundColor();
+ method public long getUndoActionContentColor();
+ property public final long actionBackgroundColor;
+ property public final long actionContentColor;
+ property public final long additionalActionBackgroundColor;
+ property public final long additionalActionContentColor;
+ property public final long undoActionBackgroundColor;
+ property public final long undoActionContentColor;
+ }
+
+ @SuppressCompatibility @androidx.wear.compose.material.ExperimentalWearMaterialApi public final class SwipeToRevealDefaults {
+ method @androidx.compose.runtime.Composable public androidx.wear.compose.material.SwipeToRevealAction action(kotlin.jvm.functions.Function0<kotlin.Unit> icon, kotlin.jvm.functions.Function0<kotlin.Unit> label, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional kotlin.jvm.functions.Function0<kotlin.Unit> onClick);
+ method @androidx.compose.runtime.Composable public androidx.wear.compose.material.SwipeToRevealActionColors actionColors(optional long actionBackgroundColor, optional long actionContentColor, optional long additionalActionBackgroundColor, optional long additionalActionContentColor, optional long undoActionBackgroundColor, optional long undoActionContentColor);
+ method @androidx.compose.runtime.Composable public androidx.wear.compose.material.SwipeToRevealAction additionalAction(kotlin.jvm.functions.Function0<kotlin.Unit> icon, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional kotlin.jvm.functions.Function0<kotlin.Unit> onClick);
+ method public androidx.compose.foundation.shape.RoundedCornerShape getCardActionShape();
+ method public androidx.compose.ui.graphics.vector.ImageVector getDelete();
+ method public androidx.compose.ui.graphics.vector.ImageVector getMoreOptions();
+ method @androidx.compose.runtime.Composable public androidx.wear.compose.material.SwipeToRevealAction undoAction(kotlin.jvm.functions.Function0<kotlin.Unit> label, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional kotlin.jvm.functions.Function0<kotlin.Unit> onClick);
+ property public final androidx.compose.foundation.shape.RoundedCornerShape CardActionShape;
+ property public final androidx.compose.ui.graphics.vector.ImageVector Delete;
+ property public final androidx.compose.ui.graphics.vector.ImageVector MoreOptions;
+ field public static final androidx.wear.compose.material.SwipeToRevealDefaults INSTANCE;
+ }
+
+ public final class SwipeToRevealKt {
+ method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.wear.compose.material.ExperimentalWearMaterialApi public static void SwipeToRevealCard(androidx.wear.compose.material.SwipeToRevealAction action, androidx.wear.compose.foundation.RevealState revealState, optional androidx.compose.ui.Modifier modifier, optional androidx.wear.compose.material.SwipeToRevealAction? additionalAction, optional androidx.wear.compose.material.SwipeToRevealAction? undoAction, optional androidx.wear.compose.material.SwipeToRevealActionColors colors, optional androidx.compose.ui.graphics.Shape shape, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.wear.compose.material.ExperimentalWearMaterialApi public static void SwipeToRevealChip(androidx.wear.compose.material.SwipeToRevealAction action, androidx.wear.compose.foundation.RevealState revealState, optional androidx.compose.ui.Modifier modifier, optional androidx.wear.compose.material.SwipeToRevealAction? additionalAction, optional androidx.wear.compose.material.SwipeToRevealAction? undoAction, optional androidx.wear.compose.material.SwipeToRevealActionColors colors, optional androidx.compose.ui.graphics.Shape shape, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ }
+
@SuppressCompatibility @androidx.wear.compose.material.ExperimentalWearMaterialApi public final class SwipeableDefaults {
method public androidx.compose.animation.core.SpringSpec<java.lang.Float> getAnimationSpec();
method public float getVelocityThreshold();
diff --git a/wear/compose/compose-material/api/restricted_current.txt b/wear/compose/compose-material/api/restricted_current.txt
index c1b45d8..099285e 100644
--- a/wear/compose/compose-material/api/restricted_current.txt
+++ b/wear/compose/compose-material/api/restricted_current.txt
@@ -207,9 +207,9 @@
}
@SuppressCompatibility @androidx.compose.runtime.Immutable @androidx.wear.compose.material.ExperimentalWearMaterialApi public final class FractionalThreshold implements androidx.wear.compose.material.ThresholdConfig {
- ctor public FractionalThreshold(float fraction);
+ ctor public FractionalThreshold(@FloatRange(from=0.0, to=1.0) float fraction);
method public float computeThreshold(androidx.compose.ui.unit.Density, float fromValue, float toValue);
- method public androidx.wear.compose.material.FractionalThreshold copy(float fraction);
+ method public androidx.wear.compose.material.FractionalThreshold copy(@FloatRange(from=0.0, to=1.0) float fraction);
}
public final class HorizontalPageIndicatorKt {
@@ -326,10 +326,10 @@
}
public final class PickerKt {
- method @Deprecated @androidx.compose.runtime.Composable public static void Picker(androidx.wear.compose.material.PickerState state, optional androidx.compose.ui.Modifier modifier, optional boolean readOnly, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? readOnlyLabel, optional androidx.wear.compose.material.ScalingParams scalingParams, optional float separation, optional float gradientRatio, optional long gradientColor, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior, kotlin.jvm.functions.Function2<? super androidx.wear.compose.material.PickerScope,? super java.lang.Integer,kotlin.Unit> option);
- method @androidx.compose.runtime.Composable public static void Picker(androidx.wear.compose.material.PickerState state, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional boolean readOnly, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? readOnlyLabel, optional kotlin.jvm.functions.Function0<kotlin.Unit> onSelected, optional androidx.wear.compose.foundation.lazy.ScalingParams scalingParams, optional float separation, optional float gradientRatio, optional long gradientColor, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior, optional boolean userScrollEnabled, kotlin.jvm.functions.Function2<? super androidx.wear.compose.material.PickerScope,? super java.lang.Integer,kotlin.Unit> option);
- method @Deprecated @androidx.compose.runtime.Composable public static void Picker(androidx.wear.compose.material.PickerState state, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional boolean readOnly, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? readOnlyLabel, optional kotlin.jvm.functions.Function0<kotlin.Unit> onSelected, optional androidx.wear.compose.material.ScalingParams scalingParams, optional float separation, optional float gradientRatio, optional long gradientColor, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior, optional boolean userScrollEnabled, kotlin.jvm.functions.Function2<? super androidx.wear.compose.material.PickerScope,? super java.lang.Integer,kotlin.Unit> option);
- method @Deprecated @androidx.compose.runtime.Composable public static void Picker(androidx.wear.compose.material.PickerState state, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional boolean readOnly, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? readOnlyLabel, optional kotlin.jvm.functions.Function0<kotlin.Unit> onSelected, optional androidx.wear.compose.material.ScalingParams scalingParams, optional float separation, optional float gradientRatio, optional long gradientColor, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior, kotlin.jvm.functions.Function2<? super androidx.wear.compose.material.PickerScope,? super java.lang.Integer,kotlin.Unit> option);
+ method @Deprecated @androidx.compose.runtime.Composable public static void Picker(androidx.wear.compose.material.PickerState state, optional androidx.compose.ui.Modifier modifier, optional boolean readOnly, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? readOnlyLabel, optional androidx.wear.compose.material.ScalingParams scalingParams, optional float separation, optional @FloatRange(from=0.0, to=0.5) float gradientRatio, optional long gradientColor, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior, kotlin.jvm.functions.Function2<? super androidx.wear.compose.material.PickerScope,? super java.lang.Integer,kotlin.Unit> option);
+ method @androidx.compose.runtime.Composable public static void Picker(androidx.wear.compose.material.PickerState state, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional boolean readOnly, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? readOnlyLabel, optional kotlin.jvm.functions.Function0<kotlin.Unit> onSelected, optional androidx.wear.compose.foundation.lazy.ScalingParams scalingParams, optional float separation, optional @FloatRange(from=0.0, to=0.5) float gradientRatio, optional long gradientColor, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior, optional boolean userScrollEnabled, kotlin.jvm.functions.Function2<? super androidx.wear.compose.material.PickerScope,? super java.lang.Integer,kotlin.Unit> option);
+ method @Deprecated @androidx.compose.runtime.Composable public static void Picker(androidx.wear.compose.material.PickerState state, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional boolean readOnly, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? readOnlyLabel, optional kotlin.jvm.functions.Function0<kotlin.Unit> onSelected, optional androidx.wear.compose.material.ScalingParams scalingParams, optional float separation, optional @FloatRange(from=0.0, to=0.5) float gradientRatio, optional long gradientColor, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior, optional boolean userScrollEnabled, kotlin.jvm.functions.Function2<? super androidx.wear.compose.material.PickerScope,? super java.lang.Integer,kotlin.Unit> option);
+ method @Deprecated @androidx.compose.runtime.Composable public static void Picker(androidx.wear.compose.material.PickerState state, String? contentDescription, optional androidx.compose.ui.Modifier modifier, optional boolean readOnly, optional kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.BoxScope,kotlin.Unit>? readOnlyLabel, optional kotlin.jvm.functions.Function0<kotlin.Unit> onSelected, optional androidx.wear.compose.material.ScalingParams scalingParams, optional float separation, optional @FloatRange(from=0.0, to=0.5) float gradientRatio, optional long gradientColor, optional androidx.compose.foundation.gestures.FlingBehavior flingBehavior, kotlin.jvm.functions.Function2<? super androidx.wear.compose.material.PickerScope,? super java.lang.Integer,kotlin.Unit> option);
method @androidx.compose.runtime.Composable public static androidx.wear.compose.material.PickerState rememberPickerState(int initialNumberOfOptions, optional int initiallySelectedOption, optional boolean repeatItems);
}
@@ -412,10 +412,10 @@
}
@androidx.compose.runtime.Stable public interface PositionIndicatorState {
- method public float getPositionFraction();
- method public float sizeFraction(float scrollableContainerSizePx);
- method public int visibility(float scrollableContainerSizePx);
- property public abstract float positionFraction;
+ method @FloatRange(from=0.0, to=1.0) public float getPositionFraction();
+ method @FloatRange(from=0.0, to=1.0) public float sizeFraction(@FloatRange(from=0.0) float scrollableContainerSizePx);
+ method public int visibility(@FloatRange(from=0.0) float scrollableContainerSizePx);
+ property @FloatRange(from=0.0, to=1.0) public abstract float positionFraction;
}
@kotlin.jvm.JvmInline public final value class PositionIndicatorVisibility {
@@ -441,7 +441,7 @@
public final class ProgressIndicatorKt {
method @androidx.compose.runtime.Composable public static void CircularProgressIndicator(optional androidx.compose.ui.Modifier modifier, optional float startAngle, optional long indicatorColor, optional long trackColor, optional float strokeWidth);
- method @androidx.compose.runtime.Composable public static void CircularProgressIndicator(float progress, optional androidx.compose.ui.Modifier modifier, optional float startAngle, optional float endAngle, optional long indicatorColor, optional long trackColor, optional float strokeWidth);
+ method @androidx.compose.runtime.Composable public static void CircularProgressIndicator(@FloatRange(from=0.0, to=1.0) float progress, optional androidx.compose.ui.Modifier modifier, optional float startAngle, optional float endAngle, optional long indicatorColor, optional long trackColor, optional float strokeWidth);
}
@androidx.compose.runtime.Stable public interface RadioButtonColors {
@@ -455,7 +455,7 @@
}
@SuppressCompatibility @androidx.compose.runtime.Immutable @androidx.wear.compose.material.ExperimentalWearMaterialApi public final class ResistanceConfig {
- ctor public ResistanceConfig(float basis, optional float factorAtMin, optional float factorAtMax);
+ ctor public ResistanceConfig(@FloatRange(from=0.0, fromInclusive=false) float basis, optional @FloatRange(from=0.0) float factorAtMin, optional @FloatRange(from=0.0) float factorAtMax);
method public float computeResistance(float overflow);
method public float getBasis();
method public float getFactorAtMax();
@@ -514,9 +514,9 @@
}
@Deprecated @androidx.compose.runtime.Stable @androidx.wear.compose.material.ScalingLazyScopeMarker public sealed interface ScalingLazyListItemScope {
- method @Deprecated public androidx.compose.ui.Modifier fillParentMaxHeight(androidx.compose.ui.Modifier, optional float fraction);
- method @Deprecated public androidx.compose.ui.Modifier fillParentMaxSize(androidx.compose.ui.Modifier, optional float fraction);
- method @Deprecated public androidx.compose.ui.Modifier fillParentMaxWidth(androidx.compose.ui.Modifier, optional float fraction);
+ method @Deprecated public androidx.compose.ui.Modifier fillParentMaxHeight(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
+ method @Deprecated public androidx.compose.ui.Modifier fillParentMaxSize(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
+ method @Deprecated public androidx.compose.ui.Modifier fillParentMaxWidth(androidx.compose.ui.Modifier, optional @FloatRange(from=0.0, to=1.0) float fraction);
}
@Deprecated public sealed interface ScalingLazyListLayoutInfo {
@@ -581,20 +581,20 @@
}
@Deprecated @androidx.compose.runtime.Stable public interface ScalingParams {
- method @Deprecated public float getEdgeAlpha();
- method @Deprecated public float getEdgeScale();
- method @Deprecated public float getMaxElementHeight();
- method @Deprecated public float getMaxTransitionArea();
- method @Deprecated public float getMinElementHeight();
- method @Deprecated public float getMinTransitionArea();
+ method @Deprecated @FloatRange(from=0.0, to=1.0) public float getEdgeAlpha();
+ method @Deprecated @FloatRange(from=0.0, to=1.0) public float getEdgeScale();
+ method @Deprecated @FloatRange(from=0.0, to=1.0) public float getMaxElementHeight();
+ method @Deprecated @FloatRange(from=0.0, to=1.0) public float getMaxTransitionArea();
+ method @Deprecated @FloatRange(from=0.0, to=1.0) public float getMinElementHeight();
+ method @Deprecated @FloatRange(from=0.0, to=1.0) public float getMinTransitionArea();
method @Deprecated public androidx.compose.animation.core.Easing getScaleInterpolator();
method @Deprecated public int resolveViewportVerticalOffset(long viewportConstraints);
- property @Deprecated public abstract float edgeAlpha;
- property @Deprecated public abstract float edgeScale;
- property @Deprecated public abstract float maxElementHeight;
- property @Deprecated public abstract float maxTransitionArea;
- property @Deprecated public abstract float minElementHeight;
- property @Deprecated public abstract float minTransitionArea;
+ property @Deprecated @FloatRange(from=0.0, to=1.0) public abstract float edgeAlpha;
+ property @Deprecated @FloatRange(from=0.0, to=1.0) public abstract float edgeScale;
+ property @Deprecated @FloatRange(from=0.0, to=1.0) public abstract float maxElementHeight;
+ property @Deprecated @FloatRange(from=0.0, to=1.0) public abstract float maxTransitionArea;
+ property @Deprecated @FloatRange(from=0.0, to=1.0) public abstract float minElementHeight;
+ property @Deprecated @FloatRange(from=0.0, to=1.0) public abstract float minTransitionArea;
property @Deprecated public abstract androidx.compose.animation.core.Easing scaleInterpolator;
}
@@ -645,7 +645,7 @@
}
@SuppressCompatibility @androidx.compose.runtime.Immutable @androidx.wear.compose.material.ExperimentalWearMaterialApi public final class SwipeProgress<T> {
- ctor public SwipeProgress(T from, T to, float fraction);
+ ctor public SwipeProgress(T from, T to, @FloatRange(from=0.0, to=1.0) float fraction);
method public float getFraction();
method public T getFrom();
method public T getTo();
@@ -694,6 +694,55 @@
enum_constant public static final androidx.wear.compose.material.SwipeToDismissValue Dismissed;
}
+ @SuppressCompatibility @androidx.wear.compose.material.ExperimentalWearMaterialApi public final class SwipeToRevealAction {
+ ctor public SwipeToRevealAction(kotlin.jvm.functions.Function0<kotlin.Unit>? icon, kotlin.jvm.functions.Function0<kotlin.Unit>? label, androidx.compose.ui.Modifier modifier, androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, kotlin.jvm.functions.Function0<kotlin.Unit> onClick);
+ method public kotlin.jvm.functions.Function0<kotlin.Unit>? getIcon();
+ method public androidx.compose.foundation.interaction.MutableInteractionSource getInteractionSource();
+ method public kotlin.jvm.functions.Function0<kotlin.Unit>? getLabel();
+ method public androidx.compose.ui.Modifier getModifier();
+ method public kotlin.jvm.functions.Function0<kotlin.Unit> getOnClick();
+ property public final kotlin.jvm.functions.Function0<kotlin.Unit>? icon;
+ property public final androidx.compose.foundation.interaction.MutableInteractionSource interactionSource;
+ property public final kotlin.jvm.functions.Function0<kotlin.Unit>? label;
+ property public final androidx.compose.ui.Modifier modifier;
+ property public final kotlin.jvm.functions.Function0<kotlin.Unit> onClick;
+ }
+
+ @SuppressCompatibility @androidx.wear.compose.material.ExperimentalWearMaterialApi public final class SwipeToRevealActionColors {
+ ctor public SwipeToRevealActionColors(long actionBackgroundColor, long actionContentColor, long additionalActionBackgroundColor, long additionalActionContentColor, long undoActionBackgroundColor, long undoActionContentColor);
+ method public long getActionBackgroundColor();
+ method public long getActionContentColor();
+ method public long getAdditionalActionBackgroundColor();
+ method public long getAdditionalActionContentColor();
+ method public long getUndoActionBackgroundColor();
+ method public long getUndoActionContentColor();
+ property public final long actionBackgroundColor;
+ property public final long actionContentColor;
+ property public final long additionalActionBackgroundColor;
+ property public final long additionalActionContentColor;
+ property public final long undoActionBackgroundColor;
+ property public final long undoActionContentColor;
+ }
+
+ @SuppressCompatibility @androidx.wear.compose.material.ExperimentalWearMaterialApi public final class SwipeToRevealDefaults {
+ method @androidx.compose.runtime.Composable public androidx.wear.compose.material.SwipeToRevealAction action(kotlin.jvm.functions.Function0<kotlin.Unit> icon, kotlin.jvm.functions.Function0<kotlin.Unit> label, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional kotlin.jvm.functions.Function0<kotlin.Unit> onClick);
+ method @androidx.compose.runtime.Composable public androidx.wear.compose.material.SwipeToRevealActionColors actionColors(optional long actionBackgroundColor, optional long actionContentColor, optional long additionalActionBackgroundColor, optional long additionalActionContentColor, optional long undoActionBackgroundColor, optional long undoActionContentColor);
+ method @androidx.compose.runtime.Composable public androidx.wear.compose.material.SwipeToRevealAction additionalAction(kotlin.jvm.functions.Function0<kotlin.Unit> icon, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional kotlin.jvm.functions.Function0<kotlin.Unit> onClick);
+ method public androidx.compose.foundation.shape.RoundedCornerShape getCardActionShape();
+ method public androidx.compose.ui.graphics.vector.ImageVector getDelete();
+ method public androidx.compose.ui.graphics.vector.ImageVector getMoreOptions();
+ method @androidx.compose.runtime.Composable public androidx.wear.compose.material.SwipeToRevealAction undoAction(kotlin.jvm.functions.Function0<kotlin.Unit> label, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource, optional kotlin.jvm.functions.Function0<kotlin.Unit> onClick);
+ property public final androidx.compose.foundation.shape.RoundedCornerShape CardActionShape;
+ property public final androidx.compose.ui.graphics.vector.ImageVector Delete;
+ property public final androidx.compose.ui.graphics.vector.ImageVector MoreOptions;
+ field public static final androidx.wear.compose.material.SwipeToRevealDefaults INSTANCE;
+ }
+
+ public final class SwipeToRevealKt {
+ method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.wear.compose.material.ExperimentalWearMaterialApi public static void SwipeToRevealCard(androidx.wear.compose.material.SwipeToRevealAction action, androidx.wear.compose.foundation.RevealState revealState, optional androidx.compose.ui.Modifier modifier, optional androidx.wear.compose.material.SwipeToRevealAction? additionalAction, optional androidx.wear.compose.material.SwipeToRevealAction? undoAction, optional androidx.wear.compose.material.SwipeToRevealActionColors colors, optional androidx.compose.ui.graphics.Shape shape, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ method @SuppressCompatibility @androidx.compose.runtime.Composable @androidx.wear.compose.material.ExperimentalWearMaterialApi public static void SwipeToRevealChip(androidx.wear.compose.material.SwipeToRevealAction action, androidx.wear.compose.foundation.RevealState revealState, optional androidx.compose.ui.Modifier modifier, optional androidx.wear.compose.material.SwipeToRevealAction? additionalAction, optional androidx.wear.compose.material.SwipeToRevealAction? undoAction, optional androidx.wear.compose.material.SwipeToRevealActionColors colors, optional androidx.compose.ui.graphics.Shape shape, kotlin.jvm.functions.Function0<kotlin.Unit> content);
+ }
+
@SuppressCompatibility @androidx.wear.compose.material.ExperimentalWearMaterialApi public final class SwipeableDefaults {
method public androidx.compose.animation.core.SpringSpec<java.lang.Float> getAnimationSpec();
method public float getVelocityThreshold();
diff --git a/wear/compose/compose-material/src/androidTest/kotlin/androidx/wear/compose/material/PlaceholderTest.kt b/wear/compose/compose-material/src/androidTest/kotlin/androidx/wear/compose/material/PlaceholderTest.kt
index 2ce728d..55f9f33 100644
--- a/wear/compose/compose-material/src/androidTest/kotlin/androidx/wear/compose/material/PlaceholderTest.kt
+++ b/wear/compose/compose-material/src/androidTest/kotlin/androidx/wear/compose/material/PlaceholderTest.kt
@@ -54,13 +54,6 @@
placeholderState = rememberPlaceholderState {
contentReady.value
}
- Chip(
- modifier = Modifier.fillMaxWidth(),
- content = {},
- onClick = {},
- colors = ChipDefaults.secondaryChipColors(),
- border = ChipDefaults.chipBorder()
- )
}
// For testing we need to manually manage the frame clock for the placeholder animation
@@ -77,19 +70,13 @@
@OptIn(ExperimentalWearMaterialApi::class)
@Test
fun placeholder_initially_show_placeholder_transitions_correctly() {
- var contentReady = false
+ lateinit var contentReady: MutableState<Boolean>
lateinit var placeholderState: PlaceholderState
rule.setContentWithTheme {
+ contentReady = remember { mutableStateOf(false) }
placeholderState = rememberPlaceholderState {
- contentReady
+ contentReady.value
}
- Chip(
- modifier = Modifier.fillMaxWidth(),
- content = {},
- onClick = {},
- colors = ChipDefaults.secondaryChipColors(),
- border = ChipDefaults.chipBorder()
- )
}
// For testing we need to manually manage the frame clock for the placeholder animation
@@ -99,12 +86,13 @@
// ShowPlaceholder
placeholderState.advanceFrameMillisAndCheckState(
PLACEHOLDER_SHIMMER_GAP_BETWEEN_ANIMATION_LOOPS_MS,
- PlaceholderStage.ShowPlaceholder)
+ PlaceholderStage.ShowPlaceholder
+ )
// Change contentReady and confirm that state is now WipeOff
- contentReady = true
+ contentReady.value = true
placeholderState.advanceFrameMillisAndCheckState(
- 0L,
+ 1L,
PlaceholderStage.WipeOff
)
@@ -209,18 +197,18 @@
expectedPlaceholderColor
)
+ // Change contentReady and confirm that state is now WipeOff
contentReady.value = true
+ placeholderState.advanceFrameMillisAndCheckState(
+ 1L,
+ PlaceholderStage.WipeOff
+ )
- // Advance the clock to the next placeholder animation loop and check for wipe-off mode
- placeholderState
- .advanceFrameMillisAndCheckState(
- (PLACEHOLDER_WIPE_OFF_PROGRESSION_DURATION_MS * 0.5f).toLong(),
- PlaceholderStage.WipeOff
- )
-
- // Advance the clock to the next placeholder animation loop and check for show content mode
- placeholderState
- .advanceToNextPlaceholderAnimationLoopAndCheckStage(PlaceholderStage.ShowContent)
+ // Advance the clock by one cycle and check we have moved to ShowContent
+ placeholderState.advanceFrameMillisAndCheckState(
+ PLACEHOLDER_WIPE_OFF_PROGRESSION_DURATION_MS,
+ PlaceholderStage.ShowContent
+ )
rule.onNodeWithTag("test-item")
.captureToImage()
@@ -269,7 +257,7 @@
// Move the start of the next placeholder shimmer animation loop and them advance the
// clock to show the shimmer.
placeholderState.advanceFrameMillisAndCheckState(
- (PLACEHOLDER_SHIMMER_DURATION_MS * 0.5f).toLong(),
+ (PLACEHOLDER_SHIMMER_DURATION_MS * 0.5f).toLong(),
PlaceholderStage.ShowPlaceholder
)
@@ -279,11 +267,12 @@
.captureToImage()
.assertDoesNotContainColor(expectedBackgroundColor)
- // Prepare to start to wipe off and show contents.
+ // Change contentReady and confirm that state is now WipeOff
contentReady.value = true
-
- placeholderState
- .advanceToNextPlaceholderAnimationLoopAndCheckStage(PlaceholderStage.WipeOff)
+ placeholderState.advanceFrameMillisAndCheckState(
+ 1L,
+ PlaceholderStage.WipeOff
+ )
// Check the background color is correct
rule.onNodeWithTag("test-item")
@@ -292,8 +281,11 @@
expectedBackgroundColor, 80f
)
- placeholderState
- .advanceToNextPlaceholderAnimationLoopAndCheckStage(PlaceholderStage.ShowContent)
+ // Advance the clock by one cycle and check we have moved to ShowContent
+ placeholderState.advanceFrameMillisAndCheckState(
+ PLACEHOLDER_WIPE_OFF_PROGRESSION_DURATION_MS,
+ PlaceholderStage.ShowContent
+ )
// Check that the shimmer is no longer visible
rule.onNodeWithTag("test-item")
@@ -352,11 +344,12 @@
expectedBackgroundColor
)
- // Prepare to start to wipe off and show contents.
+ // Change contentReady and confirm that state is now WipeOff
contentReady.value = true
-
- placeholderState
- .advanceToNextPlaceholderAnimationLoopAndCheckStage(PlaceholderStage.WipeOff)
+ placeholderState.advanceFrameMillisAndCheckState(
+ 1L,
+ PlaceholderStage.WipeOff
+ )
// Check that placeholder background is still visible
rule.onNodeWithTag("test-item")
@@ -434,10 +427,12 @@
// Trigger move to WipeOff stage
placeholderState.value?.advanceFrameMillisAndCheckState(
- 1, PlaceholderStage.WipeOff)
+ 1, PlaceholderStage.WipeOff
+ )
placeholderState.value?.advanceFrameMillisAndCheckState(
- PLACEHOLDER_WIPE_OFF_PROGRESSION_DURATION_MS, PlaceholderStage.ShowContent)
+ PLACEHOLDER_WIPE_OFF_PROGRESSION_DURATION_MS, PlaceholderStage.ShowContent
+ )
}
@RequiresApi(Build.VERSION_CODES.O)
@@ -458,6 +453,7 @@
Chip(
modifier = Modifier
.testTag("test-item")
+ .fillMaxWidth()
.placeholderShimmer(placeholderState),
content = {},
onClick = {},
@@ -480,14 +476,16 @@
expectedPlaceholderBackgroundColor
)
+ // Change contentReady and confirm that state is now WipeOff
contentReady.value = true
-
- // Trigger move to WipeOff stage
placeholderState.advanceFrameMillisAndCheckState(
- 1, PlaceholderStage.WipeOff)
+ 1L, PlaceholderStage.WipeOff
+ )
placeholderState.advanceFrameMillisAndCheckState(
- PLACEHOLDER_WIPE_OFF_PROGRESSION_DURATION_MS, PlaceholderStage.ShowContent)
+ PLACEHOLDER_WIPE_OFF_PROGRESSION_DURATION_MS,
+ PlaceholderStage.ShowContent
+ )
// Check the placeholder background has gone and that we can see the chips background
rule.onNodeWithTag("test-item")
diff --git a/wear/compose/compose-material/src/androidTest/kotlin/androidx/wear/compose/material/SwipeToRevealTest.kt b/wear/compose/compose-material/src/androidTest/kotlin/androidx/wear/compose/material/SwipeToRevealTest.kt
new file mode 100644
index 0000000..e30df01
--- /dev/null
+++ b/wear/compose/compose-material/src/androidTest/kotlin/androidx/wear/compose/material/SwipeToRevealTest.kt
@@ -0,0 +1,377 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.material
+
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.captureToImage
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.performClick
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.foundation.ExperimentalWearFoundationApi
+import androidx.wear.compose.foundation.RevealState
+import androidx.wear.compose.foundation.RevealValue
+import androidx.wear.compose.foundation.rememberRevealState
+import junit.framework.TestCase.assertEquals
+import org.junit.Rule
+import org.junit.Test
+
+@OptIn(ExperimentalWearFoundationApi::class, ExperimentalWearMaterialApi::class)
+class SwipeToRevealActionTest {
+ @get:Rule
+ val rule = createComposeRule()
+
+ @Test
+ fun supports_testTag_onChip() {
+ rule.setContentWithTheme {
+ swipeToRevealChipDefault(modifier = Modifier.testTag(TEST_TAG))
+ }
+
+ rule.onNodeWithTag(TEST_TAG).assertExists()
+ }
+
+ @Test
+ fun supports_testTag_onCard() {
+ rule.setContentWithTheme {
+ swipeToRevealCardDefault(modifier = Modifier.testTag(TEST_TAG))
+ }
+
+ rule.onNodeWithTag(TEST_TAG).assertExists()
+ }
+
+ @Test
+ fun supports_testTag_onContent_onChip() {
+ rule.setContentWithTheme {
+ swipeToRevealChipDefault()
+ }
+
+ rule.onNodeWithTag(CONTENT_TAG).assertExists()
+ }
+
+ @Test
+ fun supports_testTag_onContent_onCard() {
+ rule.setContentWithTheme {
+ swipeToRevealCardDefault()
+ }
+
+ rule.onNodeWithTag(CONTENT_TAG).assertExists()
+ }
+
+ @Test
+ fun whenNotRevealed_actionsDoNotExist_inChip() {
+ rule.setContentWithTheme {
+ swipeToRevealChipDefault()
+ }
+
+ rule.onNodeWithTag(ACTION_TAG).assertDoesNotExist()
+ rule.onNodeWithTag(ADDITIONAL_ACTION_TAG).assertDoesNotExist()
+ rule.onNodeWithTag(UNDO_ACTION_TAG).assertDoesNotExist()
+ }
+
+ @Test
+ fun whenNotRevealed_actionsDoNotExist_inCard() {
+ rule.setContentWithTheme {
+ swipeToRevealCardDefault()
+ }
+
+ rule.onNodeWithTag(ACTION_TAG).assertDoesNotExist()
+ rule.onNodeWithTag(ADDITIONAL_ACTION_TAG).assertDoesNotExist()
+ rule.onNodeWithTag(UNDO_ACTION_TAG).assertDoesNotExist()
+ }
+
+ @Test
+ fun whenRevealing_actionsExist_inChip() {
+ rule.setContentWithTheme {
+ swipeToRevealChipDefault(
+ revealState = rememberRevealState(initialValue = RevealValue.Revealing)
+ )
+ }
+ rule.onNodeWithTag(ACTION_TAG).assertExists()
+ rule.onNodeWithTag(ADDITIONAL_ACTION_TAG).assertExists()
+ }
+
+ @Test
+ fun whenRevealing_actionsExist_inCard() {
+ rule.setContentWithTheme {
+ swipeToRevealCardDefault(
+ revealState = rememberRevealState(initialValue = RevealValue.Revealing)
+ )
+ }
+ rule.onNodeWithTag(ACTION_TAG).assertExists()
+ rule.onNodeWithTag(ADDITIONAL_ACTION_TAG).assertExists()
+ }
+
+ @Test
+ fun whenRevealed_undoActionExists_inChip() {
+ rule.setContentWithTheme {
+ swipeToRevealChipDefault(
+ revealState = rememberRevealState(initialValue = RevealValue.Revealed)
+ )
+ }
+
+ rule.onNodeWithTag(UNDO_ACTION_TAG).assertExists()
+ }
+
+ @Test
+ fun whenRevealed_undoActionExists_inCard() {
+ rule.setContentWithTheme {
+ swipeToRevealChipDefault(
+ revealState = rememberRevealState(initialValue = RevealValue.Revealed)
+ )
+ }
+
+ rule.onNodeWithTag(UNDO_ACTION_TAG).assertExists()
+ }
+
+ @Test
+ fun whenRevealed_actionsDoesNotExists_inChip() {
+ rule.setContentWithTheme {
+ swipeToRevealChipDefault(
+ revealState = rememberRevealState(initialValue = RevealValue.Revealed)
+ )
+ }
+
+ rule.onNodeWithTag(ACTION_TAG).assertDoesNotExist()
+ rule.onNodeWithTag(ADDITIONAL_ACTION_TAG).assertDoesNotExist()
+ }
+
+ @Test
+ fun whenRevealed_actionsDoesNotExists_inCard() {
+ rule.setContentWithTheme {
+ swipeToRevealCardDefault(
+ revealState = rememberRevealState(initialValue = RevealValue.Revealed)
+ )
+ }
+
+ rule.onNodeWithTag(ACTION_TAG).assertDoesNotExist()
+ rule.onNodeWithTag(ADDITIONAL_ACTION_TAG).assertDoesNotExist()
+ }
+
+ @Test
+ fun onActionClick_triggersOnClick_forChip() {
+ var clicked = false
+ rule.setContentWithTheme {
+ swipeToRevealChipDefault(
+ revealState = rememberRevealState(initialValue = RevealValue.Revealing),
+ action = createAction(
+ onClick = { clicked = true }
+ )
+ )
+ }
+
+ rule.onNodeWithTag(ACTION_TAG).performClick()
+ rule.runOnIdle { assertEquals(true, clicked) }
+ }
+
+ @Test
+ fun onAdditionalActionClick_triggersOnClick_forChip() {
+ var clicked = false
+ rule.setContentWithTheme {
+ swipeToRevealChipDefault(
+ revealState = rememberRevealState(initialValue = RevealValue.Revealing),
+ additionalAction = createAdditionalAction(
+ onClick = { clicked = true }
+ )
+ )
+ }
+
+ rule.onNodeWithTag(ADDITIONAL_ACTION_TAG).performClick()
+ rule.runOnIdle { assertEquals(true, clicked) }
+ }
+
+ @Test
+ fun onActionClick_stateChangesToRevealed_forChip() {
+ lateinit var revealState: RevealState
+ rule.setContentWithTheme {
+ revealState = rememberRevealState(initialValue = RevealValue.Revealing)
+ swipeToRevealChipDefault(
+ revealState = revealState,
+ )
+ }
+
+ rule.onNodeWithTag(ACTION_TAG).performClick()
+ rule.runOnIdle { assertEquals(RevealValue.Revealed, revealState.currentValue) }
+ }
+
+ @Test
+ fun onAdditionalActionClick_stateChangesToRevealed_forChip() {
+ lateinit var revealState: RevealState
+ rule.setContentWithTheme {
+ revealState = rememberRevealState(initialValue = RevealValue.Revealing)
+ swipeToRevealChipDefault(
+ revealState = revealState,
+ )
+ }
+
+ rule.onNodeWithTag(ADDITIONAL_ACTION_TAG).performClick()
+ rule.runOnIdle { assertEquals(RevealValue.Revealed, revealState.currentValue) }
+ }
+
+ @Test
+ fun onUndoActionClick_stateChangesToCovered_forChip() {
+ lateinit var revealState: RevealState
+ rule.setContentWithTheme {
+ revealState = rememberRevealState(initialValue = RevealValue.Revealed)
+ swipeToRevealChipDefault(
+ revealState = revealState,
+ )
+ }
+
+ rule.onNodeWithTag(UNDO_ACTION_TAG).performClick()
+ rule.runOnIdle { assertEquals(RevealValue.Covered, revealState.currentValue) }
+ }
+
+ @RequiresApi(Build.VERSION_CODES.O)
+ @Test
+ fun verifyActionColors() {
+ var actionColor = Color.Yellow
+ var additionalActionColor = Color.Green
+ rule.setContentWithTheme {
+ actionColor = MaterialTheme.colors.error
+ additionalActionColor = MaterialTheme.colors.surface
+ swipeToRevealChipDefault(
+ revealState = rememberRevealState(initialValue = RevealValue.Revealing)
+ )
+ }
+
+ rule.onNodeWithTag(ACTION_TAG)
+ .captureToImage()
+ .assertContainsColor(actionColor, 50.0f)
+ rule.onNodeWithTag(ADDITIONAL_ACTION_TAG)
+ .captureToImage()
+ .assertContainsColor(additionalActionColor)
+ }
+
+ @RequiresApi(Build.VERSION_CODES.O)
+ @Test
+ fun canOverrideActionColors() {
+ val overrideActionColor = Color.Yellow
+ val overrideAdditionalActionColor = Color.Green
+ rule.setContentWithTheme {
+ swipeToRevealChipDefault(
+ revealState = rememberRevealState(initialValue = RevealValue.Revealing),
+ colors = SwipeToRevealDefaults.actionColors(
+ actionBackgroundColor = overrideActionColor,
+ additionalActionBackgroundColor = overrideAdditionalActionColor
+ )
+ )
+ }
+
+ rule.onNodeWithTag(ACTION_TAG)
+ .captureToImage()
+ .assertContainsColor(overrideActionColor, 50.0f)
+ rule.onNodeWithTag(ADDITIONAL_ACTION_TAG)
+ .captureToImage()
+ .assertContainsColor(overrideAdditionalActionColor, 50.0f)
+ }
+
+ @Composable
+ private fun swipeToRevealChipDefault(
+ modifier: Modifier = Modifier,
+ revealState: RevealState = rememberRevealState(),
+ action: SwipeToRevealAction = createAction(),
+ additionalAction: SwipeToRevealAction = createAdditionalAction(),
+ undoAction: SwipeToRevealAction = createUndoAction(),
+ colors: SwipeToRevealActionColors = SwipeToRevealDefaults.actionColors(),
+ content: @Composable () -> Unit = { createContent() }
+ ) {
+ SwipeToRevealChip(
+ modifier = modifier,
+ revealState = revealState,
+ action = action,
+ additionalAction = additionalAction,
+ undoAction = undoAction,
+ colors = colors,
+ content = content
+ )
+ }
+
+ @Composable
+ private fun swipeToRevealCardDefault(
+ modifier: Modifier = Modifier,
+ revealState: RevealState = rememberRevealState(),
+ action: SwipeToRevealAction = createAction(),
+ additionalAction: SwipeToRevealAction = createAdditionalAction(),
+ undoAction: SwipeToRevealAction = createUndoAction(),
+ colors: SwipeToRevealActionColors = SwipeToRevealDefaults.actionColors(),
+ content: @Composable () -> Unit = { createContent() }
+ ) {
+ SwipeToRevealCard(
+ modifier = modifier,
+ revealState = revealState,
+ action = action,
+ additionalAction = additionalAction,
+ undoAction = undoAction,
+ colors = colors,
+ content = content
+ )
+ }
+
+ @Composable
+ private fun createAction(
+ icon: @Composable () -> Unit = { Icon(SwipeToRevealDefaults.Delete, "Delete") },
+ label: @Composable () -> Unit = { Text("Clear") },
+ modifier: Modifier = Modifier,
+ onClick: () -> Unit = {},
+ ): SwipeToRevealAction = SwipeToRevealDefaults.action(
+ icon = icon,
+ label = label,
+ modifier = modifier.testTag(ACTION_TAG),
+ onClick = onClick
+ )
+
+ @Composable
+ private fun createAdditionalAction(
+ icon: @Composable () -> Unit = { Icon(SwipeToRevealDefaults.MoreOptions, "More Options") },
+ modifier: Modifier = Modifier,
+ onClick: () -> Unit = {},
+ ): SwipeToRevealAction = SwipeToRevealDefaults.additionalAction(
+ icon = icon,
+ modifier = modifier.testTag(ADDITIONAL_ACTION_TAG),
+ onClick = onClick
+ )
+
+ @Composable
+ private fun createUndoAction(
+ label: @Composable () -> Unit = { Text("Undo") },
+ modifier: Modifier = Modifier
+ ) = SwipeToRevealDefaults.undoAction(
+ label = label,
+ modifier = modifier.testTag(UNDO_ACTION_TAG)
+ )
+
+ @Composable
+ private fun createContent(
+ modifier: Modifier = Modifier
+ ) = Box(modifier = modifier
+ .fillMaxWidth()
+ .height(50.dp)
+ .testTag(CONTENT_TAG))
+
+ private val CONTENT_TAG = "Content"
+ private val ACTION_TAG = "Action"
+ private val ADDITIONAL_ACTION_TAG = "AdditionalAction"
+ private val UNDO_ACTION_TAG = "UndoAction"
+}
diff --git a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ContentAlpha.kt b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ContentAlpha.kt
index 314e312..2f6e8b5 100644
--- a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ContentAlpha.kt
+++ b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ContentAlpha.kt
@@ -15,6 +15,7 @@
*/
package androidx.wear.compose.material
+import androidx.annotation.FloatRange
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ProvidableCompositionLocal
import androidx.compose.runtime.compositionLocalOf
@@ -69,9 +70,9 @@
*/
@Composable
private fun contentAlpha(
- /*@FloatRange(from = 0.0, to = 1.0)*/
+ @FloatRange(from = 0.0, to = 1.0)
highContrastAlpha: Float,
- /*@FloatRange(from = 0.0, to = 1.0)*/
+ @FloatRange(from = 0.0, to = 1.0)
lowContrastAlpha: Float
): Float {
val contentColor = LocalContentColor.current
diff --git a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/Picker.kt b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/Picker.kt
index 2aaa04b..6587a21 100644
--- a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/Picker.kt
+++ b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/Picker.kt
@@ -15,6 +15,7 @@
*/
package androidx.wear.compose.material
+import androidx.annotation.FloatRange
import androidx.compose.animation.core.CubicBezierEasing
import androidx.compose.animation.core.DecayAnimationSpec
import androidx.compose.animation.core.Easing
@@ -59,11 +60,11 @@
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
-import androidx.wear.compose.foundation.lazy.AutoCenteringParams as AutoCenteringParams
-import androidx.wear.compose.foundation.lazy.ScalingLazyColumn as ScalingLazyColumn
-import androidx.wear.compose.foundation.lazy.ScalingLazyColumnDefaults as ScalingLazyColumnDefaults
-import androidx.wear.compose.foundation.lazy.ScalingLazyListState as ScalingLazyListState
-import androidx.wear.compose.foundation.lazy.ScalingParams as ScalingParams
+import androidx.wear.compose.foundation.lazy.AutoCenteringParams
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
+import androidx.wear.compose.foundation.lazy.ScalingLazyColumnDefaults
+import androidx.wear.compose.foundation.lazy.ScalingLazyListState
+import androidx.wear.compose.foundation.lazy.ScalingParams
import kotlinx.coroutines.launch
/**
@@ -120,7 +121,7 @@
onSelected: () -> Unit = {},
scalingParams: ScalingParams = PickerDefaults.defaultScalingParams(),
separation: Dp = 0.dp,
- /* @FloatRange(from = 0.0, to = 0.5) */
+ @FloatRange(from = 0.0, to = 0.5)
gradientRatio: Float = PickerDefaults.DefaultGradientRatio,
gradientColor: Color = MaterialTheme.colors.background,
flingBehavior: FlingBehavior = PickerDefaults.flingBehavior(state),
@@ -245,7 +246,7 @@
onSelected: () -> Unit = {},
scalingParams: androidx.wear.compose.material.ScalingParams = PickerDefaults.scalingParams(),
separation: Dp = 0.dp,
- /* @FloatRange(from = 0.0, to = 0.5) */
+ @FloatRange(from = 0.0, to = 0.5)
gradientRatio: Float = PickerDefaults.DefaultGradientRatio,
gradientColor: Color = MaterialTheme.colors.background,
flingBehavior: FlingBehavior = PickerDefaults.flingBehavior(state),
@@ -322,7 +323,7 @@
onSelected: () -> Unit = {},
scalingParams: androidx.wear.compose.material.ScalingParams = PickerDefaults.scalingParams(),
separation: Dp = 0.dp,
- /* @FloatRange(from = 0.0, to = 0.5) */
+ @FloatRange(from = 0.0, to = 0.5)
gradientRatio: Float = PickerDefaults.DefaultGradientRatio,
gradientColor: Color = MaterialTheme.colors.background,
flingBehavior: FlingBehavior = PickerDefaults.flingBehavior(state),
@@ -389,7 +390,7 @@
readOnlyLabel: @Composable (BoxScope.() -> Unit)? = null,
scalingParams: androidx.wear.compose.material.ScalingParams = PickerDefaults.scalingParams(),
separation: Dp = 0.dp,
- /* @FloatRange(from = 0.0, to = 0.5) */
+ @FloatRange(from = 0.0, to = 0.5)
gradientRatio: Float = PickerDefaults.DefaultGradientRatio,
gradientColor: Color = MaterialTheme.colors.background,
flingBehavior: FlingBehavior = PickerDefaults.flingBehavior(state),
diff --git a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/PositionIndicator.kt b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/PositionIndicator.kt
index d5da8bc..77f29fb 100644
--- a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/PositionIndicator.kt
+++ b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/PositionIndicator.kt
@@ -16,6 +16,7 @@
package androidx.wear.compose.material
+import androidx.annotation.FloatRange
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.AnimationSpec
import androidx.compose.animation.core.AnimationVector
@@ -127,9 +128,7 @@
* Position of the indicator in the range [0f,1f]. 0f means it is at the top|start, 1f means
* it is positioned at the bottom|end.
*/
-// @FloatRange(
-// fromInclusive = true, from = 0.0, toInclusive = true, to = 1.0
-// )
+ @get:FloatRange(from = 0.0, to = 1.0)
val positionFraction: Float
/**
@@ -139,10 +138,11 @@
* in pixels depending on orientation of the indicator, (height for vertical, width for
* horizontal)
*/
-// @FloatRange(
-// fromInclusive = true, from = 0.0, toInclusive = true, to = 1.0
-// )
- fun sizeFraction(scrollableContainerSizePx: Float): Float
+ @FloatRange(from = 0.0, to = 1.0)
+ fun sizeFraction(
+ @FloatRange(from = 0.0)
+ scrollableContainerSizePx: Float
+ ): Float
/**
* Should we show the Position Indicator
@@ -151,7 +151,10 @@
* in pixels depending on orientation of the indicator, (height for vertical, width for
* horizontal)
*/
- fun visibility(scrollableContainerSizePx: Float): PositionIndicatorVisibility
+ fun visibility(
+ @FloatRange(from = 0.0)
+ scrollableContainerSizePx: Float
+ ): PositionIndicatorVisibility
}
/**
@@ -223,9 +226,11 @@
* @param reverseDirection Reverses direction of PositionIndicator if true
*/
@Suppress("DEPRECATION")
-@Deprecated("This overload is provided for backwards compatibility with Compose for Wear OS 1.1." +
+@Deprecated(
+ "This overload is provided for backwards compatibility with Compose for Wear OS 1.1." +
"A newer overload is available which uses ScalingLazyListState from " +
- "androidx.wear.compose.foundation.lazy package", level = DeprecationLevel.WARNING)
+ "androidx.wear.compose.foundation.lazy package", level = DeprecationLevel.WARNING
+)
@Composable
public fun PositionIndicator(
scalingLazyListState: androidx.wear.compose.material.ScalingLazyListState,
@@ -782,7 +787,7 @@
if (state.layoutInfo.visibleItemsInfo.isEmpty()) return 0f
val firstItem = state.layoutInfo.visibleItemsInfo.first()
val firstItemStartOffset = firstItem.startOffset(state.layoutInfo.anchorType)
- val viewportStartOffset = - (state.layoutInfo.viewportSize.height / 2f)
+ val viewportStartOffset = -(state.layoutInfo.viewportSize.height / 2f)
// Coerce item size to at least 1 to avoid divide by zero for zero height items
val firstItemInvisibleFraction =
((viewportStartOffset - firstItemStartOffset) /
@@ -866,7 +871,7 @@
if (state.layoutInfo.visibleItemsInfo.isEmpty()) return 0f
val firstItem = state.layoutInfo.visibleItemsInfo.first()
val firstItemStartOffset = firstItem.startOffset(state.anchorType.value!!)
- val viewportStartOffset = - (state.viewportHeightPx.value!! / 2f)
+ val viewportStartOffset = -(state.viewportHeightPx.value!! / 2f)
// Coerce item size to at least 1 to avoid divide by zero for zero height items
val firstItemInvisibleFraction =
((viewportStartOffset - firstItemStartOffset) /
@@ -1122,7 +1127,8 @@
modifier
.transparentSizeModifier(size)
.absoluteOffset { -offset() }, content = content,
- contentAlignment = AbsoluteAlignment.TopLeft)
+ contentAlignment = AbsoluteAlignment.TopLeft
+ )
}
// Sets the size of this element, but lets the child measure using the constraints
diff --git a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ProgressIndicator.kt b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ProgressIndicator.kt
index 2b0ebbe..c05d7b5 100644
--- a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ProgressIndicator.kt
+++ b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ProgressIndicator.kt
@@ -1,5 +1,6 @@
package androidx.wear.compose.material
+import androidx.annotation.FloatRange
import androidx.compose.animation.core.AnimationSpec
import androidx.compose.animation.core.CubicBezierEasing
import androidx.compose.animation.core.LinearEasing
@@ -78,7 +79,7 @@
*/
@Composable
public fun CircularProgressIndicator(
- /* @FloatRange(fromInclusive = true, from = 0.0, toInclusive = true, to = 1.0) */
+ @FloatRange(from = 0.0, to = 1.0)
progress: Float,
modifier: Modifier = Modifier,
startAngle: Float = 270f,
diff --git a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ScalingLazyColumnMeasure.kt b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ScalingLazyColumnMeasure.kt
index 382be50..b3abd6e 100644
--- a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ScalingLazyColumnMeasure.kt
+++ b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ScalingLazyColumnMeasure.kt
@@ -18,6 +18,7 @@
package androidx.wear.compose.material
+import androidx.annotation.FloatRange
import androidx.compose.animation.core.Easing
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.lazy.LazyListItemInfo
@@ -104,9 +105,7 @@
* scaled, e.g. at the edge of the viewport. A value between [0f,1f], so a value of 0.2f
* means to scale an item to 20% of its normal size.
*/
-// @FloatRange(
-// fromInclusive = true, from = 0.0, toInclusive = true, to = 1.0
-// )
+ @get:FloatRange(from = 0.0, to = 1.0)
val edgeScale: Float
/**
@@ -114,9 +113,7 @@
* when closest to the edge of the screen. A value between [0f,1f], so a value of
* 0.2f means to set the alpha of an item to 20% of its normal value.
*/
-// @FloatRange(
-// fromInclusive = true, from = 0.0, toInclusive = true, to = 1.0
-// )
+ @get:FloatRange(from = 0.0, to = 1.0)
val edgeAlpha: Float
/**
@@ -126,9 +123,7 @@
* will be treated as if [maxElementHeight]. Must be greater than or equal to
* [minElementHeight].
*/
-// @FloatRange(
-// fromInclusive = true, from = 0.0, toInclusive = true, to = 1.0
-// )
+ @get:FloatRange(from = 0.0, to = 1.0)
val minElementHeight: Float
/**
@@ -138,9 +133,7 @@
* will be treated as if [maxElementHeight]. Must be greater than or equal to
* [minElementHeight].
*/
-// @FloatRange(
-// fromInclusive = true, from = 0.0, toInclusive = true, to = 1.0
-// )
+ @get:FloatRange(from = 0.0, to = 1.0)
val maxElementHeight: Float
/**
@@ -157,9 +150,7 @@
* list items exist. Depending on the size of the list item the specific point in the area is
* calculated.
*/
-// @FloatRange(
-// fromInclusive = true, from = 0.0, toInclusive = true, to = 1.0
-// )
+ @get:FloatRange(from = 0.0, to = 1.0)
val minTransitionArea: Float
/**
@@ -176,9 +167,7 @@
* list items exist. Depending on the size of the list item the specific point in the area is
* calculated.
*/
-// @FloatRange(
-// fromInclusive = true, from = 0.0, toInclusive = true, to = 1.0
-// )
+ @get:FloatRange(from = 0.0, to = 1.0)
val maxTransitionArea: Float
/**
diff --git a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ScalingLazyListItemScope.kt b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ScalingLazyListItemScope.kt
index d346a0b..2f2c6fad 100644
--- a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ScalingLazyListItemScope.kt
+++ b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ScalingLazyListItemScope.kt
@@ -17,6 +17,7 @@
package androidx.wear.compose.material
+import androidx.annotation.FloatRange
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
@@ -46,7 +47,7 @@
* measured with [Constraints.Infinity] as the constraints for the main axis.
*/
fun Modifier.fillParentMaxSize(
- /*@FloatRange(from = 0.0, to = 1.0)*/
+ @FloatRange(from = 0.0, to = 1.0)
fraction: Float = 1f
): Modifier
@@ -61,7 +62,7 @@
* items are measured with [Constraints.Infinity] as the constraints for the main axis.
*/
fun Modifier.fillParentMaxWidth(
- /*@FloatRange(from = 0.0, to = 1.0)*/
+ @FloatRange(from = 0.0, to = 1.0)
fraction: Float = 1f
): Modifier
@@ -76,7 +77,7 @@
* items are measured with [Constraints.Infinity] as the constraints for the main axis.
*/
fun Modifier.fillParentMaxHeight(
- /*@FloatRange(from = 0.0, to = 1.0)*/
+ @FloatRange(from = 0.0, to = 1.0)
fraction: Float = 1f
): Modifier
}
diff --git a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/SwipeToReveal.kt b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/SwipeToReveal.kt
new file mode 100644
index 0000000..59b4635
--- /dev/null
+++ b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/SwipeToReveal.kt
@@ -0,0 +1,537 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.material
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.heightIn
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Delete
+import androidx.compose.material.icons.outlined.MoreVert
+import androidx.compose.material.ripple.rememberRipple
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.unit.dp
+import androidx.wear.compose.foundation.ExperimentalWearFoundationApi
+import androidx.wear.compose.foundation.RevealScope
+import androidx.wear.compose.foundation.RevealState
+import androidx.wear.compose.foundation.RevealValue
+import androidx.wear.compose.foundation.SwipeToReveal
+import kotlin.math.abs
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+/**
+ * [SwipeToReveal] Material composable for Chips. This provides the default style for consistency.
+ *
+ * @see [SwipeToReveal]
+ * @param action An [SwipeToRevealAction] object to describe the primary action when swiping. See
+ * [SwipeToRevealDefaults.action].
+ * @param revealState [RevealState] of the [SwipeToReveal]
+ * @param modifier [Modifier] to be applied on the composable
+ * @param additionalAction An [SwipeToRevealAction] object to describe the contents of addition action.
+ * See [SwipeToRevealDefaults.additionalAction]
+ * @param undoAction An [SwipeToRevealAction] object to describe the contents of undo action. See
+ * [SwipeToRevealDefaults.undoAction].
+ * @param colors An instance of [SwipeToRevealActionColors] to describe the colors of actions. See
+ * [SwipeToRevealDefaults.actionColors].
+ * @param shape The shape of action and additional action composables. Recommended shape for chips
+ * is [Shapes.small].
+ * @param content The initial content shown prior to the swipe-to-reveal gesture.
+ */
+@ExperimentalWearMaterialApi
+@OptIn(ExperimentalWearFoundationApi::class)
+@Composable
+public fun SwipeToRevealChip(
+ action: SwipeToRevealAction,
+ revealState: RevealState,
+ modifier: Modifier = Modifier,
+ additionalAction: SwipeToRevealAction? = null,
+ undoAction: SwipeToRevealAction? = null,
+ colors: SwipeToRevealActionColors = SwipeToRevealDefaults.actionColors(),
+ shape: Shape = MaterialTheme.shapes.small,
+ content: @Composable () -> Unit
+) {
+ SwipeToRevealComponent(
+ action = action,
+ revealState = revealState,
+ modifier = modifier,
+ additionalAction = additionalAction,
+ undoAction = undoAction,
+ colors = colors,
+ shape = shape,
+ content = content
+ )
+}
+
+/**
+ * [SwipeToReveal] Material composable for Cards. This provides the default style for consistency.
+ *
+ * @see [SwipeToReveal]
+ * @param action An [SwipeToRevealAction] object to describe the primary action when swiping. See
+ * [SwipeToRevealDefaults.action]
+ * @param revealState [RevealState] of the [SwipeToReveal]
+ * @param modifier [Modifier] to be applied on the composable
+ * @param additionalAction An [SwipeToRevealAction] object to describe the contents of addition action
+ * @param undoAction An [SwipeToRevealAction] object to describe the contents of undo action
+ * @param colors An instance of [SwipeToRevealActionColors] to describe the colors of actions. See
+ * [SwipeToRevealDefaults.actionColors].
+ * @param shape The shape of action and additional action composables. Recommended shape for cards
+ * is [SwipeToRevealDefaults.CardActionShape].
+ * @param content The initial content shown prior to the swipe-to-reveal gesture.
+ */
+@ExperimentalWearMaterialApi
+@OptIn(ExperimentalWearFoundationApi::class)
+@Composable
+public fun SwipeToRevealCard(
+ action: SwipeToRevealAction,
+ revealState: RevealState,
+ modifier: Modifier = Modifier,
+ additionalAction: SwipeToRevealAction? = null,
+ undoAction: SwipeToRevealAction? = null,
+ colors: SwipeToRevealActionColors = SwipeToRevealDefaults.actionColors(),
+ shape: Shape = SwipeToRevealDefaults.CardActionShape,
+ content: @Composable () -> Unit
+) {
+ SwipeToRevealComponent(
+ action = action,
+ revealState = revealState,
+ modifier = modifier,
+ additionalAction = additionalAction,
+ undoAction = undoAction,
+ colors = colors,
+ shape = shape,
+ content = content
+ )
+}
+
+/**
+ * Defaults for Material [SwipeToReveal].
+ */
+@ExperimentalWearMaterialApi
+public object SwipeToRevealDefaults {
+ /**
+ * Recommended shape for [SwipeToReveal] actions when used with [Card].
+ */
+ public val CardActionShape = RoundedCornerShape(40.dp)
+
+ /**
+ * Colors to be used with different actions in [SwipeToReveal].
+ *
+ * @param actionBackgroundColor The background color (color of the shape) of the action
+ * @param actionContentColor The content color (text and icon) of the action
+ * @param additionalActionBackgroundColor The background color (color of the shape) of the
+ * additional action
+ * @param additionalActionContentColor The content color (text and icon) of the additional
+ * action
+ * @param undoActionBackgroundColor The background color (color of the shape) of the undo action
+ * @param undoActionContentColor The content color (text) of the undo action
+ */
+ @Composable
+ public fun actionColors(
+ actionBackgroundColor: Color = MaterialTheme.colors.error,
+ actionContentColor: Color = MaterialTheme.colors.onError,
+ additionalActionBackgroundColor: Color = MaterialTheme.colors.surface,
+ additionalActionContentColor: Color = MaterialTheme.colors.onSurface,
+ undoActionBackgroundColor: Color = MaterialTheme.colors.surface,
+ undoActionContentColor: Color = MaterialTheme.colors.onSurface
+ ): SwipeToRevealActionColors {
+ return SwipeToRevealActionColors(
+ actionBackgroundColor = actionBackgroundColor,
+ actionContentColor = actionContentColor,
+ additionalActionBackgroundColor = additionalActionBackgroundColor,
+ additionalActionContentColor = additionalActionContentColor,
+ undoActionBackgroundColor = undoActionBackgroundColor,
+ undoActionContentColor = undoActionContentColor
+ )
+ }
+
+ /**
+ * Creates a new [SwipeToRevealAction] instance for the primary action parameter of [SwipeToRevealChip] and
+ * [SwipeToRevealCard]. The default behaviour of this action is to animate to
+ * [RevealValue.Revealed] and execute the [onClick] parameter. To override, consider using
+ * [Modifier.clickable].
+ *
+ * @param icon The icon that will be displayed initially on the action
+ * @param label The text that will be displayed on the expanded action
+ * @param modifier [Modifier] to be applied on this action composable
+ * @param interactionSource The [MutableInteractionSource] representing the stream of
+ * interactions with this action.
+ * @param onClick Called when this action is clicked.
+ */
+ @Composable
+ public fun action(
+ icon: @Composable () -> Unit,
+ label: @Composable () -> Unit,
+ modifier: Modifier = Modifier,
+ interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+ onClick: () -> Unit = {}
+ ): SwipeToRevealAction {
+ return SwipeToRevealAction(
+ icon = icon,
+ label = label,
+ modifier = modifier,
+ interactionSource = interactionSource,
+ onClick = onClick
+ )
+ }
+
+ /**
+ * Creates a new [SwipeToRevealAction] instance for the additional action of [SwipeToRevealChip] and
+ * [SwipeToRevealCard]. The default behaviour of this action is to animate to
+ * [RevealValue.Revealed] and execute the [onClick] parameter. To override, consider using
+ * [Modifier.clickable].
+ *
+ * @param icon The icon that will be displayed initially on the screen
+ * @param modifier [Modifier] to be applied on this action composable
+ * @param interactionSource The [MutableInteractionSource] representing the stream of
+ * interactions with this action.
+ * @param onClick Called when this action is clicked.
+ */
+ @Composable
+ public fun additionalAction(
+ icon: @Composable () -> Unit,
+ modifier: Modifier = Modifier,
+ interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+ onClick: () -> Unit = {}
+ ): SwipeToRevealAction {
+ return SwipeToRevealAction(
+ icon = icon,
+ label = null,
+ modifier = modifier,
+ interactionSource = interactionSource,
+ onClick = onClick
+ )
+ }
+
+ /**
+ * Creates a new [SwipeToRevealAction] instance for the undo action of [SwipeToRevealChip] and
+ * [SwipeToRevealCard]. The default behaviour of this action is to snap to
+ * [RevealValue.Covered] and execute the [onClick] parameter. To override, consider using
+ * [Modifier.clickable].
+ *
+ * @param label The text that will be displayed on the undo action composable
+ * @param modifier [Modifier] to be applied on this action composable
+ * @param interactionSource The [MutableInteractionSource] representing the stream of
+ * interactions with this action.
+ * @param onClick Called when this action is clicked.
+ */
+ @Composable
+ public fun undoAction(
+ label: @Composable () -> Unit,
+ modifier: Modifier = Modifier,
+ interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
+ onClick: () -> Unit = {}
+ ): SwipeToRevealAction {
+ return SwipeToRevealAction(
+ icon = null,
+ label = label,
+ modifier = modifier,
+ interactionSource = interactionSource,
+ onClick = onClick
+ )
+ }
+
+ /**
+ * [ImageVector] for delete icon, often used for the primary action.
+ */
+ public val Delete = Icons.Outlined.Delete
+
+ /**
+ * [ImageVector] for more options icon, often used for the additional action.
+ */
+ public val MoreOptions = Icons.Outlined.MoreVert
+
+ internal val UndoButtonHorizontalPadding = 14.dp
+ internal val UndoButtonVerticalPadding = 6.dp
+ internal val ActionMaxHeight = 84.dp
+}
+
+/**
+ * A class representing the colors applied in [SwipeToReveal] actions.
+ *
+ * @param actionBackgroundColor Color of the shape (background)
+ * @param actionContentColor Color of icon or text used in the action
+ * @param additionalActionBackgroundColor Color of the additional action shape (background)
+ * @param additionalActionContentColor Color of the icon or text used in the additional action
+ * @param undoActionBackgroundColor Color of the undo action shape (background)
+ * @param undoActionContentColor Color of the icon or text used in the undo action
+ */
+@ExperimentalWearMaterialApi
+public class SwipeToRevealActionColors constructor(
+ val actionBackgroundColor: Color,
+ val actionContentColor: Color,
+ val additionalActionBackgroundColor: Color,
+ val additionalActionContentColor: Color,
+ val undoActionBackgroundColor: Color,
+ val undoActionContentColor: Color
+) {
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other == null) return false
+ if (this::class != other::class) return false
+
+ other as SwipeToRevealActionColors
+
+ if (actionBackgroundColor != other.actionBackgroundColor) return false
+ if (actionContentColor != other.actionContentColor) return false
+ if (additionalActionBackgroundColor != other.additionalActionBackgroundColor) return false
+ if (additionalActionContentColor != other.additionalActionContentColor) return false
+ if (undoActionBackgroundColor != other.undoActionBackgroundColor) return false
+ if (undoActionContentColor != other.undoActionContentColor) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = actionBackgroundColor.hashCode()
+ result = 31 * result + actionContentColor.hashCode()
+ result = 31 * result + additionalActionBackgroundColor.hashCode()
+ result = 31 * result + additionalActionContentColor.hashCode()
+ result = 31 * result + undoActionBackgroundColor.hashCode()
+ result = 31 * result + undoActionContentColor.hashCode()
+ return result
+ }
+}
+
+/**
+ * A class containing the details required for describing the content of an action composable.
+ *
+ * @param icon A slot for providing the icon for this [SwipeToReveal] action
+ * @param label A slot for providing a text label for this [SwipeToRevealAction] action. The
+ * content provided here will be used in different perspective based on the action type
+ * (action, additional action or undo action).
+ * @param modifier The [Modifier] to be applied on the action.
+ * @param interactionSource The [MutableInteractionSource] representing the stream of
+ * interactions with this action.
+ * @param onClick Called when the user clicks the action.
+ */
+@ExperimentalWearMaterialApi
+public class SwipeToRevealAction constructor(
+ val icon: (@Composable () -> Unit)?,
+ val label: (@Composable () -> Unit)?,
+ val modifier: Modifier,
+ val interactionSource: MutableInteractionSource,
+ val onClick: () -> Unit
+) {
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other == null) return false
+ if (this::class != other::class) return false
+
+ other as SwipeToRevealAction
+
+ if (icon != other.icon) return false
+ if (label != other.label) return false
+ if (modifier != other.modifier) return false
+ if (interactionSource != other.interactionSource) return false
+ if (onClick != other.onClick) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = icon.hashCode()
+ result = 31 * result + label.hashCode()
+ result = 31 * result + modifier.hashCode()
+ result = 31 * result + interactionSource.hashCode()
+ result = 31 * result + onClick.hashCode()
+ return result
+ }
+}
+
+@OptIn(ExperimentalWearMaterialApi::class, ExperimentalWearFoundationApi::class)
+@Composable
+private fun SwipeToRevealComponent(
+ action: SwipeToRevealAction,
+ revealState: RevealState,
+ modifier: Modifier,
+ additionalAction: SwipeToRevealAction?,
+ undoAction: SwipeToRevealAction?,
+ colors: SwipeToRevealActionColors,
+ shape: Shape,
+ content: @Composable () -> Unit
+) {
+ val coroutineScope = rememberCoroutineScope()
+ SwipeToReveal(
+ state = revealState,
+ modifier = modifier,
+ onFullSwipe = action.onClick,
+ action = {
+ SwipeToRevealAction(
+ revealState = revealState,
+ coroutineScope = coroutineScope,
+ action = action,
+ backgroundColor = colors.actionBackgroundColor,
+ contentColor = colors.actionContentColor,
+ shape = shape,
+ animateTo = RevealValue.Revealed
+ )
+ },
+ additionalAction =
+ additionalAction?.let {
+ {
+ SwipeToRevealAction(
+ revealState = revealState,
+ coroutineScope = coroutineScope,
+ action = additionalAction,
+ backgroundColor = colors.additionalActionBackgroundColor,
+ contentColor = colors.additionalActionContentColor,
+ shape = shape,
+ animateTo = RevealValue.Revealed
+ )
+ }
+ },
+ undoAction =
+ undoAction?.label?.let {
+ {
+ Box(
+ modifier = undoAction.modifier
+ .clickable(
+ interactionSource = undoAction.interactionSource,
+ indication = rememberRipple(),
+ role = Role.Button,
+ onClick = {
+ coroutineScope.launch { revealState.animateTo(RevealValue.Covered) }
+ undoAction.onClick()
+ }
+ )
+ .clip(CircleShape)
+ .background(color = colors.undoActionBackgroundColor)
+ .padding(
+ horizontal = SwipeToRevealDefaults.UndoButtonHorizontalPadding,
+ vertical = SwipeToRevealDefaults.UndoButtonVerticalPadding
+ ),
+ contentAlignment = Alignment.Center
+ ) {
+ CompositionLocalProvider(
+ LocalContentColor provides colors.undoActionContentColor
+ ) {
+ undoAction.label.invoke()
+ }
+ }
+ }
+ },
+ content = content
+ )
+}
+
+/**
+ * Action composables for [SwipeToReveal].
+ */
+@OptIn(ExperimentalWearFoundationApi::class, ExperimentalWearMaterialApi::class)
+@Composable
+private fun RevealScope.SwipeToRevealAction(
+ revealState: RevealState,
+ coroutineScope: CoroutineScope,
+ action: SwipeToRevealAction,
+ backgroundColor: Color,
+ contentColor: Color,
+ shape: Shape,
+ animateTo: RevealValue
+) {
+ // Change opacity of shape from 0% to 100% between 10% and 20% of the progress
+ val shapeAlpha =
+ if (revealOffset > 0)
+ ((-revealState.offset - revealOffset * 0.1f) / (0.1f * revealOffset))
+ .coerceIn(0.0f, 1.0f)
+ else 1f
+ Row(
+ modifier = action.modifier
+ .graphicsLayer { alpha = shapeAlpha }
+ .background(backgroundColor, shape)
+ // Limit the incoming constraints to max height
+ .heightIn(min = 0.dp, max = SwipeToRevealDefaults.ActionMaxHeight)
+ // Then, fill the max height based on incoming constraints
+ .fillMaxSize()
+ .clickable(
+ interactionSource = action.interactionSource,
+ indication = rememberRipple(),
+ role = Role.Button,
+ onClick = {
+ coroutineScope.launch { revealState.animateTo(animateTo) }
+ action.onClick()
+ }
+ ),
+ horizontalArrangement = Arrangement.Center,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ CompositionLocalProvider(
+ LocalContentColor provides contentColor
+ ) {
+ if (action.icon != null) {
+ ActionIcon(
+ revealState = revealState,
+ content = action.icon
+ )
+ }
+ if (abs(revealState.offset) > revealOffset && action.label != null) {
+ Spacer(Modifier.size(5.dp))
+ action.label.invoke()
+ }
+ }
+ }
+}
+
+@OptIn(ExperimentalWearFoundationApi::class)
+@Composable
+private fun RevealScope.ActionIcon(
+ revealState: RevealState,
+ content: @Composable () -> Unit
+) {
+ // Change opacity of icons from 0% to 100% between 50% to 75% of the progress
+ val iconAlpha =
+ if (revealOffset > 0)
+ ((-revealState.offset - revealOffset * 0.5f) / (revealOffset * 0.25f))
+ .coerceIn(0.0f, 1.0f)
+ else 1f
+ // Scale icons from 50% to 100% between 50% and 100% of the progress
+ val iconScale =
+ if (revealOffset > 0)
+ ((-revealState.offset - revealOffset * 0.5f) / revealOffset)
+ .coerceIn(0.0f, 0.5f) + 0.5f
+ else 1f
+ Box(
+ modifier = Modifier.graphicsLayer {
+ alpha = iconAlpha
+ scaleX = iconScale
+ scaleY = iconScale
+ }
+ ) {
+ content()
+ }
+}
diff --git a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/Swipeable.kt b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/Swipeable.kt
index 9a6fda0..b605836 100644
--- a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/Swipeable.kt
+++ b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/Swipeable.kt
@@ -16,6 +16,7 @@
package androidx.wear.compose.material
+import androidx.annotation.FloatRange
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.AnimationSpec
import androidx.compose.animation.core.SpringSpec
@@ -442,7 +443,7 @@
class SwipeProgress<T>(
val from: T,
val to: T,
- /*@FloatRange(from = 0.0, to = 1.0)*/
+ @FloatRange(from = 0.0, to = 1.0)
val fraction: Float
) {
override fun equals(other: Any?): Boolean {
@@ -660,7 +661,7 @@
@Immutable
@ExperimentalWearMaterialApi
data class FractionalThreshold(
- /*@FloatRange(from = 0.0, to = 1.0)*/
+ @FloatRange(from = 0.0, to = 1.0)
private val fraction: Float
) : ThresholdConfig {
override fun Density.computeThreshold(fromValue: Float, toValue: Float): Float {
@@ -693,11 +694,11 @@
@Immutable
@ExperimentalWearMaterialApi
class ResistanceConfig(
- /*@FloatRange(from = 0.0, fromInclusive = false)*/
+ @FloatRange(from = 0.0, fromInclusive = false)
val basis: Float,
- /*@FloatRange(from = 0.0)*/
+ @FloatRange(from = 0.0)
val factorAtMin: Float = StandardResistanceFactor,
- /*@FloatRange(from = 0.0)*/
+ @FloatRange(from = 0.0)
val factorAtMax: Float = StandardResistanceFactor
) {
fun computeResistance(overflow: Float): Float {
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconToggleButtonScreenshotTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconToggleButtonScreenshotTest.kt
new file mode 100644
index 0000000..3903cba
--- /dev/null
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/IconToggleButtonScreenshotTest.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.material3
+
+import android.os.Build
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Star
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import androidx.test.filters.SdkSuppress
+import androidx.test.screenshot.AndroidXScreenshotTestRule
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TestName
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+class IconToggleButtonScreenshotTest {
+ @get:Rule
+ val rule = createComposeRule()
+
+ @get:Rule
+ val screenshotRule = AndroidXScreenshotTestRule(SCREENSHOT_GOLDEN_PATH)
+
+ @get:Rule
+ val testName = TestName()
+
+ @Test
+ fun iconToggleButtonEnabledAndChecked() = rule.verifyScreenshot(
+ methodName = testName.methodName,
+ screenshotRule = screenshotRule,
+ content = { sampleIconToggleButton() }
+ )
+
+ @Test
+ fun iconToggleButtonEnabledAndUnchecked() = rule.verifyScreenshot(
+ methodName = testName.methodName,
+ screenshotRule = screenshotRule,
+ content = { sampleIconToggleButton(checked = false) }
+ )
+
+ @Test
+ fun iconToggleButtonDisabledAndChecked() = rule.verifyScreenshot(
+ methodName = testName.methodName,
+ screenshotRule = screenshotRule,
+ content = { sampleIconToggleButton(enabled = false) }
+ )
+
+ @Test
+ fun iconToggleButtonDisabledAndUnchecked() = rule.verifyScreenshot(
+ methodName = testName.methodName,
+ screenshotRule = screenshotRule,
+ content = { sampleIconToggleButton(enabled = false, checked = false) }
+ )
+
+ @Composable
+ private fun sampleIconToggleButton(
+ enabled: Boolean = true,
+ checked: Boolean = true
+ ) {
+ IconToggleButton(
+ checked = checked,
+ onCheckedChange = {},
+ enabled = enabled,
+ modifier = Modifier.testTag(TEST_TAG)
+ ) {
+ Icon(
+ imageVector = Icons.Outlined.Star,
+ contentDescription = "Favourite"
+ )
+ }
+ }
+}
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/Material3Test.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/Material3Test.kt
index afa5a80..093455c 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/Material3Test.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/Material3Test.kt
@@ -30,6 +30,7 @@
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Add
import androidx.compose.runtime.Composable
+import androidx.compose.testutils.assertAgainstGolden
import androidx.compose.testutils.assertContainsColor
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -53,6 +54,7 @@
import androidx.compose.ui.unit.height
import androidx.compose.ui.unit.isUnspecified
import androidx.compose.ui.unit.toSize
+import androidx.test.screenshot.AndroidXScreenshotTestRule
import kotlin.math.abs
import org.junit.Assert
@@ -279,6 +281,27 @@
}
}
+@RequiresApi(Build.VERSION_CODES.O)
+internal fun ComposeContentTestRule.verifyScreenshot(
+ methodName: String,
+ screenshotRule: AndroidXScreenshotTestRule,
+ testTag: String = TEST_TAG,
+ content: @Composable () -> Unit
+) {
+ setContentWithTheme {
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .background(MaterialTheme.colorScheme.background)
+ ) {
+ content()
+ }
+ }
+
+ onNodeWithTag(testTag).captureToImage()
+ .assertAgainstGolden(screenshotRule, methodName)
+}
+
private fun ImageBitmap.histogram(): MutableMap<Color, Long> {
val pixels = this.toPixelMap()
val histogram = mutableMapOf<Color, Long>()
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextToggleButtonScreenshotTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextToggleButtonScreenshotTest.kt
new file mode 100644
index 0000000..71a30a6
--- /dev/null
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/TextToggleButtonScreenshotTest.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.compose.material3
+
+import android.os.Build
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import androidx.test.filters.SdkSuppress
+import androidx.test.screenshot.AndroidXScreenshotTestRule
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TestName
+import org.junit.runner.RunWith
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+class TextToggleButtonScreenshotTest {
+ @get:Rule
+ val rule = createComposeRule()
+
+ @get:Rule
+ val screenshotRule = AndroidXScreenshotTestRule(SCREENSHOT_GOLDEN_PATH)
+
+ @get:Rule
+ val testName = TestName()
+
+ @Test
+ fun textToggleButtonEnabledAndChecked() = rule.verifyScreenshot(
+ methodName = testName.methodName,
+ screenshotRule = screenshotRule,
+ content = { sampleTextToggleButton() }
+ )
+
+ @Test
+ fun textToggleButtonEnabledAndUnchecked() = rule.verifyScreenshot(
+ methodName = testName.methodName,
+ screenshotRule = screenshotRule,
+ content = { sampleTextToggleButton(checked = false) }
+ )
+
+ @Test
+ fun textToggleButtonDisabledAndChecked() = rule.verifyScreenshot(
+ methodName = testName.methodName,
+ screenshotRule = screenshotRule,
+ content = { sampleTextToggleButton(enabled = false) }
+ )
+
+ @Test
+ fun textToggleButtonDisabledAndUnchecked() = rule.verifyScreenshot(
+ methodName = testName.methodName,
+ screenshotRule = screenshotRule,
+ content = { sampleTextToggleButton(enabled = false, checked = false) }
+ )
+
+ @Composable
+ private fun sampleTextToggleButton(
+ enabled: Boolean = true,
+ checked: Boolean = true
+ ) {
+ TextToggleButton(
+ checked = checked,
+ onCheckedChange = {},
+ enabled = enabled,
+ modifier = Modifier.testTag(TEST_TAG)) {
+ Text(text = if (checked) "ON" else "OFF")
+ }
+ }
+}
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ContentAlpha.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ContentAlpha.kt
index b978a2b..a66faf1 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ContentAlpha.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/ContentAlpha.kt
@@ -15,6 +15,7 @@
*/
package androidx.wear.compose.material3
+import androidx.annotation.FloatRange
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ProvidableCompositionLocal
import androidx.compose.runtime.compositionLocalOf
@@ -83,9 +84,9 @@
*/
@Composable
private fun contentAlpha(
- /*@FloatRange(from = 0.0, to = 1.0)*/
+ @FloatRange(from = 0.0, to = 1.0)
highContrastAlpha: Float,
- /*@FloatRange(from = 0.0, to = 1.0)*/
+ @FloatRange(from = 0.0, to = 1.0)
lowContrastAlpha: Float
): Float {
val contentColor = LocalContentColor.current
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Typography.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Typography.kt
index 8c3aaa1..341fc8d 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Typography.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Typography.kt
@@ -15,13 +15,15 @@
*/
package androidx.wear.compose.material3
+import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.text.PlatformTextStyle
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
-import androidx.compose.ui.text.font.FontWeight
-import androidx.compose.ui.unit.sp
+import androidx.wear.compose.material3.tokens.TypographyKeyTokens
+import androidx.wear.compose.material3.tokens.TypographyTokens
/**
* Class holding typography definitions as defined by the Wear Material typography specification.
@@ -95,84 +97,19 @@
) {
public constructor (
defaultFontFamily: FontFamily = FontFamily.Default,
- displayLarge: TextStyle = DefaultTextStyle.copy(
- fontWeight = FontWeight.Normal,
- fontSize = 40.sp,
- lineHeight = 44.sp,
- letterSpacing = 0.sp
- ),
- displayMedium: TextStyle = DefaultTextStyle.copy(
- fontWeight = FontWeight.Normal,
- fontSize = 30.sp,
- lineHeight = 34.sp,
- letterSpacing = 0.sp
- ),
- displaySmall: TextStyle = DefaultTextStyle.copy(
- fontWeight = FontWeight.Medium,
- fontSize = 24.sp,
- lineHeight = 28.sp,
- letterSpacing = 0.sp,
- ),
- titleLarge: TextStyle = DefaultTextStyle.copy(
- fontWeight = FontWeight.Medium,
- fontSize = 20.sp,
- lineHeight = 22.sp,
- letterSpacing = 0.2.sp
- ),
- titleMedium: TextStyle = DefaultTextStyle.copy(
- fontWeight = FontWeight.Medium,
- fontSize = 16.sp,
- lineHeight = 18.sp,
- letterSpacing = 0.3.sp
- ),
- titleSmall: TextStyle = DefaultTextStyle.copy(
- fontWeight = FontWeight.Medium,
- fontSize = 14.sp,
- lineHeight = 16.sp,
- letterSpacing = 0.3.sp
- ),
- labelLarge: TextStyle = DefaultTextStyle.copy(
- fontWeight = FontWeight.Medium,
- fontSize = 20.sp,
- lineHeight = 22.sp,
- letterSpacing = 0.2.sp
- ),
- labelMedium: TextStyle = DefaultTextStyle.copy(
- fontWeight = FontWeight.Medium,
- fontSize = 15.sp,
- lineHeight = 16.sp,
- letterSpacing = 0.2.sp
- ),
- labelSmall: TextStyle = DefaultTextStyle.copy(
- fontWeight = FontWeight.Medium,
- fontSize = 13.sp,
- lineHeight = 14.sp,
- letterSpacing = 0.3.sp
- ),
- bodyLarge: TextStyle = DefaultTextStyle.copy(
- fontWeight = FontWeight.Normal,
- fontSize = 16.sp,
- lineHeight = 18.sp,
- letterSpacing = 0.3.sp,
- ),
- bodyMedium: TextStyle = DefaultTextStyle.copy(
- fontWeight = FontWeight.Normal,
- fontSize = 14.sp,
- lineHeight = 18.sp,
- letterSpacing = 0.2.sp
- ),
- bodySmall: TextStyle = DefaultTextStyle.copy(
- fontWeight = FontWeight.Medium,
- fontSize = 12.sp,
- lineHeight = 16.sp,
- letterSpacing = 0.4.sp
- ),
- bodyExtraSmall: TextStyle = DefaultTextStyle.copy(
- fontWeight = FontWeight.Normal,
- fontSize = 11.sp,
- lineHeight = 14.sp,
- letterSpacing = 0.4.sp
- )
+ displayLarge: TextStyle = TypographyTokens.DisplayLarge,
+ displayMedium: TextStyle = TypographyTokens.DisplayMedium,
+ displaySmall: TextStyle = TypographyTokens.DisplaySmall,
+ titleLarge: TextStyle = TypographyTokens.TitleLarge,
+ titleMedium: TextStyle = TypographyTokens.TitleMedium,
+ titleSmall: TextStyle = TypographyTokens.TitleSmall,
+ labelLarge: TextStyle = TypographyTokens.LabelLarge,
+ labelMedium: TextStyle = TypographyTokens.LabelMedium,
+ labelSmall: TextStyle = TypographyTokens.LabelSmall,
+ bodyLarge: TextStyle = TypographyTokens.BodyLarge,
+ bodyMedium: TextStyle = TypographyTokens.BodyMedium,
+ bodySmall: TextStyle = TypographyTokens.BodySmall,
+ bodyExtraSmall: TextStyle = TypographyTokens.BodyExtraSmall
) : this(
displayLarge = displayLarge.withDefaultFontFamily(defaultFontFamily),
displayMedium = displayMedium.withDefaultFontFamily(defaultFontFamily),
@@ -289,6 +226,36 @@
)
/**
+ * Helper function for typography tokens.
+ */
+internal fun Typography.fromToken(value: TypographyKeyTokens): TextStyle {
+ return when (value) {
+ TypographyKeyTokens.DisplayLarge -> displayLarge
+ TypographyKeyTokens.DisplayMedium -> displayMedium
+ TypographyKeyTokens.DisplaySmall -> displaySmall
+ TypographyKeyTokens.TitleLarge -> titleLarge
+ TypographyKeyTokens.TitleMedium -> titleMedium
+ TypographyKeyTokens.TitleSmall -> titleSmall
+ TypographyKeyTokens.LabelLarge -> labelLarge
+ TypographyKeyTokens.LabelMedium -> labelMedium
+ TypographyKeyTokens.LabelSmall -> labelSmall
+ TypographyKeyTokens.BodyLarge -> bodyLarge
+ TypographyKeyTokens.BodyMedium -> bodyMedium
+ TypographyKeyTokens.BodySmall -> bodySmall
+ TypographyKeyTokens.BodyExtraSmall -> bodyExtraSmall
+ }
+}
+
+/**
+ * Helper function to convert [TypographyKeyTokens] to [TextStyle].
+ */
+@Composable
+@ReadOnlyComposable
+internal fun TypographyKeyTokens.toTextStyle(): TextStyle {
+ return MaterialTheme.typography.fromToken(this)
+}
+
+/**
* This Ambient holds on to the current definition of typography for this application as described
* by the Wear Material spec. You can read the values in it when creating custom components that
* want to use Wear Material types, as well as override the values when you want to re-style a part
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TypefaceTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TypefaceTokens.kt
new file mode 100644
index 0000000..2eea3e2
--- /dev/null
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TypefaceTokens.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// VERSION: v0_8
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+package androidx.wear.compose.material3.tokens
+
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontWeight
+
+internal object TypefaceTokens {
+ val Brand = FontFamily.SansSerif
+ val MediumWeight = FontWeight.Medium
+ val Plain = FontFamily.SansSerif
+ val RegularWeight = FontWeight.Normal
+ val WeightBold = FontWeight.Bold
+}
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TypescaleTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TypescaleTokens.kt
new file mode 100644
index 0000000..858bbf7
--- /dev/null
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TypescaleTokens.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// VERSION: v0_9
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+package androidx.wear.compose.material3.tokens
+
+import androidx.compose.ui.unit.sp
+
+internal object TypeScaleTokens {
+ val BodyExtraSmallFont = TypefaceTokens.Brand
+ val BodyExtraSmallLineHeight = 14.0.sp
+ val BodyExtraSmallSize = 11.sp
+ val BodyExtraSmallTracking = 0.4.sp
+ val BodyExtraSmallWeight = TypefaceTokens.RegularWeight
+ val BodyExtraSmallWeightProminent = TypefaceTokens.WeightBold
+ val BodyLargeFont = TypefaceTokens.Brand
+ val BodyLargeLineHeight = 18.0.sp
+ val BodyLargeSize = 16.sp
+ val BodyLargeTracking = 0.3.sp
+ val BodyLargeWeight = TypefaceTokens.RegularWeight
+ val BodyLargeWeightProminent = TypefaceTokens.WeightBold
+ val BodyMediumFont = TypefaceTokens.Brand
+ val BodyMediumLineHeight = 18.0.sp
+ val BodyMediumSize = 14.sp
+ val BodyMediumTracking = 0.2.sp
+ val BodyMediumWeight = TypefaceTokens.RegularWeight
+ val BodyMediumWeightProminent = TypefaceTokens.WeightBold
+ val BodySmallFont = TypefaceTokens.Brand
+ val BodySmallLineHeight = 16.0.sp
+ val BodySmallSize = 12.sp
+ val BodySmallTracking = 0.4.sp
+ val BodySmallWeight = TypefaceTokens.MediumWeight
+ val BodySmallWeightProminent = TypefaceTokens.WeightBold
+ val DisplayLargeFont = TypefaceTokens.Brand
+ val DisplayLargeLineHeight = 44.0.sp
+ val DisplayLargeSize = 40.sp
+ val DisplayLargeTracking = 0.0.sp
+ val DisplayLargeWeight = TypefaceTokens.RegularWeight
+ val DisplayMediumFont = TypefaceTokens.Brand
+ val DisplayMediumLineHeight = 34.0.sp
+ val DisplayMediumSize = 30.sp
+ val DisplayMediumTracking = 0.0.sp
+ val DisplayMediumWeight = TypefaceTokens.RegularWeight
+ val DisplaySmallFont = TypefaceTokens.Brand
+ val DisplaySmallLineHeight = 28.0.sp
+ val DisplaySmallSize = 24.sp
+ val DisplaySmallTracking = 0.0.sp
+ val DisplaySmallWeight = TypefaceTokens.MediumWeight
+ val LabelLargeFont = TypefaceTokens.Brand
+ val LabelLargeLineHeight = 22.0.sp
+ val LabelLargeSize = 20.sp
+ val LabelLargeTracking = 0.2.sp
+ val LabelLargeWeight = TypefaceTokens.MediumWeight
+ val LabelMediumFont = TypefaceTokens.Brand
+ val LabelMediumLineHeight = 16.0.sp
+ val LabelMediumSize = 15.sp
+ val LabelMediumTracking = 0.2.sp
+ val LabelMediumWeight = TypefaceTokens.MediumWeight
+ val LabelSmallFont = TypefaceTokens.Brand
+ val LabelSmallLineHeight = 14.0.sp
+ val LabelSmallSize = 13.sp
+ val LabelSmallTracking = 0.3.sp
+ val LabelSmallWeight = TypefaceTokens.MediumWeight
+ val TitleLargeFont = TypefaceTokens.Brand
+ val TitleLargeLineHeight = 22.0.sp
+ val TitleLargeSize = 20.sp
+ val TitleLargeTracking = 0.2.sp
+ val TitleLargeWeight = TypefaceTokens.MediumWeight
+ val TitleMediumFont = TypefaceTokens.Brand
+ val TitleMediumLineHeight = 18.0.sp
+ val TitleMediumSize = 16.sp
+ val TitleMediumTracking = 0.3.sp
+ val TitleMediumWeight = TypefaceTokens.MediumWeight
+ val TitleSmallFont = TypefaceTokens.Brand
+ val TitleSmallLineHeight = 16.0.sp
+ val TitleSmallSize = 14.sp
+ val TitleSmallTracking = 0.3.sp
+ val TitleSmallWeight = TypefaceTokens.MediumWeight
+}
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TypographyKeyTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TypographyKeyTokens.kt
new file mode 100644
index 0000000..40869af
--- /dev/null
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TypographyKeyTokens.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// VERSION: v0_8
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+package androidx.wear.compose.material3.tokens
+
+internal enum class TypographyKeyTokens {
+ BodyExtraSmall,
+ BodyLarge,
+ BodyMedium,
+ BodySmall,
+ DisplayLarge,
+ DisplayMedium,
+ DisplaySmall,
+ LabelLarge,
+ LabelMedium,
+ LabelSmall,
+ TitleLarge,
+ TitleMedium,
+ TitleSmall,
+}
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TypographyTokens.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TypographyTokens.kt
new file mode 100644
index 0000000..60131a2
--- /dev/null
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/tokens/TypographyTokens.kt
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// VERSION: v0_8
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+package androidx.wear.compose.material3.tokens
+
+import androidx.wear.compose.material3.DefaultTextStyle
+
+internal object TypographyTokens {
+ val BodyExtraSmall =
+ DefaultTextStyle.copy(
+ fontFamily = TypeScaleTokens.BodyExtraSmallFont,
+ fontWeight = TypeScaleTokens.BodyExtraSmallWeight,
+ fontSize = TypeScaleTokens.BodyExtraSmallSize,
+ lineHeight = TypeScaleTokens.BodyExtraSmallLineHeight,
+ letterSpacing = TypeScaleTokens.BodyExtraSmallTracking,
+ )
+ val BodyLarge =
+ DefaultTextStyle.copy(
+ fontFamily = TypeScaleTokens.BodyLargeFont,
+ fontWeight = TypeScaleTokens.BodyLargeWeight,
+ fontSize = TypeScaleTokens.BodyLargeSize,
+ lineHeight = TypeScaleTokens.BodyLargeLineHeight,
+ letterSpacing = TypeScaleTokens.BodyLargeTracking,
+ )
+ val BodyMedium =
+ DefaultTextStyle.copy(
+ fontFamily = TypeScaleTokens.BodyMediumFont,
+ fontWeight = TypeScaleTokens.BodyMediumWeight,
+ fontSize = TypeScaleTokens.BodyMediumSize,
+ lineHeight = TypeScaleTokens.BodyMediumLineHeight,
+ letterSpacing = TypeScaleTokens.BodyMediumTracking,
+ )
+ val BodySmall =
+ DefaultTextStyle.copy(
+ fontFamily = TypeScaleTokens.BodySmallFont,
+ fontWeight = TypeScaleTokens.BodySmallWeight,
+ fontSize = TypeScaleTokens.BodySmallSize,
+ lineHeight = TypeScaleTokens.BodySmallLineHeight,
+ letterSpacing = TypeScaleTokens.BodySmallTracking,
+ )
+ val DisplayLarge =
+ DefaultTextStyle.copy(
+ fontFamily = TypeScaleTokens.DisplayLargeFont,
+ fontWeight = TypeScaleTokens.DisplayLargeWeight,
+ fontSize = TypeScaleTokens.DisplayLargeSize,
+ lineHeight = TypeScaleTokens.DisplayLargeLineHeight,
+ letterSpacing = TypeScaleTokens.DisplayLargeTracking,
+ )
+ val DisplayMedium =
+ DefaultTextStyle.copy(
+ fontFamily = TypeScaleTokens.DisplayMediumFont,
+ fontWeight = TypeScaleTokens.DisplayMediumWeight,
+ fontSize = TypeScaleTokens.DisplayMediumSize,
+ lineHeight = TypeScaleTokens.DisplayMediumLineHeight,
+ letterSpacing = TypeScaleTokens.DisplayMediumTracking,
+ )
+ val DisplaySmall =
+ DefaultTextStyle.copy(
+ fontFamily = TypeScaleTokens.DisplaySmallFont,
+ fontWeight = TypeScaleTokens.DisplaySmallWeight,
+ fontSize = TypeScaleTokens.DisplaySmallSize,
+ lineHeight = TypeScaleTokens.DisplaySmallLineHeight,
+ letterSpacing = TypeScaleTokens.DisplaySmallTracking,
+ )
+ val LabelLarge =
+ DefaultTextStyle.copy(
+ fontFamily = TypeScaleTokens.LabelLargeFont,
+ fontWeight = TypeScaleTokens.LabelLargeWeight,
+ fontSize = TypeScaleTokens.LabelLargeSize,
+ lineHeight = TypeScaleTokens.LabelLargeLineHeight,
+ letterSpacing = TypeScaleTokens.LabelLargeTracking,
+ )
+ val LabelMedium =
+ DefaultTextStyle.copy(
+ fontFamily = TypeScaleTokens.LabelMediumFont,
+ fontWeight = TypeScaleTokens.LabelMediumWeight,
+ fontSize = TypeScaleTokens.LabelMediumSize,
+ lineHeight = TypeScaleTokens.LabelMediumLineHeight,
+ letterSpacing = TypeScaleTokens.LabelMediumTracking,
+ )
+ val LabelSmall =
+ DefaultTextStyle.copy(
+ fontFamily = TypeScaleTokens.LabelSmallFont,
+ fontWeight = TypeScaleTokens.LabelSmallWeight,
+ fontSize = TypeScaleTokens.LabelSmallSize,
+ lineHeight = TypeScaleTokens.LabelSmallLineHeight,
+ letterSpacing = TypeScaleTokens.LabelSmallTracking,
+ )
+ val TitleLarge =
+ DefaultTextStyle.copy(
+ fontFamily = TypeScaleTokens.TitleLargeFont,
+ fontWeight = TypeScaleTokens.TitleLargeWeight,
+ fontSize = TypeScaleTokens.TitleLargeSize,
+ lineHeight = TypeScaleTokens.TitleLargeLineHeight,
+ letterSpacing = TypeScaleTokens.TitleLargeTracking,
+ )
+ val TitleMedium =
+ DefaultTextStyle.copy(
+ fontFamily = TypeScaleTokens.TitleMediumFont,
+ fontWeight = TypeScaleTokens.TitleMediumWeight,
+ fontSize = TypeScaleTokens.TitleMediumSize,
+ lineHeight = TypeScaleTokens.TitleMediumLineHeight,
+ letterSpacing = TypeScaleTokens.TitleMediumTracking,
+ )
+ val TitleSmall =
+ DefaultTextStyle.copy(
+ fontFamily = TypeScaleTokens.TitleSmallFont,
+ fontWeight = TypeScaleTokens.TitleSmallWeight,
+ fontSize = TypeScaleTokens.TitleSmallSize,
+ lineHeight = TypeScaleTokens.TitleSmallLineHeight,
+ letterSpacing = TypeScaleTokens.TitleSmallTracking,
+ )
+}
diff --git a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/SwipeToRevealDemo.kt b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/SwipeToRevealDemo.kt
index 6c5270e..58777cc 100644
--- a/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/SwipeToRevealDemo.kt
+++ b/wear/compose/integration-tests/demos/src/main/java/androidx/wear/compose/integration/demos/SwipeToRevealDemo.kt
@@ -16,57 +16,39 @@
package androidx.wear.compose.integration.demos
-import androidx.compose.foundation.background
-import androidx.compose.foundation.clickable
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.shape.CircleShape
-import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Email
-import androidx.compose.material.icons.outlined.Delete
-import androidx.compose.material.icons.outlined.MoreVert
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.rememberCoroutineScope
-import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.Shape
-import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.wear.compose.foundation.ExpandableState
import androidx.wear.compose.foundation.ExperimentalWearFoundationApi
-import androidx.wear.compose.foundation.RevealScope
-import androidx.wear.compose.foundation.RevealState
import androidx.wear.compose.foundation.RevealValue
-import androidx.wear.compose.foundation.SwipeToReveal
import androidx.wear.compose.foundation.createAnchors
import androidx.wear.compose.foundation.expandableItem
-import androidx.wear.compose.foundation.fractionalPositionalThreshold
import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
import androidx.wear.compose.foundation.rememberExpandableState
import androidx.wear.compose.foundation.rememberRevealState
import androidx.wear.compose.material.AppCard
import androidx.wear.compose.material.Chip
import androidx.wear.compose.material.ChipDefaults
+import androidx.wear.compose.material.ExperimentalWearMaterialApi
import androidx.wear.compose.material.Icon
-import androidx.wear.compose.material.MaterialTheme
+import androidx.wear.compose.material.SwipeToRevealCard
+import androidx.wear.compose.material.SwipeToRevealChip
+import androidx.wear.compose.material.SwipeToRevealDefaults
import androidx.wear.compose.material.Text
-import kotlin.math.abs
-import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
-import kotlinx.coroutines.launch
@Composable
fun SwipeToRevealChips() {
@@ -139,12 +121,15 @@
SwipeToRevealSingleAction()
}
+/**
+ * Swipe to reveal in RTL. This is should be identical to LTR.
+ */
@Composable
fun SwipeToRevealInRtl() {
SwipeToRevealSingleAction(LayoutDirection.Rtl)
}
-@OptIn(ExperimentalWearFoundationApi::class)
+@OptIn(ExperimentalWearFoundationApi::class, ExperimentalWearMaterialApi::class)
@Composable
private fun SwipeToRevealChipExpandable(
expandableState: ExpandableState
@@ -156,34 +141,37 @@
expandableState.expanded = false
}
}
- Box(
- contentAlignment = Alignment.Center,
- modifier = Modifier.size(width = 200.dp, height = 52.dp)
+ SwipeToRevealChip(
+ revealState = state,
+ action = SwipeToRevealDefaults.action(
+ icon = { Icon(SwipeToRevealDefaults.Delete, contentDescription = "Delete") },
+ label = { Text(text = "Delete") },
+ ),
+ additionalAction = SwipeToRevealDefaults.additionalAction(
+ icon = { Icon(SwipeToRevealDefaults.MoreOptions, contentDescription = "More Options") },
+ ),
+ undoAction = SwipeToRevealDefaults.undoAction(
+ label = { Text(text = "Undo") },
+ ),
) {
- SwipeToRevealWithDefaultButtons(
- shape = CircleShape,
- state = state,
- ) {
- Chip(
- onClick = { /*TODO*/ },
- colors = ChipDefaults.secondaryChipColors(),
- modifier = Modifier.fillMaxWidth(),
- label = {
- Text("S2R Chip with defaults")
- }
- )
- }
+ Chip(
+ onClick = { /*TODO*/ },
+ colors = ChipDefaults.secondaryChipColors(),
+ modifier = Modifier.fillMaxWidth(),
+ label = {
+ Text("S2R Chip with defaults")
+ }
+ )
}
}
-@OptIn(ExperimentalWearFoundationApi::class)
+@OptIn(ExperimentalWearFoundationApi::class, ExperimentalWearMaterialApi::class)
@Composable
private fun SwipeToRevealCardExpandable(
expandableState: ExpandableState,
from: String,
email: String
) {
-
val state = rememberRevealState()
LaunchedEffect(state.currentValue) {
if (state.currentValue == RevealValue.Revealed) {
@@ -191,13 +179,22 @@
expandableState.expanded = false
}
}
- SwipeToRevealWithDefaultButtons(
- shape = RoundedCornerShape(30.dp),
- state = state
+ SwipeToRevealCard(
+ revealState = state,
+ action = SwipeToRevealDefaults.action(
+ icon = { Icon(SwipeToRevealDefaults.Delete, contentDescription = "Delete") },
+ label = { Text(text = "Delete") },
+ ),
+ additionalAction = SwipeToRevealDefaults.additionalAction(
+ icon = { Icon(SwipeToRevealDefaults.MoreOptions, contentDescription = "More Options") },
+ ),
+ undoAction = SwipeToRevealDefaults.undoAction(
+ label = { Text(text = "Undo") },
+ ),
) {
AppCard(
onClick = {},
- modifier = Modifier.size(width = 200.dp, height = 100.dp),
+ modifier = Modifier.width(width = 200.dp),
appName = { Text("Gmail") },
appImage = {
Icon(
@@ -206,7 +203,7 @@
)
},
time = { Text("now") },
- title = { Text("From: $from") }
+ title = { Text("From: $from", maxLines = 1, overflow = TextOverflow.Ellipsis) }
) {
Text(
text = email,
@@ -217,52 +214,7 @@
}
}
-@OptIn(ExperimentalWearFoundationApi::class)
-@Composable
-private fun SwipeToRevealWithDefaultButtons(
- state: RevealState = rememberRevealState(),
- shape: Shape = CircleShape,
- content: @Composable () -> Unit
-) {
- val coroutineScope = rememberCoroutineScope()
- SwipeToReveal(
- action = {
- SwipeToRevealAction(
- color = MaterialTheme.colors.error,
- icon = Icons.Outlined.Delete,
- text = "Clear",
- contentDescription = "Delete",
- onClick = { state.animateTo(RevealValue.Revealed) },
- shape = shape,
- coroutineScope = coroutineScope,
- state = state
- )
- },
- additionalAction = {
- SwipeToRevealAction(
- color = MaterialTheme.colors.onSurfaceVariant,
- icon = Icons.Outlined.MoreVert,
- onClick = { state.animateTo(RevealValue.Covered) },
- shape = shape,
- coroutineScope = coroutineScope,
- state = state
- )
- },
- undoAction = {
- Chip(
- modifier = Modifier.fillMaxWidth(),
- onClick = { coroutineScope.launch { state.animateTo(RevealValue.Covered) } },
- colors = ChipDefaults.secondaryChipColors(),
- border = ChipDefaults.outlinedChipBorder(),
- label = { Text(text = "Undo") }
- )
- },
- state = state,
- content = content,
- )
-}
-
-@OptIn(ExperimentalWearFoundationApi::class)
+@OptIn(ExperimentalWearFoundationApi::class, ExperimentalWearMaterialApi::class)
@Composable
private fun SwipeToRevealSingleAction(
layoutDirection: LayoutDirection = LayoutDirection.Ltr
@@ -281,26 +233,24 @@
state = expandableState[curr]
) { expanded ->
val state = rememberRevealState(
- anchors = createAnchors(revealingAnchor = 0.5f),
- positionalThreshold = fractionalPositionalThreshold(0.5f)
+ // Setting anchor to 0.4 since there is only one action.
+ anchors = createAnchors(revealingAnchor = 0.4f),
)
if (expanded) {
CompositionLocalProvider(
LocalLayoutDirection provides layoutDirection
) {
- SwipeToReveal(
- action = {
- SwipeToRevealAction(
- color = MaterialTheme.colors.error,
- icon = Icons.Outlined.Delete,
- text = "Clear",
- contentDescription = "Delete",
- onClick = { state.animateTo(RevealValue.Revealed) },
- shape = CircleShape,
- state = state
- )
- },
- state = state
+ SwipeToRevealChip(
+ revealState = state,
+ action = SwipeToRevealDefaults.action(
+ icon = {
+ Icon(
+ SwipeToRevealDefaults.Delete,
+ contentDescription = "Delete"
+ )
+ },
+ label = { Text(text = "Delete") },
+ ),
) {
Chip(
onClick = { /*TODO*/ },
@@ -323,33 +273,3 @@
}
}
}
-
-@OptIn(ExperimentalWearFoundationApi::class)
-@Composable
-private fun RevealScope.SwipeToRevealAction(
- color: Color,
- icon: ImageVector,
- text: String? = null,
- contentDescription: String? = null,
- onClick: suspend () -> Unit = {},
- shape: Shape = RoundedCornerShape(15.dp),
- state: RevealState = rememberRevealState(),
- coroutineScope: CoroutineScope = rememberCoroutineScope(),
-) {
- Row(
- modifier = Modifier
- .clickable {
- coroutineScope.launch { onClick() }
- }
- .background(color, shape)
- .fillMaxSize(),
- horizontalArrangement = Arrangement.Center,
- verticalAlignment = Alignment.CenterVertically
- ) {
- Icon(imageVector = icon, contentDescription = contentDescription, tint = Color.DarkGray)
- if (abs(state.offset) > revealOffset && text != null) {
- Spacer(Modifier.size(5.dp))
- Text(text = text)
- }
- }
-}
diff --git a/wear/protolayout/protolayout-renderer/build.gradle b/wear/protolayout/protolayout-renderer/build.gradle
index a29d50f..b483e2a 100644
--- a/wear/protolayout/protolayout-renderer/build.gradle
+++ b/wear/protolayout/protolayout-renderer/build.gradle
@@ -33,7 +33,7 @@
api(project(":wear:protolayout:protolayout-expression-pipeline"))
implementation "androidx.concurrent:concurrent-futures:1.1.0"
implementation("androidx.core:core:1.7.0")
- implementation("androidx.wear:wear:1.3.0-rc01")
+ implementation("androidx.wear:wear:1.3.0")
testImplementation(libs.mockitoCore4)
testImplementation(libs.testExtJunit)
diff --git a/wear/tiles/tiles-renderer/src/test/java/androidx/wear/tiles/manager/UpdateSchedulerTest.java b/wear/tiles/tiles-renderer/src/test/java/androidx/wear/tiles/manager/UpdateSchedulerTest.java
index c96a0bc..b9696c8 100644
--- a/wear/tiles/tiles-renderer/src/test/java/androidx/wear/tiles/manager/UpdateSchedulerTest.java
+++ b/wear/tiles/tiles-renderer/src/test/java/androidx/wear/tiles/manager/UpdateSchedulerTest.java
@@ -292,6 +292,7 @@
expect.that(mFired).isEmpty();
}
+ @SuppressWarnings("deprecation") // ScheduledAlarm usage, see b/284981234
private void advanceToTime(Long targetTime) {
while (mShadowAlarmManager.peekNextScheduledAlarm() != null
&& mShadowAlarmManager.peekNextScheduledAlarm().triggerAtTime <= targetTime) {
diff --git a/wear/tiles/tiles-renderer/src/test/java/androidx/wear/tiles/timeline/TilesTimelineManagerTest.java b/wear/tiles/tiles-renderer/src/test/java/androidx/wear/tiles/timeline/TilesTimelineManagerTest.java
index cd97c3c..ba44a1f 100644
--- a/wear/tiles/tiles-renderer/src/test/java/androidx/wear/tiles/timeline/TilesTimelineManagerTest.java
+++ b/wear/tiles/tiles-renderer/src/test/java/androidx/wear/tiles/timeline/TilesTimelineManagerTest.java
@@ -365,6 +365,7 @@
.build();
}
+ @SuppressWarnings("deprecation") // ScheduledAlarm usage, see b/284981234
private void seekToTime(long timeMillis) {
ShadowAlarmManager shadowAlarmManager = shadowOf(mAlarmManager);
diff --git a/wear/watchface/watchface-complications-data-source-samples/build.gradle b/wear/watchface/watchface-complications-data-source-samples/build.gradle
index 5bc5294..bccb6ae 100644
--- a/wear/watchface/watchface-complications-data-source-samples/build.gradle
+++ b/wear/watchface/watchface-complications-data-source-samples/build.gradle
@@ -17,6 +17,7 @@
plugins {
id("AndroidXPlugin")
id("com.android.application")
+ id("kotlin-android")
}
dependencies {