Merge changes Iea4f404c,Icffdf98b into androidx-main
* changes:
@samples removing frameworks/support/ prefix
Removing unused frameworks/support/samples symlink
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/stub/StubCreators.java b/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/stub/StubCreators.java
index 6591e3a..97887bf 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/stub/StubCreators.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/stub/StubCreators.java
@@ -82,4 +82,8 @@
/** Stub creator for {@link androidx.appsearch.app.VisibilityDocument}. */
public static class VisibilityDocumentCreator extends AbstractCreator {
}
+
+ /** Stub creator for {@link androidx.appsearch.stats.SchemaMigrationStats}. */
+ public static class SchemaMigrationStatsCreator extends AbstractCreator {
+ }
}
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/stats/SchemaMigrationStats.java b/appsearch/appsearch/src/main/java/androidx/appsearch/stats/SchemaMigrationStats.java
index 5c3a226..0a24293 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/stats/SchemaMigrationStats.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/stats/SchemaMigrationStats.java
@@ -16,7 +16,7 @@
package androidx.appsearch.stats;
-import android.os.Bundle;
+import android.os.Parcel;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
@@ -24,11 +24,13 @@
import androidx.appsearch.annotation.CanIgnoreReturnValue;
import androidx.appsearch.app.AppSearchResult;
import androidx.appsearch.app.SetSchemaRequest;
-import androidx.appsearch.util.BundleUtil;
-import androidx.core.util.Preconditions;
+import androidx.appsearch.safeparcel.AbstractSafeParcelable;
+import androidx.appsearch.safeparcel.SafeParcelable;
+import androidx.appsearch.safeparcel.stub.StubCreators.SchemaMigrationStatsCreator;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
/**
* Class holds detailed stats for Schema migration.
@@ -36,7 +38,10 @@
* @exportToFramework:hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public final class SchemaMigrationStats {
[email protected](creator = "SchemaMigrationStatsCreator")
+public final class SchemaMigrationStats extends AbstractSafeParcelable {
+ @NonNull public static final SchemaMigrationStatsCreator CREATOR =
+ new SchemaMigrationStatsCreator();
// Indicate the how a SetSchema call relative to SchemaMigration case.
@IntDef(
@@ -55,82 +60,114 @@
/** This is the second SetSchema call in Migration cases to apply new schema changes */
public static final int SECOND_CALL_APPLY_NEW_SCHEMA = 2;
- private static final String PACKAGE_NAME_FIELD = "packageName";
- private static final String DATABASE_FIELD = "database";
- private static final String STATUS_CODE_FIELD = "StatusCode";
- private static final String EXECUTOR_ACQUISITION_MILLIS_FIELD =
- "ExecutorAcquisitionLatencyMillis";
- private static final String TOTAL_LATENCY_MILLIS_FIELD = "totalLatencyMillis";
- private static final String GET_SCHEMA_LATENCY_MILLIS_FIELD = "getSchemaLatencyMillis";
- private static final String QUERY_AND_TRANSFORM_LATENCY_MILLIS_FIELD =
- "queryAndTransformLatencyMillis";
- private static final String FIRST_SET_SCHEMA_LATENCY_MILLIS_FIELD =
- "firstSetSchemaLatencyMillis";
- private static final String IS_FIRST_SET_SCHEMA_SUCCESS_FIELD = "isFirstSetSchemaSuccess";
- private static final String SECOND_SET_SCHEMA_LATENCY_MILLIS_FIELD =
- "secondSetSchemaLatencyMillis";
- private static final String SAVE_DOCUMENT_LATENCY_MILLIS_FIELD = "saveDocumentLatencyMillis";
- private static final String TOTAL_NEED_MIGRATED_DOCUMENT_COUNT_FIELD =
- "totalNeedMigratedDocumentCount";
- private static final String MIGRATION_FAILURE_COUNT_FIELD = "migrationFailureCount";
- private static final String TOTAL_SUCCESS_MIGRATED_DOCUMENT_COUNT_FIELD =
- "totalSuccessMigratedDocumentCount";
-
- /**
- * Contains all {@link SchemaMigrationStats} information in a packaged format.
- *
- * <p>Keys are the {@code *_FIELD} constants in this class.
- */
+ @Field(id = 1, getter = "getPackageName")
@NonNull
- final Bundle mBundle;
+ private final String mPackageName;
- /** Build a {@link SchemaMigrationStats} from the given bundle. */
- public SchemaMigrationStats(@NonNull Bundle bundle) {
- mBundle = Preconditions.checkNotNull(bundle);
- }
-
- /**
- * Returns the {@link Bundle} populated by this builder.
- *
- * @exportToFramework:hide
- */
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @Field(id = 2, getter = "getDatabase")
@NonNull
- public Bundle getBundle() {
- return mBundle;
+ private final String mDatabase;
+
+ @Field(id = 3, getter = "getStatusCode")
+ private final int mStatusCode;
+
+ @Field(id = 4, getter = "getExecutorAcquisitionLatencyMillis")
+ private final int mExecutorAcquisitionLatencyMillis;
+
+ @Field(id = 5, getter = "getTotalLatencyMillis")
+ private final int mTotalLatencyMillis;
+
+ @Field(id = 6, getter = "getGetSchemaLatencyMillis")
+ private final int mGetSchemaLatencyMillis;
+
+ @Field(id = 7, getter = "getQueryAndTransformLatencyMillis")
+ private final int mQueryAndTransformLatencyMillis;
+
+ @Field(id = 8, getter = "getFirstSetSchemaLatencyMillis")
+ private final int mFirstSetSchemaLatencyMillis;
+
+ @Field(id = 9, getter = "isFirstSetSchemaSuccess")
+ private final boolean mIsFirstSetSchemaSuccess;
+
+ @Field(id = 10, getter = "getSecondSetSchemaLatencyMillis")
+ private final int mSecondSetSchemaLatencyMillis;
+
+ @Field(id = 11, getter = "getSaveDocumentLatencyMillis")
+ private final int mSaveDocumentLatencyMillis;
+
+ @Field(id = 12, getter = "getTotalNeedMigratedDocumentCount")
+ private final int mTotalNeedMigratedDocumentCount;
+
+ @Field(id = 13, getter = "getMigrationFailureCount")
+ private final int mMigrationFailureCount;
+
+ @Field(id = 14, getter = "getTotalSuccessMigratedDocumentCount")
+ private final int mTotalSuccessMigratedDocumentCount;
+
+ /** Build a {@link SchemaMigrationStats} from the given parameters. */
+ @Constructor
+ public SchemaMigrationStats(
+ @Param(id = 1) @NonNull String packageName,
+ @Param(id = 2) @NonNull String database,
+ @Param(id = 3) int statusCode,
+ @Param(id = 4) int executorAcquisitionLatencyMillis,
+ @Param(id = 5) int totalLatencyMillis,
+ @Param(id = 6) int getSchemaLatencyMillis,
+ @Param(id = 7) int queryAndTransformLatencyMillis,
+ @Param(id = 8) int firstSetSchemaLatencyMillis,
+ @Param(id = 9) boolean isFirstSetSchemaSuccess,
+ @Param(id = 10) int secondSetSchemaLatencyMillis,
+ @Param(id = 11) int saveDocumentLatencyMillis,
+ @Param(id = 12) int totalNeedMigratedDocumentCount,
+ @Param(id = 13) int migrationFailureCount,
+ @Param(id = 14) int totalSuccessMigratedDocumentCount) {
+ mPackageName = packageName;
+ mDatabase = database;
+ mStatusCode = statusCode;
+ mExecutorAcquisitionLatencyMillis = executorAcquisitionLatencyMillis;
+ mTotalLatencyMillis = totalLatencyMillis;
+ mGetSchemaLatencyMillis = getSchemaLatencyMillis;
+ mQueryAndTransformLatencyMillis = queryAndTransformLatencyMillis;
+ mFirstSetSchemaLatencyMillis = firstSetSchemaLatencyMillis;
+ mIsFirstSetSchemaSuccess = isFirstSetSchemaSuccess;
+ mSecondSetSchemaLatencyMillis = secondSetSchemaLatencyMillis;
+ mSaveDocumentLatencyMillis = saveDocumentLatencyMillis;
+ mTotalNeedMigratedDocumentCount = totalNeedMigratedDocumentCount;
+ mMigrationFailureCount = migrationFailureCount;
+ mTotalSuccessMigratedDocumentCount = totalSuccessMigratedDocumentCount;
}
/** Returns calling package name. */
@NonNull
public String getPackageName() {
- return mBundle.getString(PACKAGE_NAME_FIELD);
+ return mPackageName;
}
/** Returns calling database name. */
@NonNull
public String getDatabase() {
- return mBundle.getString(DATABASE_FIELD);
+ return mDatabase;
}
/** Returns status of the schema migration action. */
@AppSearchResult.ResultCode
public int getStatusCode() {
- return mBundle.getInt(STATUS_CODE_FIELD);
+ return mStatusCode;
}
/** Gets the latency for waiting the executor. */
public int getExecutorAcquisitionLatencyMillis() {
- return mBundle.getInt(EXECUTOR_ACQUISITION_MILLIS_FIELD);
+ return mExecutorAcquisitionLatencyMillis;
}
/** Gets total latency for the schema migration action in milliseconds. */
public int getTotalLatencyMillis() {
- return mBundle.getInt(TOTAL_LATENCY_MILLIS_FIELD);
+ return mTotalLatencyMillis;
}
/** Returns GetSchema latency in milliseconds. */
public int getGetSchemaLatencyMillis() {
- return mBundle.getInt(GET_SCHEMA_LATENCY_MILLIS_FIELD);
+ return mGetSchemaLatencyMillis;
}
/**
@@ -138,7 +175,7 @@
* transforming documents to new version in milliseconds.
*/
public int getQueryAndTransformLatencyMillis() {
- return mBundle.getInt(QUERY_AND_TRANSFORM_LATENCY_MILLIS_FIELD);
+ return mQueryAndTransformLatencyMillis;
}
/**
@@ -150,12 +187,12 @@
* <p>Please see {@link SetSchemaRequest} for what is "incompatible".
*/
public int getFirstSetSchemaLatencyMillis() {
- return mBundle.getInt(FIRST_SET_SCHEMA_LATENCY_MILLIS_FIELD);
+ return mFirstSetSchemaLatencyMillis;
}
/** Returns whether the first SetSchema action success. */
public boolean isFirstSetSchemaSuccess() {
- return mBundle.getBoolean(IS_FIRST_SET_SCHEMA_SUCCESS_FIELD);
+ return mIsFirstSetSchemaSuccess;
}
/**
@@ -166,39 +203,56 @@
* be set to Icing by this action.
*/
public int getSecondSetSchemaLatencyMillis() {
- return mBundle.getInt(SECOND_SET_SCHEMA_LATENCY_MILLIS_FIELD);
+ return mSecondSetSchemaLatencyMillis;
}
/** Returns latency of putting migrated document to Icing lib in milliseconds. */
public int getSaveDocumentLatencyMillis() {
- return mBundle.getInt(SAVE_DOCUMENT_LATENCY_MILLIS_FIELD);
+ return mSaveDocumentLatencyMillis;
}
/** Returns number of document that need to be migrated to another version. */
public int getTotalNeedMigratedDocumentCount() {
- return mBundle.getInt(TOTAL_NEED_MIGRATED_DOCUMENT_COUNT_FIELD);
+ return mTotalNeedMigratedDocumentCount;
}
/** Returns number of {@link androidx.appsearch.app.SetSchemaResponse.MigrationFailure}. */
public int getMigrationFailureCount() {
- return mBundle.getInt(MIGRATION_FAILURE_COUNT_FIELD);
+ return mMigrationFailureCount;
}
/** Returns number of successfully migrated and saved in Icing. */
public int getTotalSuccessMigratedDocumentCount() {
- return mBundle.getInt(TOTAL_SUCCESS_MIGRATED_DOCUMENT_COUNT_FIELD);
+ return mTotalSuccessMigratedDocumentCount;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ SchemaMigrationStatsCreator.writeToParcel(this, dest, flags);
}
/** Builder for {@link SchemaMigrationStats}. */
public static class Builder {
- private final Bundle mBundle;
+ String mPackageName;
+ String mDatabase;
+ int mStatusCode;
+ int mExecutorAcquisitionLatencyMillis;
+ int mTotalLatencyMillis;
+ int mGetSchemaLatencyMillis;
+ int mQueryAndTransformLatencyMillis;
+ int mFirstSetSchemaLatencyMillis;
+ boolean mIsFirstSetSchemaSuccess;
+ int mSecondSetSchemaLatencyMillis;
+ int mSaveDocumentLatencyMillis;
+ int mTotalNeedMigratedDocumentCount;
+ int mMigrationFailureCount;
+ int mTotalSuccessMigratedDocumentCount;
/** Creates a {@link SchemaMigrationStats.Builder}. */
public Builder(@NonNull String packageName, @NonNull String database) {
- mBundle = new Bundle();
- mBundle.putString(PACKAGE_NAME_FIELD, packageName);
- mBundle.putString(DATABASE_FIELD, database);
+ mPackageName = Objects.requireNonNull(packageName);
+ mDatabase = Objects.requireNonNull(database);
}
/**
@@ -208,23 +262,29 @@
* SchemaMigrationStats.
*/
public Builder(@NonNull SchemaMigrationStats stats) {
- mBundle = BundleUtil.deepCopy(stats.mBundle);
- }
+ Objects.requireNonNull(stats);
- /**
- * Creates a new {@link SchemaMigrationStats.Builder} from the given Bundle
- *
- * <p>The bundle is NOT copied.
- */
- public Builder(@NonNull Bundle bundle) {
- mBundle = Preconditions.checkNotNull(bundle);
+ mPackageName = stats.mPackageName;
+ mDatabase = stats.mDatabase;
+ mStatusCode = stats.mStatusCode;
+ mExecutorAcquisitionLatencyMillis = stats.mExecutorAcquisitionLatencyMillis;
+ mTotalLatencyMillis = stats.mTotalLatencyMillis;
+ mGetSchemaLatencyMillis = stats.mGetSchemaLatencyMillis;
+ mQueryAndTransformLatencyMillis = stats.mQueryAndTransformLatencyMillis;
+ mFirstSetSchemaLatencyMillis = stats.mFirstSetSchemaLatencyMillis;
+ mIsFirstSetSchemaSuccess = stats.mIsFirstSetSchemaSuccess;
+ mSecondSetSchemaLatencyMillis = stats.mSecondSetSchemaLatencyMillis;
+ mSaveDocumentLatencyMillis = stats.mSaveDocumentLatencyMillis;
+ mTotalNeedMigratedDocumentCount = stats.mTotalNeedMigratedDocumentCount;
+ mMigrationFailureCount = stats.mMigrationFailureCount;
+ mTotalSuccessMigratedDocumentCount = stats.mTotalSuccessMigratedDocumentCount;
}
/** Sets status code for the schema migration action. */
@CanIgnoreReturnValue
@NonNull
public Builder setStatusCode(@AppSearchResult.ResultCode int statusCode) {
- mBundle.putInt(STATUS_CODE_FIELD, statusCode);
+ mStatusCode = statusCode;
return this;
}
@@ -232,7 +292,7 @@
@CanIgnoreReturnValue
@NonNull
public Builder setExecutorAcquisitionLatencyMillis(int executorAcquisitionLatencyMillis) {
- mBundle.putInt(EXECUTOR_ACQUISITION_MILLIS_FIELD, executorAcquisitionLatencyMillis);
+ mExecutorAcquisitionLatencyMillis = executorAcquisitionLatencyMillis;
return this;
}
@@ -241,7 +301,7 @@
@CanIgnoreReturnValue
@NonNull
public Builder setTotalLatencyMillis(int totalLatencyMillis) {
- mBundle.putInt(TOTAL_LATENCY_MILLIS_FIELD, totalLatencyMillis);
+ mTotalLatencyMillis = totalLatencyMillis;
return this;
}
@@ -249,7 +309,7 @@
@CanIgnoreReturnValue
@NonNull
public Builder setGetSchemaLatencyMillis(int getSchemaLatencyMillis) {
- mBundle.putInt(GET_SCHEMA_LATENCY_MILLIS_FIELD, getSchemaLatencyMillis);
+ mGetSchemaLatencyMillis = getSchemaLatencyMillis;
return this;
}
@@ -261,8 +321,7 @@
@NonNull
public Builder setQueryAndTransformLatencyMillis(
int queryAndTransformLatencyMillis) {
- mBundle.putInt(QUERY_AND_TRANSFORM_LATENCY_MILLIS_FIELD,
- queryAndTransformLatencyMillis);
+ mQueryAndTransformLatencyMillis = queryAndTransformLatencyMillis;
return this;
}
@@ -271,7 +330,7 @@
@NonNull
public Builder setFirstSetSchemaLatencyMillis(
int firstSetSchemaLatencyMillis) {
- mBundle.putInt(FIRST_SET_SCHEMA_LATENCY_MILLIS_FIELD, firstSetSchemaLatencyMillis);
+ mFirstSetSchemaLatencyMillis = firstSetSchemaLatencyMillis;
return this;
}
@@ -279,7 +338,7 @@
@CanIgnoreReturnValue
@NonNull
public Builder setIsFirstSetSchemaSuccess(boolean isFirstSetSchemaSuccess) {
- mBundle.putBoolean(IS_FIRST_SET_SCHEMA_SUCCESS_FIELD, isFirstSetSchemaSuccess);
+ mIsFirstSetSchemaSuccess = isFirstSetSchemaSuccess;
return this;
}
@@ -288,7 +347,7 @@
@NonNull
public Builder setSecondSetSchemaLatencyMillis(
int secondSetSchemaLatencyMillis) {
- mBundle.putInt(SECOND_SET_SCHEMA_LATENCY_MILLIS_FIELD, secondSetSchemaLatencyMillis);
+ mSecondSetSchemaLatencyMillis = secondSetSchemaLatencyMillis;
return this;
}
@@ -297,7 +356,7 @@
@NonNull
public Builder setSaveDocumentLatencyMillis(
int saveDocumentLatencyMillis) {
- mBundle.putInt(SAVE_DOCUMENT_LATENCY_MILLIS_FIELD, saveDocumentLatencyMillis);
+ mSaveDocumentLatencyMillis = saveDocumentLatencyMillis;
return this;
}
@@ -305,7 +364,7 @@
@CanIgnoreReturnValue
@NonNull
public Builder setTotalNeedMigratedDocumentCount(int migratedDocumentCount) {
- mBundle.putInt(TOTAL_NEED_MIGRATED_DOCUMENT_COUNT_FIELD, migratedDocumentCount);
+ mTotalNeedMigratedDocumentCount = migratedDocumentCount;
return this;
}
@@ -314,8 +373,7 @@
@NonNull
public Builder setTotalSuccessMigratedDocumentCount(
int totalSuccessMigratedDocumentCount) {
- mBundle.putInt(TOTAL_SUCCESS_MIGRATED_DOCUMENT_COUNT_FIELD,
- totalSuccessMigratedDocumentCount);
+ mTotalSuccessMigratedDocumentCount = totalSuccessMigratedDocumentCount;
return this;
}
@@ -323,7 +381,7 @@
@CanIgnoreReturnValue
@NonNull
public Builder setMigrationFailureCount(int migrationFailureCount) {
- mBundle.putInt(MIGRATION_FAILURE_COUNT_FIELD, migrationFailureCount);
+ mMigrationFailureCount = migrationFailureCount;
return this;
}
@@ -332,7 +390,21 @@
*/
@NonNull
public SchemaMigrationStats build() {
- return new SchemaMigrationStats(mBundle);
+ return new SchemaMigrationStats(
+ mPackageName,
+ mDatabase,
+ mStatusCode,
+ mExecutorAcquisitionLatencyMillis,
+ mTotalLatencyMillis,
+ mGetSchemaLatencyMillis,
+ mQueryAndTransformLatencyMillis,
+ mFirstSetSchemaLatencyMillis,
+ mIsFirstSetSchemaSuccess,
+ mSecondSetSchemaLatencyMillis,
+ mSaveDocumentLatencyMillis,
+ mTotalNeedMigratedDocumentCount,
+ mMigrationFailureCount,
+ mTotalSuccessMigratedDocumentCount);
}
}
}
diff --git a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/CodeGenerator.java b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/CodeGenerator.java
index caa2e58..e319de9 100644
--- a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/CodeGenerator.java
+++ b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/CodeGenerator.java
@@ -16,6 +16,9 @@
package androidx.appsearch.compiler;
+import static androidx.appsearch.compiler.IntrospectionHelper.DOCUMENT_CLASS_FACTORY_CLASS;
+import static androidx.appsearch.compiler.IntrospectionHelper.getDocumentClassFactoryForClass;
+
import androidx.annotation.NonNull;
import com.google.auto.common.GeneratedAnnotationSpecs;
@@ -36,7 +39,6 @@
*/
class CodeGenerator {
private final ProcessingEnvironment mEnv;
- private final IntrospectionHelper mHelper;
private final DocumentModel mModel;
private final String mOutputPackage;
@@ -49,11 +51,10 @@
}
private CodeGenerator(
- @NonNull ProcessingEnvironment env, @NonNull DocumentModel model)
- throws ProcessingException {
+ @NonNull ProcessingEnvironment env,
+ @NonNull DocumentModel model) throws ProcessingException {
// Prepare constants needed for processing
mEnv = env;
- mHelper = new IntrospectionHelper(env);
mModel = model;
// Perform the actual work of generating code
@@ -69,22 +70,21 @@
* Creates factory class for any class annotated with
* {@link androidx.appsearch.annotation.Document}
* <p>Class Example 1:
- * For a class Foo annotated with @Document, we will generated a
- * $$__AppSearch__Foo.class under the output package.
+ * For a class Foo annotated with @Document, we will generated a
+ * $$__AppSearch__Foo.class under the output package.
* <p>Class Example 2:
- * For an inner class Foo.Bar annotated with @Document, we will generated a
- * $$__AppSearch__Foo$$__Bar.class under the output package.
+ * For an inner class Foo.Bar annotated with @Document, we will generated a
+ * $$__AppSearch__Foo$$__Bar.class under the output package.
*/
private TypeSpec createClass() throws ProcessingException {
// Gets the full name of target class.
String qualifiedName = mModel.getQualifiedDocumentClassName();
String className = qualifiedName.substring(mOutputPackage.length() + 1);
- ClassName genClassName = mHelper.getDocumentClassFactoryForClass(mOutputPackage, className);
+ ClassName genClassName = getDocumentClassFactoryForClass(mOutputPackage, className);
TypeName genClassType = TypeName.get(mModel.getClassElement().asType());
- TypeName factoryType = ParameterizedTypeName.get(
- mHelper.getAppSearchClass("DocumentClassFactory"),
- genClassType);
+ TypeName factoryType =
+ ParameterizedTypeName.get(DOCUMENT_CLASS_FACTORY_CLASS, genClassType);
TypeSpec.Builder genClass = TypeSpec
.classBuilder(genClassName)
diff --git a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/DocumentModel.java b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/DocumentModel.java
index e8faa1e..a6c6a24 100644
--- a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/DocumentModel.java
+++ b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/DocumentModel.java
@@ -15,45 +15,35 @@
*/
package androidx.appsearch.compiler;
-import static androidx.appsearch.compiler.IntrospectionHelper.BUILDER_PRODUCER_CLASS;
-import static androidx.appsearch.compiler.IntrospectionHelper.DOCUMENT_ANNOTATION_CLASS;
import static androidx.appsearch.compiler.IntrospectionHelper.generateClassHierarchy;
import static androidx.appsearch.compiler.IntrospectionHelper.getDocumentAnnotation;
+import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.groupingBy;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
-import androidx.appsearch.compiler.IntrospectionHelper.PropertyClass;
import androidx.appsearch.compiler.annotationwrapper.DataPropertyAnnotation;
import androidx.appsearch.compiler.annotationwrapper.MetadataPropertyAnnotation;
import androidx.appsearch.compiler.annotationwrapper.PropertyAnnotation;
-import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
-import java.util.EnumMap;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.Set;
import java.util.function.Predicate;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
-import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.VariableElement;
import javax.lang.model.util.Elements;
-import javax.lang.model.util.Types;
/**
* Processes @Document annotations.
@@ -62,52 +52,20 @@
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
class DocumentModel {
-
- /** Enumeration of fields that must be handled specially (i.e. are not properties) */
- enum SpecialField {NAMESPACE, ID, CREATION_TIMESTAMP_MILLIS, TTL_MILLIS, SCORE}
-
- /** Determines how the annotation processor has decided to write the value of a field. */
- enum WriteKind {FIELD, SETTER, CREATION_METHOD}
-
private static final String CLASS_SUFFIX = ".class";
private final IntrospectionHelper mHelper;
- private final TypeElement mClass;
- private final Types mTypeUtil;
+
private final Elements mElementUtil;
+
+ private final TypeElement mClass;
+
// The name of the original class annotated with @Document
private final String mQualifiedDocumentClassName;
- private String mSchemaName;
- private final Set<TypeElement> mParentTypes = new LinkedHashSet<>();
- // All methods in the current @Document annotated class/interface, or in the generated class
- // for AutoValue document.
- // Warning: if you change this to a HashSet, we may choose different getters or setters from
- // run to run, causing the generated code to bounce.
- private final LinkedHashSet<ExecutableElement> mAllMethods;
- // All methods in the builder class, if a builder producer is provided.
- private final LinkedHashSet<ExecutableElement> mAllBuilderMethods;
- // Key: Name of the element whose value is set through the setter method.
- // Value: ExecutableElement of the setter method.
- private final Map<String, ExecutableElement> mSetterMethods = new HashMap<>();
- // Warning: if you change this to a HashMap, we may assign elements in a different order from
- // run to run, causing the generated code to bounce.
- // Keeps tracks of all AppSearch elements so we can find creation and access methods for them
- // all
- private final Map<String, Element> mAllAppSearchElements = new LinkedHashMap<>();
- // Warning: if you change this to a HashMap, we may assign elements in a different order from
- // run to run, causing the generated code to bounce.
- // Keeps track of property elements so we don't allow multiple annotated elements of the same
- // name
- private final Map<String, Element> mPropertyElements = new LinkedHashMap<>();
- private final Map<SpecialField, String> mSpecialFieldNames = new EnumMap<>(SpecialField.class);
- private final Map<Element, WriteKind> mWriteKinds = new HashMap<>();
- // Contains the reason why that element couldn't be written either by field or by setter.
- private final Map<Element, ProcessingException> mWriteWhyCreationMethod =
- new HashMap<>();
- private ExecutableElement mChosenCreationMethod = null;
- private List<String> mChosenCreationMethodParams = null;
- private TypeElement mBuilderClass = null;
- private Set<ExecutableElement> mBuilderProducers = new LinkedHashSet<>();
+
+ private final String mSchemaName;
+
+ private final LinkedHashSet<TypeElement> mParentTypes;
private final LinkedHashSet<AnnotatedGetterOrField> mAnnotatedGettersAndFields;
@@ -133,13 +91,16 @@
}
mHelper = new IntrospectionHelper(env);
- mClass = clazz;
- mTypeUtil = env.getTypeUtils();
mElementUtil = env.getElementUtils();
+ mClass = clazz;
mQualifiedDocumentClassName = generatedAutoValueElement != null
? generatedAutoValueElement.getQualifiedName().toString()
: clazz.getQualifiedName().toString();
- mAnnotatedGettersAndFields = scanAnnotatedGettersAndFields(clazz, env);
+ mParentTypes = getParentSchemaTypes(clazz);
+
+ List<TypeElement> classHierarchy = generateClassHierarchy(clazz);
+ mSchemaName = computeSchemaName(classHierarchy);
+ mAnnotatedGettersAndFields = scanAnnotatedGettersAndFields(classHierarchy, env);
requireNoDuplicateMetadataProperties();
mIdAnnotatedGetterOrField = requireGetterOrFieldMatchingPredicate(
@@ -152,83 +113,17 @@
/* errorMessage= */"All @Document classes must have exactly one field annotated "
+ "with @Namespace");
- mAllMethods = mHelper.getAllMethods(clazz);
- mAccessors = inferPropertyAccessors(mAnnotatedGettersAndFields, mAllMethods, mHelper);
+ LinkedHashSet<ExecutableElement> allMethods = mHelper.getAllMethods(clazz);
+ mAccessors = inferPropertyAccessors(mAnnotatedGettersAndFields, allMethods, mHelper);
mDocumentClassCreationInfo =
DocumentClassCreationInfo.infer(clazz, mAnnotatedGettersAndFields, mHelper);
-
- // Scan methods and constructors. We will need this info when processing fields to
- // make sure the fields can be get and set.
- Set<ExecutableElement> potentialCreationMethods = extractCreationMethods(clazz);
- mAllBuilderMethods = mBuilderClass != null
- ? mHelper.getAllMethods(mBuilderClass) : new LinkedHashSet<>();
- scanFields(mClass);
- chooseCreationMethod(potentialCreationMethods);
- }
-
- /**
- * Scans all the elements in typeElement to find a builder producer. If found, set
- * mBuilderProducers and mBuilderClass to the builder producer candidates and the builder class
- * respectively.
- *
- * @throws ProcessingException if there are more than one elements annotated with
- * {@code @Document.BuilderProducer}, or if the builder producer
- * element is not a visible static
- * method or a class.
- */
- private void extractBuilderProducer(TypeElement typeElement)
- throws ProcessingException {
- for (Element child : typeElement.getEnclosedElements()) {
- boolean isAnnotated = false;
- for (AnnotationMirror annotation : child.getAnnotationMirrors()) {
- if (annotation.getAnnotationType().toString().equals(
- BUILDER_PRODUCER_CLASS.canonicalName())) {
- isAnnotated = true;
- break;
- }
- }
- if (!isAnnotated) {
- continue;
- }
- if (child.getKind() != ElementKind.METHOD && child.getKind() != ElementKind.CLASS) {
- // Since @Document.BuilderProducer is configured with
- // @Target({ElementType.METHOD, ElementType.TYPE}), it's not possible to reach here.
- throw new ProcessingException("Builder producer must be a method or a class",
- child);
- }
- if (mBuilderClass != null) {
- throw new ProcessingException("Found duplicated builder producer", typeElement);
- }
- Set<Modifier> methodModifiers = child.getModifiers();
- if (!methodModifiers.contains(Modifier.STATIC)) {
- throw new ProcessingException("Builder producer must be static", child);
- }
- if (methodModifiers.contains(Modifier.PRIVATE)) {
- throw new ProcessingException("Builder producer cannot be private", child);
- }
- if (child.getKind() == ElementKind.METHOD) {
- ExecutableElement method = (ExecutableElement) child;
- mBuilderProducers.add(method);
- mBuilderClass = (TypeElement) mTypeUtil.asElement(method.getReturnType());
- } else {
- // child is a class, so extract all of its constructors as builder producer
- // candidates. The validity of the constructor will be checked later when we
- // choose the right creation method.
- mBuilderClass = (TypeElement) child;
- for (Element builderProducer : mBuilderClass.getEnclosedElements()) {
- if (builderProducer.getKind() == ElementKind.CONSTRUCTOR) {
- mBuilderProducers.add((ExecutableElement) builderProducer);
- }
- }
- }
- }
}
private static LinkedHashSet<AnnotatedGetterOrField> scanAnnotatedGettersAndFields(
- @NonNull TypeElement clazz,
+ @NonNull List<TypeElement> hierarchy,
@NonNull ProcessingEnvironment env) throws ProcessingException {
AnnotatedGetterAndFieldAccumulator accumulator = new AnnotatedGetterAndFieldAccumulator();
- for (TypeElement type : generateClassHierarchy(clazz)) {
+ for (TypeElement type : hierarchy) {
for (Element enclosedElement : type.getEnclosedElements()) {
AnnotatedGetterOrField getterOrField =
AnnotatedGetterOrField.tryCreateFor(enclosedElement, env);
@@ -285,29 +180,6 @@
.orElseThrow(() -> new ProcessingException(errorMessage, mClass));
}
- private Set<ExecutableElement> extractCreationMethods(TypeElement typeElement)
- throws ProcessingException {
- extractBuilderProducer(typeElement);
- // If a builder producer is provided, then only the builder can be used as a creation
- // method.
- if (mBuilderClass != null) {
- return Collections.unmodifiableSet(mBuilderProducers);
- }
-
- Set<ExecutableElement> creationMethods = new LinkedHashSet<>();
- for (Element child : typeElement.getEnclosedElements()) {
- if (child.getKind() == ElementKind.CONSTRUCTOR) {
- creationMethods.add((ExecutableElement) child);
- } else if (child.getKind() == ElementKind.METHOD) {
- ExecutableElement method = (ExecutableElement) child;
- if (isFactoryMethod(method)) {
- creationMethods.add(method);
- }
- }
- }
- return Collections.unmodifiableSet(creationMethods);
- }
-
/**
* Tries to create an {@link DocumentModel} from the given {@link Element}.
*
@@ -360,11 +232,6 @@
return mParentTypes;
}
- @NonNull
- public Map<String, Element> getAllElements() {
- return Collections.unmodifiableMap(mAllAppSearchElements);
- }
-
/**
* Returns all getters/fields (declared or inherited) annotated with some
* {@link PropertyAnnotation}.
@@ -409,93 +276,6 @@
}
/**
- * @deprecated Use {@link #getAnnotatedGettersAndFields()} instead.
- */
- @Deprecated
- @NonNull
- public Map<String, Element> getPropertyElements() {
- return Collections.unmodifiableMap(mPropertyElements);
- }
-
- @Nullable
- public String getSpecialFieldName(SpecialField field) {
- return mSpecialFieldNames.get(field);
- }
-
- @Nullable
- public WriteKind getElementWriteKind(String elementName) {
- Element element = mAllAppSearchElements.get(elementName);
- return mWriteKinds.get(element);
- }
-
- @Nullable
- public ExecutableElement getSetterForElement(String elementName) {
- return mSetterMethods.get(elementName);
- }
-
- /**
- * Finds the AppSearch name for the given property.
- *
- * <p>This is usually the name of the field in Java, but may be changed if the developer
- * specifies a different 'name' parameter in the annotation.
- *
- * @deprecated Use {@link #getAnnotatedGettersAndFields()} and
- * {@link DataPropertyAnnotation#getName()} ()} instead.
- */
- @Deprecated
- @NonNull
- public String getPropertyName(@NonNull Element property) throws ProcessingException {
- AnnotationMirror annotation = getPropertyAnnotation(property);
- Map<String, Object> params = mHelper.getAnnotationParams(annotation);
- String propertyName = params.get("name").toString();
- if (propertyName.isEmpty()) {
- propertyName = getNormalizedElementName(property);
- }
- return propertyName;
- }
-
- /**
- * Returns the first found AppSearch property annotation element from the input element's
- * annotations.
- *
- * @throws ProcessingException if no AppSearch property annotation is found.
- * @deprecated Use {@link #getAnnotatedGettersAndFields()} and
- * {@link AnnotatedGetterOrField#getAnnotation()} instead.
- */
- @Deprecated
- @NonNull
- public AnnotationMirror getPropertyAnnotation(@NonNull Element element)
- throws ProcessingException {
- Objects.requireNonNull(element);
- Set<String> propertyClassPaths = new HashSet<>();
- for (PropertyClass propertyClass : PropertyClass.values()) {
- propertyClassPaths.add(propertyClass.getClassFullPath());
- }
- for (AnnotationMirror annotation : element.getAnnotationMirrors()) {
- String annotationFq = annotation.getAnnotationType().toString();
- if (propertyClassPaths.contains(annotationFq)) {
- return annotation;
- }
- }
- throw new ProcessingException("Missing AppSearch property annotation.", element);
- }
-
- @NonNull
- public ExecutableElement getChosenCreationMethod() {
- return mChosenCreationMethod;
- }
-
- @NonNull
- public List<String> getChosenCreationMethodParams() {
- return Collections.unmodifiableList(mChosenCreationMethodParams);
- }
-
- @Nullable
- public TypeElement getBuilderClass() {
- return mBuilderClass;
- }
-
- /**
* Infers the {@link PropertyAccessor} for each of the {@link AnnotatedGetterOrField}.
*
* <p>Each accessor may be the {@link AnnotatedGetterOrField} itself or some other non-private
@@ -515,493 +295,30 @@
return accessors;
}
- private boolean isFactoryMethod(ExecutableElement method) {
- Set<Modifier> methodModifiers = method.getModifiers();
- return methodModifiers.contains(Modifier.STATIC)
- && !methodModifiers.contains(Modifier.PRIVATE)
- && mTypeUtil.isSameType(method.getReturnType(), mClass.asType());
- }
-
/**
- * Scan the annotations of a field to determine the fields type and handle it accordingly
- *
- * @param childElement the member of class elements currently being scanned
- * @deprecated Rely on {@link #mAnnotatedGettersAndFields} instead of
- * {@link #mAllAppSearchElements} and {@link #mSpecialFieldNames}.
+ * Returns the parent types mentioned within the {@code @Document} annotation.
*/
- @Deprecated
- private void scanAnnotatedField(@NonNull Element childElement) throws ProcessingException {
- String fieldName = childElement.getSimpleName().toString();
-
- // a property field shouldn't be able to override a special field
- if (mSpecialFieldNames.containsValue(fieldName)) {
+ @NonNull
+ private LinkedHashSet<TypeElement> getParentSchemaTypes(
+ @NonNull TypeElement documentClass) throws ProcessingException {
+ AnnotationMirror documentAnnotation = requireNonNull(getDocumentAnnotation(documentClass));
+ Map<String, Object> params = mHelper.getAnnotationParams(documentAnnotation);
+ LinkedHashSet<TypeElement> parentsSchemaTypes = new LinkedHashSet<>();
+ Object parentsParam = params.get("parent");
+ if (parentsParam instanceof List) {
+ for (Object parent : (List<?>) parentsParam) {
+ String parentClassName = parent.toString();
+ parentClassName = parentClassName.substring(0,
+ parentClassName.length() - CLASS_SUFFIX.length());
+ parentsSchemaTypes.add(mElementUtil.getTypeElement(parentClassName));
+ }
+ }
+ if (!parentsSchemaTypes.isEmpty() && params.get("name").toString().isEmpty()) {
throw new ProcessingException(
- "Non-annotated field overriding special annotated fields named: "
- + fieldName, mAllAppSearchElements.get(fieldName));
- }
-
- // no annotation mirrors -> non-indexable field
- for (AnnotationMirror annotation : childElement.getAnnotationMirrors()) {
- String annotationFq = annotation.getAnnotationType().toString();
- if (!annotationFq.startsWith(DOCUMENT_ANNOTATION_CLASS.canonicalName())
- || annotationFq.equals(BUILDER_PRODUCER_CLASS.canonicalName())) {
- continue;
- }
- if (childElement.getKind() == ElementKind.CLASS) {
- continue;
- }
-
- if (annotationFq.equals(MetadataPropertyAnnotation.ID.getClassName().canonicalName())) {
- if (mSpecialFieldNames.containsKey(SpecialField.ID)) {
- throw new ProcessingException(
- "Class hierarchy contains multiple fields annotated @Id",
- childElement);
- }
- mSpecialFieldNames.put(SpecialField.ID, fieldName);
- } else if (annotationFq.equals(
- MetadataPropertyAnnotation.NAMESPACE.getClassName().canonicalName())) {
- if (mSpecialFieldNames.containsKey(SpecialField.NAMESPACE)) {
- throw new ProcessingException(
- "Class hierarchy contains multiple fields annotated @Namespace",
- childElement);
- }
- mSpecialFieldNames.put(SpecialField.NAMESPACE, fieldName);
- } else if (annotationFq.equals(
- MetadataPropertyAnnotation.CREATION_TIMESTAMP_MILLIS
- .getClassName()
- .canonicalName())) {
- if (mSpecialFieldNames.containsKey(SpecialField.CREATION_TIMESTAMP_MILLIS)) {
- throw new ProcessingException("Class hierarchy contains multiple fields "
- + "annotated @CreationTimestampMillis", childElement);
- }
- mSpecialFieldNames.put(
- SpecialField.CREATION_TIMESTAMP_MILLIS, fieldName);
- } else if (annotationFq.equals(
- MetadataPropertyAnnotation.TTL_MILLIS.getClassName().canonicalName())) {
- if (mSpecialFieldNames.containsKey(SpecialField.TTL_MILLIS)) {
- throw new ProcessingException(
- "Class hierarchy contains multiple fields annotated @TtlMillis",
- childElement);
- }
- mSpecialFieldNames.put(SpecialField.TTL_MILLIS, fieldName);
- } else if (annotationFq.equals(
- MetadataPropertyAnnotation.SCORE.getClassName().canonicalName())) {
- if (mSpecialFieldNames.containsKey(SpecialField.SCORE)) {
- throw new ProcessingException(
- "Class hierarchy contains multiple fields annotated @Score",
- childElement);
- }
- mSpecialFieldNames.put(SpecialField.SCORE, fieldName);
- } else {
- PropertyClass propertyClass = getPropertyClass(annotationFq);
- if (propertyClass != null) {
- // A property must either:
- // 1. be unique
- // 2. override a property from the Java parent while maintaining the same
- // AppSearch property name
- checkFieldTypeForPropertyAnnotation(childElement, propertyClass);
- // It's assumed that parent types, in the context of Java's type system,
- // are always visited before child types, so existingProperty must come
- // from the parent type. To make this assumption valid, the result
- // returned by generateClassHierarchy must put parent types before child
- // types.
- Element existingProperty = mPropertyElements.get(fieldName);
- if (existingProperty != null) {
- if (!mTypeUtil.isSameType(
- existingProperty.asType(), childElement.asType())) {
- throw new ProcessingException(
- "Cannot override a property with a different type",
- childElement);
- }
- if (!getPropertyName(existingProperty).equals(getPropertyName(
- childElement))) {
- throw new ProcessingException(
- "Cannot override a property with a different name",
- childElement);
- }
- }
- mPropertyElements.put(fieldName, childElement);
- }
- }
-
- mAllAppSearchElements.put(fieldName, childElement);
- }
- }
-
- /**
- * Scans all the fields of the class, as well as superclasses annotated with @Document,
- * to get AppSearch fields such as id
- *
- * @param element the class to scan
- */
- private void scanFields(@NonNull TypeElement element) throws ProcessingException {
- AnnotationMirror documentAnnotation = getDocumentAnnotation(element);
- if (documentAnnotation != null) {
- Map<String, Object> params = mHelper.getAnnotationParams(documentAnnotation);
- Object parents = params.get("parent");
- if (parents instanceof List) {
- for (Object parent : (List<?>) parents) {
- String parentClassName = parent.toString();
- parentClassName = parentClassName.substring(0,
- parentClassName.length() - CLASS_SUFFIX.length());
- mParentTypes.add(mElementUtil.getTypeElement(parentClassName));
- }
- }
- if (!mParentTypes.isEmpty() && params.get("name").toString().isEmpty()) {
- throw new ProcessingException(
- "All @Document classes with a parent must explicitly provide a name",
- mClass);
- }
- }
-
- List<TypeElement> hierarchy = generateClassHierarchy(element);
-
- for (TypeElement clazz : hierarchy) {
- List<? extends Element> enclosedElements = clazz.getEnclosedElements();
- for (Element childElement : enclosedElements) {
- scanAnnotatedField(childElement);
- }
- }
-
- // Every document must always have a namespace
- if (!mSpecialFieldNames.containsKey(SpecialField.NAMESPACE)) {
- throw new ProcessingException(
- "All @Document classes must have exactly one field annotated with @Namespace",
+ "All @Document classes with a parent must explicitly provide a name",
mClass);
}
-
- // Every document must always have an ID
- if (!mSpecialFieldNames.containsKey(SpecialField.ID)) {
- throw new ProcessingException(
- "All @Document classes must have exactly one field annotated with @Id",
- mClass);
- }
-
- mSchemaName = computeSchemaName(hierarchy);
-
- for (Element appSearchField : mAllAppSearchElements.values()) {
- chooseWriteKind(appSearchField);
- }
- }
-
- /**
- * Checks whether property's data type matches the {@code androidx.appsearch.annotation
- * .Document} property annotation's requirement.
- *
- * @throws ProcessingException if data type doesn't match property annotation's requirement.
- */
- void checkFieldTypeForPropertyAnnotation(@NonNull Element property,
- PropertyClass propertyClass) throws ProcessingException {
- switch (propertyClass) {
- case BOOLEAN_PROPERTY_CLASS:
- if (mHelper.isFieldOfExactType(property, mHelper.mBooleanBoxType,
- mHelper.mBooleanPrimitiveType)) {
- return;
- }
- break;
- case BYTES_PROPERTY_CLASS:
- if (mHelper.isFieldOfExactType(property, mHelper.mByteBoxType,
- mHelper.mBytePrimitiveType, mHelper.mByteBoxArrayType,
- mHelper.mBytePrimitiveArrayType)) {
- return;
- }
- break;
- case DOCUMENT_PROPERTY_CLASS:
- if (mHelper.isFieldOfDocumentType(property)) {
- return;
- }
- break;
- case DOUBLE_PROPERTY_CLASS:
- if (mHelper.isFieldOfExactType(property, mHelper.mDoubleBoxType,
- mHelper.mDoublePrimitiveType, mHelper.mFloatBoxType,
- mHelper.mFloatPrimitiveType)) {
- return;
- }
- break;
- case LONG_PROPERTY_CLASS:
- if (mHelper.isFieldOfExactType(property, mHelper.mIntegerBoxType,
- mHelper.mIntPrimitiveType, mHelper.mLongBoxType,
- mHelper.mLongPrimitiveType)) {
- return;
- }
- break;
- case STRING_PROPERTY_CLASS:
- if (mHelper.isFieldOfExactType(property, mHelper.mStringType)) {
- return;
- }
- break;
- default:
- // do nothing
- }
- throw new ProcessingException(
- "Property Annotation " + propertyClass.getClassFullPath() + " doesn't accept the "
- + "data type of property field " + property.getSimpleName(), property);
- }
-
- /**
- * Returns the {@link PropertyClass} with {@code annotationFq} as full class path, and {@code
- * null} if failed to find such a {@link PropertyClass}.
- */
- @Nullable
- private PropertyClass getPropertyClass(@Nullable String annotationFq) {
- for (PropertyClass propertyClass : PropertyClass.values()) {
- if (propertyClass.isPropertyClass(annotationFq)) {
- return propertyClass;
- }
- }
- return null;
- }
-
- /**
- * Chooses how to write a given field.
- *
- * <p>The writing strategy can be one of: visible mutable field, or visible setter, or visible
- * creation method accepting at minimum all fields that aren't mutable and have no visible
- * setter.
- */
- private void chooseWriteKind(@NonNull Element field) {
- // TODO(b/300114568): Carve out better distinction b/w the different write strategies
- Set<Modifier> modifiers = field.getModifiers();
- // Choose set access
- if (modifiers.contains(Modifier.PRIVATE) || modifiers.contains(Modifier.FINAL)
- || modifiers.contains(Modifier.STATIC) || field.getKind() == ElementKind.METHOD
- || mBuilderClass != null) {
- // Try to find a setter. If we can't find one, mark the WriteKind as {@code
- // CREATION_METHOD}. We don't know if this is true yet, the creation methods will be
- // inspected in a subsequent pass.
- try {
- findSetter(field);
- mWriteKinds.put(field, WriteKind.SETTER);
- } catch (ProcessingException e) {
- // We'll look for a creation method, so we may still be able to set this field,
- // but it's more likely the developer configured the setter incorrectly. Keep
- // the exception around to include it in the report if no creation method is found.
- mWriteWhyCreationMethod.put(field, e);
- mWriteKinds.put(field, WriteKind.CREATION_METHOD);
- }
- } else {
- mWriteKinds.put(field, WriteKind.FIELD);
- }
- }
-
- private void chooseCreationMethod(Set<ExecutableElement> creationMethods)
- throws ProcessingException {
- // Maps field name to Element.
- // If this is changed to a HashSet, we might report errors to the developer in a different
- // order about why a field was written via creation method.
- Map<String, Element> creationMethodWrittenFields = new LinkedHashMap<>();
- for (Map.Entry<Element, WriteKind> it : mWriteKinds.entrySet()) {
- if (it.getValue() == WriteKind.CREATION_METHOD) {
- String name = it.getKey().getSimpleName().toString();
- creationMethodWrittenFields.put(name, it.getKey());
- }
- }
-
- // Maps normalized field name to real field name.
- Map<String, String> normalizedToRawFieldName = new HashMap<>();
- for (Element field : mAllAppSearchElements.values()) {
- normalizedToRawFieldName.put(getNormalizedElementName(field),
- field.getSimpleName().toString());
- }
-
- Map<ExecutableElement, String> whyNotCreationMethod = new HashMap<>();
- creationMethodSearch:
- for (ExecutableElement method : creationMethods) {
- if (method.getModifiers().contains(Modifier.PRIVATE)) {
- whyNotCreationMethod.put(method, "Creation method is private");
- continue creationMethodSearch;
- }
- // The field name of each field that goes into the creation method, in the order they
- // are declared in the creation method signature.
- List<String> creationMethodParamFields = new ArrayList<>();
- Set<String> remainingFields = new HashSet<>(creationMethodWrittenFields.keySet());
- for (VariableElement parameter : method.getParameters()) {
- String paramName = parameter.getSimpleName().toString();
- String fieldName = normalizedToRawFieldName.get(paramName);
- if (fieldName == null) {
- whyNotCreationMethod.put(
- method,
- "Parameter \"" + paramName + "\" is not an AppSearch parameter; don't "
- + "know how to supply it.");
- continue creationMethodSearch;
- }
- remainingFields.remove(fieldName);
- creationMethodParamFields.add(fieldName);
- }
- if (!remainingFields.isEmpty()) {
- whyNotCreationMethod.put(
- method,
- "This method doesn't have parameters for the following fields: "
- + remainingFields);
- continue creationMethodSearch;
- }
-
- // If the field is set in the constructor, choose creation method for the write kind
- for (String param : creationMethodParamFields) {
- for (Element appSearchField : mAllAppSearchElements.values()) {
- if (appSearchField.getSimpleName().toString().equals(param)) {
- mWriteKinds.put(appSearchField, WriteKind.CREATION_METHOD);
- break;
- }
- }
- }
-
- // Found one!
- mChosenCreationMethod = method;
- mChosenCreationMethodParams = creationMethodParamFields;
- return;
- }
-
- // If we got here, we couldn't find any creation methods.
- ProcessingException e =
- new ProcessingException(
- "Failed to find any suitable creation methods to build class \""
- + mClass.getQualifiedName()
- + "\". See warnings for details.",
- mClass);
-
- // Inform the developer why we started looking for creation methods in the first place.
- for (Element field : creationMethodWrittenFields.values()) {
- ProcessingException warning = mWriteWhyCreationMethod.get(field);
- if (warning != null) {
- e.addWarning(warning);
- }
- }
-
- // Inform the developer about why each creation method we considered was rejected.
- for (Map.Entry<ExecutableElement, String> it : whyNotCreationMethod.entrySet()) {
- ProcessingException warning = new ProcessingException(
- "Cannot use this creation method to construct the class: " + it.getValue(),
- it.getKey());
- e.addWarning(warning);
- }
-
- throw e;
- }
-
- /**
- * Finds setter function for a private field, or for a property defined by a annotated getter
- * method.
- */
- private void findSetter(@NonNull Element element) throws ProcessingException {
- String elementName = element.getSimpleName().toString();
- // We can't report setter failure until we've searched the creation methods, so this
- // message is anticipatory and should be buffered by the caller.
- String error;
- if (mBuilderClass != null) {
- error = "Element cannot be written directly because a builder producer is provided";
- } else if (element.getKind() == ElementKind.METHOD) {
- error = "Element cannot be written directly because it is an annotated getter";
- } else {
- error = "Field cannot be written directly because it is private, final, or static";
- }
- error += ", and we failed to find a suitable setter for \"" + elementName + "\". "
- + "Trying to find a suitable creation method.";
- ProcessingException e = new ProcessingException(error,
- mAllAppSearchElements.get(elementName));
-
- // When using the builder pattern, setters can only come from the builder.
- Set<ExecutableElement> methods;
- if (mBuilderClass != null) {
- methods = mAllBuilderMethods;
- } else {
- methods = mAllMethods;
- }
- for (ExecutableElement method : methods) {
- String methodName = method.getSimpleName().toString();
- String normalizedElementName = getNormalizedElementName(element);
- if (methodName.equals(normalizedElementName)
- || methodName.equals("set"
- + normalizedElementName.substring(0, 1).toUpperCase()
- + normalizedElementName.substring(1))) {
- if (method.getModifiers().contains(Modifier.PRIVATE)) {
- e.addWarning(new ProcessingException(
- "Setter cannot be used: private visibility", method));
- continue;
- }
- if (method.getParameters().size() != 1) {
- e.addWarning(new ProcessingException(
- "Setter cannot be used: takes " + method.getParameters().size()
- + " parameters instead of 1",
- method));
- continue;
- }
- // Found one!
- mSetterMethods.put(elementName, method);
- return;
- }
- }
-
- // Broke out of the loop without finding anything.
- throw e;
- }
-
- /**
- * Produces the canonical name of a field element.
- *
- * @see #getNormalizedElementName(Element)
- */
- private String getNormalizedFieldElementName(Element fieldElement) {
- String fieldName = fieldElement.getSimpleName().toString();
-
- if (fieldName.length() < 2) {
- return fieldName;
- }
-
- // Handle convention of having field names start with m
- // (e.g. String mName; public String getName())
- if (fieldName.charAt(0) == 'm' && Character.isUpperCase(fieldName.charAt(1))) {
- return fieldName.substring(1, 2).toLowerCase() + fieldName.substring(2);
- }
-
- // Handle convention of having field names start with _
- // (e.g. String _name; public String getName())
- if (fieldName.charAt(0) == '_'
- && fieldName.charAt(1) != '_'
- && Character.isLowerCase(fieldName.charAt(1))) {
- return fieldName.substring(1);
- }
-
- // Handle convention of having field names end with _
- // (e.g. String name_; public String getName())
- if (fieldName.charAt(fieldName.length() - 1) == '_'
- && fieldName.charAt(fieldName.length() - 2) != '_') {
- return fieldName.substring(0, fieldName.length() - 1);
- }
-
- return fieldName;
- }
-
- /**
- * Produces the canonical name of a method element.
- *
- * @see #getNormalizedElementName(Element)
- */
- private String getNormalizedMethodElementName(Element methodElement) {
- String methodName = methodElement.getSimpleName().toString();
-
- // If this property is defined by an annotated getter, then we can remove the prefix
- // "get" or "is" if possible.
- if (methodName.startsWith("get") && methodName.length() > 3) {
- methodName = methodName.substring(3, 4).toLowerCase() + methodName.substring(4);
- } else if (mHelper.isFieldOfBooleanType(methodElement) && methodName.startsWith("is")
- && methodName.length() > 2) {
- // "is" is a valid getter prefix for boolean property.
- methodName = methodName.substring(2, 3).toLowerCase() + methodName.substring(3);
- }
- // Return early because the rest normalization procedures do not apply to getters.
- return methodName;
- }
-
- /**
- * Produces the canonical name of a element (which is used as the default property name as
- * well as to find accessors) by removing prefixes and suffixes of common conventions.
- */
- private String getNormalizedElementName(Element property) {
- if (property.getKind() == ElementKind.METHOD) {
- return getNormalizedMethodElementName(property);
- }
- return getNormalizedFieldElementName(property);
+ return parentsSchemaTypes;
}
/**
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 1947dea..8bb9788 100644
--- a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/IntrospectionHelper.java
+++ b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/IntrospectionHelper.java
@@ -89,6 +89,9 @@
public static final ClassName BUILDER_PRODUCER_CLASS =
DOCUMENT_ANNOTATION_CLASS.nestedClass("BuilderProducer");
+ static final ClassName DOCUMENT_CLASS_FACTORY_CLASS =
+ ClassName.get(APPSEARCH_PKG, "DocumentClassFactory");
+
public final TypeMirror mStringType;
public final TypeMirror mLongPrimitiveType;
public final TypeMirror mIntPrimitiveType;
@@ -172,7 +175,8 @@
}
/** Checks whether the property data type is one of the valid types. */
- public boolean isFieldOfExactType(Element property, TypeMirror... validTypes) {
+ public boolean isFieldOfExactType(
+ @NonNull Element property, @NonNull TypeMirror... validTypes) {
TypeMirror propertyType = getPropertyType(property);
for (TypeMirror validType : validTypes) {
if (propertyType.getKind() == TypeKind.ARRAY) {
@@ -193,31 +197,14 @@
}
/** Checks whether the property data type is of boolean type. */
- public boolean isFieldOfBooleanType(Element property) {
+ public boolean isFieldOfBooleanType(@NonNull Element property) {
return isFieldOfExactType(property, mBooleanBoxType, mBooleanPrimitiveType);
}
/**
- * Checks whether the property data class has {@code androidx.appsearch.annotation.Document
- * .DocumentProperty} annotation.
+ * Returns the annotation's params as a map. Includes the default values.
*/
- public boolean isFieldOfDocumentType(Element property) {
- TypeMirror propertyType = getPropertyType(property);
-
- AnnotationMirror documentAnnotation = null;
-
- if (propertyType.getKind() == TypeKind.ARRAY) {
- documentAnnotation = getDocumentAnnotation(
- mTypeUtils.asElement(((ArrayType) propertyType).getComponentType()));
- } else if (mTypeUtils.isAssignable(mTypeUtils.erasure(propertyType), mCollectionType)) {
- documentAnnotation = getDocumentAnnotation(mTypeUtils.asElement(
- ((DeclaredType) propertyType).getTypeArguments().get(0)));
- } else {
- documentAnnotation = getDocumentAnnotation(mTypeUtils.asElement(propertyType));
- }
- return documentAnnotation != null;
- }
-
+ @NonNull
public Map<String, Object> getAnnotationParams(@NonNull AnnotationMirror annotation) {
Map<? extends ExecutableElement, ? extends AnnotationValue> values =
mEnv.getElementUtils().getElementValuesWithDefaults(annotation);
@@ -251,10 +238,6 @@
return getDocumentClassFactoryForClass(clazz.packageName(), className);
}
- public ClassName getAppSearchClass(String clazz, String... nested) {
- return ClassName.get(APPSEARCH_PKG, clazz, nested);
- }
-
/**
* Returns all the methods within a class, whether inherited or declared directly.
*
@@ -454,27 +437,4 @@
hierarchy, visited);
}
}
-
- enum PropertyClass {
- BOOLEAN_PROPERTY_CLASS("androidx.appsearch.annotation.Document.BooleanProperty"),
- BYTES_PROPERTY_CLASS("androidx.appsearch.annotation.Document.BytesProperty"),
- DOCUMENT_PROPERTY_CLASS("androidx.appsearch.annotation.Document.DocumentProperty"),
- DOUBLE_PROPERTY_CLASS("androidx.appsearch.annotation.Document.DoubleProperty"),
- LONG_PROPERTY_CLASS("androidx.appsearch.annotation.Document.LongProperty"),
- STRING_PROPERTY_CLASS("androidx.appsearch.annotation.Document.StringProperty");
-
- private final String mClassFullPath;
-
- PropertyClass(String classFullPath) {
- mClassFullPath = classFullPath;
- }
-
- String getClassFullPath() {
- return mClassFullPath;
- }
-
- boolean isPropertyClass(String annotationFq) {
- return mClassFullPath.equals(annotationFq);
- }
- }
}
diff --git a/appsearch/compiler/src/test/java/androidx/appsearch/compiler/AppSearchCompilerTest.java b/appsearch/compiler/src/test/java/androidx/appsearch/compiler/AppSearchCompilerTest.java
index 5690a38..22910ce 100644
--- a/appsearch/compiler/src/test/java/androidx/appsearch/compiler/AppSearchCompilerTest.java
+++ b/appsearch/compiler/src/test/java/androidx/appsearch/compiler/AppSearchCompilerTest.java
@@ -96,7 +96,7 @@
}
@Test
- public void testAutoValueInheritance() throws Exception {
+ public void testAutoValueInheritance() {
Compilation docExtendsAutoValueDoc = compile(
"import com.google.auto.value.AutoValue;\n"
+ "import com.google.auto.value.AutoValue.*;\n"
@@ -144,7 +144,7 @@
}
@Test
- public void testSuperClassErrors() throws Exception {
+ public void testSuperClassErrors() {
Compilation specialFieldReassigned = compile(
"@Document\n"
+ "public class Gift {\n"
@@ -169,27 +169,6 @@
"Property type must stay consistent when overriding annotated "
+ "members but changed from @Id -> @StringProperty");
- Compilation nonAnnotatedFieldHasSameName = compile(
- "@Document\n"
- + "public class Gift {\n"
- + " @Document.Namespace String namespace;\n"
- + " @Document.Id String id;\n"
- + " Gift(String id, String namespace) {\n"
- + " this.id = id;\n"
- + " this.namespace = namespace;\n"
- + " }\n"
- + "}\n"
- + "@Document\n"
- + "class CoolGift extends Gift {\n"
- + " String id;\n"
- + " CoolGift(String id, String namespace) {\n"
- + " super(id, namespace);\n"
- + " }\n"
- + " public String getId() { return id; }\n"
- + "}\n");
- assertThat(nonAnnotatedFieldHasSameName).hadErrorContaining(
- "Non-annotated field overriding special annotated fields named: id");
-
//error on collision
Compilation idCollision = compile(
"@Document\n"
@@ -1209,7 +1188,7 @@
}
@Test
- public void testInvalidLongPropertyIndexingType() throws Exception {
+ public void testInvalidLongPropertyIndexingType() {
// AppSearchSchema requires Android and is not available in this desktop test, so we cheat
// by using the integer constants directly.
Compilation compilation = compile(
@@ -1241,7 +1220,7 @@
}
@Test
- public void testRepeatedPropertyJoinableType_throwsError() throws Exception {
+ public void testRepeatedPropertyJoinableType_throwsError() {
Compilation compilation = compile(
"import java.util.*;\n"
+ "@Document\n"
@@ -1711,7 +1690,7 @@
}
@Test
- public void testPolymorphismOverrideExtendedPropertyInvalid() throws Exception {
+ public void testPolymorphismOverrideExtendedPropertyInvalid() {
// Overridden properties cannot change the names.
Compilation compilation = compile(
"@Document\n"
@@ -1828,7 +1807,7 @@
}
@Test
- public void testPolymorphismChildTypeWithoutName() throws Exception {
+ public void testPolymorphismChildTypeWithoutName() {
Compilation compilation = compile(
"@Document\n"
+ "class Parent {\n"
@@ -1922,7 +1901,7 @@
}
@Test
- public void testAnnotationOnGetterWithoutFactory() throws Exception {
+ public void testAnnotationOnGetterWithoutFactory() {
// An interface without any factory method is not able to initialize, as interfaces do
// not have constructors.
Compilation compilation = compile(
@@ -2098,7 +2077,7 @@
}
@Test
- public void testSameNameGetterAndFieldAnnotatingBothButGetterIsPrivate() throws Exception {
+ public void testSameNameGetterAndFieldAnnotatingBothButGetterIsPrivate() {
Compilation compilation = compile(
"@Document\n"
+ "public class Gift {\n"
@@ -2168,7 +2147,7 @@
}
@Test
- public void testGetterWithParameterCannotBeUsed() throws Exception {
+ public void testGetterWithParameterCannotBeUsed() {
Compilation compilation = compile(
"@Document\n"
+ "public class Gift {\n"
@@ -2185,7 +2164,7 @@
}
@Test
- public void testPrivateGetterCannotBeUsed() throws Exception {
+ public void testPrivateGetterCannotBeUsed() {
Compilation compilation = compile(
"@Document\n"
+ "public class Gift {\n"
@@ -2224,7 +2203,7 @@
}
@Test
- public void testGetterWithWrongReturnType() throws Exception {
+ public void testGetterWithWrongReturnType() {
Compilation compilation = compile(
"@Document\n"
+ "public class Gift {\n"
@@ -2571,7 +2550,7 @@
}
@Test
- public void testCreationByBuilderErrors() throws Exception {
+ public void testCreationByBuilderErrors() {
// Cannot have multiple builder producer
Compilation compilation = compile(
"@Document\n"
diff --git a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/BenchmarkStateTest.kt b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/BenchmarkStateTest.kt
index ce079ea..72edbd2 100644
--- a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/BenchmarkStateTest.kt
+++ b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/BenchmarkStateTest.kt
@@ -168,8 +168,8 @@
if (simplifiedTimingOnlyMode) 0 else BenchmarkState.REPEAT_COUNT_ALLOCATION
val expectedCount = report.warmupIterations +
report.repeatIterations * expectedRepeatCount +
- // method tracing phase by default only when API > 21, and simplified timing off
- if (simplifiedTimingOnlyMode || Build.VERSION.SDK_INT <= 21) 0 else 1
+ // method tracing phase by default only when API in 22..33, and simplified timing off
+ if (Build.VERSION.SDK_INT in 22..33 && !simplifiedTimingOnlyMode) 1 else 0
assertEquals(expectedCount, total)
if (Arguments.iterations != null) {
diff --git a/benchmark/benchmark-common/src/main/java/androidx/benchmark/Arguments.kt b/benchmark/benchmark-common/src/main/java/androidx/benchmark/Arguments.kt
index c866e25..d431e05 100644
--- a/benchmark/benchmark-common/src/main/java/androidx/benchmark/Arguments.kt
+++ b/benchmark/benchmark-common/src/main/java/androidx/benchmark/Arguments.kt
@@ -90,10 +90,11 @@
val argumentName = "profiling.mode"
val argumentValue = getBenchmarkArgument(argumentName, "DEFAULT_VAL")
if (argumentValue == "DEFAULT_VAL") {
- return if (Build.VERSION.SDK_INT > 21) {
+ return if (Build.VERSION.SDK_INT in 22..33) {
MethodTracing to true
} else {
// Method tracing can corrupt the stack on API 21, see b/300658578
+ // on API 34, it causes regressions in jit behavior, see b/303686344
null to true
}
}
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 9d96b85..aeb7b1b 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
@@ -21,9 +21,11 @@
import androidx.bluetooth.BluetoothLe
import java.util.UUID
import kotlinx.coroutines.cancel
+import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.runTest
import org.junit.Assert
+import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
@@ -46,6 +48,17 @@
}
}
+ @Test
+ fun advertise_noBlock() = runTest {
+ val params = AdvertiseParams()
+ val advertiseJob = launch {
+ bluetoothLe.advertise(params)
+ }
+ delay(100)
+ assertTrue(advertiseJob.isActive)
+ advertiseJob.cancel()
+ }
+
/**
* Tests if [BluetoothLe.advertise] returns error when data is larger than
* the legacy advertise limit (31 bytes)
diff --git a/bluetooth/bluetooth-testing/src/test/kotlin/androidx/bluetooth/testing/RobolectricGattServerTest.kt b/bluetooth/bluetooth-testing/src/test/kotlin/androidx/bluetooth/testing/RobolectricGattServerTest.kt
index 14f8951..6882a83 100644
--- a/bluetooth/bluetooth-testing/src/test/kotlin/androidx/bluetooth/testing/RobolectricGattServerTest.kt
+++ b/bluetooth/bluetooth-testing/src/test/kotlin/androidx/bluetooth/testing/RobolectricGattServerTest.kt
@@ -42,6 +42,7 @@
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import org.junit.Assert
+import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
@@ -71,8 +72,6 @@
private val notifyCharUuid = UUID.fromString("00002223-0000-1000-8000-00805F9B34FB")
private val unknownCharUuid = UUID.fromString("00002224-0000-1000-8000-00805F9B34FB")
- private val cccdUuid = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")
-
private val readCharacteristic = GattCharacteristic(readCharUuid, PROPERTY_READ)
private val writeCharacteristic = GattCharacteristic(
writeCharUuid, PROPERTY_READ or PROPERTY_WRITE
@@ -99,6 +98,8 @@
@Test
fun openGattServer() = runTest {
val device = createDevice("00:11:22:33:44:55")
+ val deviceName = "Device A"
+ shadowOf(device).setName(deviceName)
val opened = CompletableDeferred<Unit>()
val closed = CompletableDeferred<Unit>()
@@ -112,7 +113,10 @@
}
bluetoothLe.openGattServer(listOf()) {
- connectRequests.first().accept {}
+ connectRequests.first().let {
+ assertEquals(deviceName, it.device.name)
+ it.accept {}
+ }
}
assertTrue(opened.isCompleted)
@@ -438,7 +442,7 @@
bluetoothLe.openGattServer(services) {
connectRequests.collect {
it.accept {
- assertTrue(notify(notifyCharacteristic, valueToNotify.toByteArray()))
+ notify(notifyCharacteristic, valueToNotify.toByteArray())
// Close the server
[email protected]()
}
@@ -589,10 +593,12 @@
characteristic: FwkCharacteristic,
confirm: Boolean,
value: ByteArray
- ) {
- baseAdapter.notifyCharacteristicChanged(device, characteristic, confirm, value)
- onNotifyCharacteristicChangedListener
- ?.onNotifyCharacteristicChanged(device, characteristic, confirm, value)
+ ): Int? {
+ baseAdapter.notifyCharacteristicChanged(device, characteristic, confirm, value).let {
+ onNotifyCharacteristicChangedListener
+ ?.onNotifyCharacteristicChanged(device, characteristic, confirm, value)
+ return it
+ }
}
override fun sendResponse(
diff --git a/bluetooth/bluetooth/api/current.txt b/bluetooth/bluetooth/api/current.txt
index c7ef8cd..6f3e787 100644
--- a/bluetooth/bluetooth/api/current.txt
+++ b/bluetooth/bluetooth/api/current.txt
@@ -91,7 +91,7 @@
public static interface BluetoothLe.GattServerSessionScope {
method public androidx.bluetooth.BluetoothDevice getDevice();
method public kotlinx.coroutines.flow.Flow<androidx.bluetooth.GattServerRequest> getRequests();
- method public suspend Object? notify(androidx.bluetooth.GattCharacteristic characteristic, byte[] value, kotlin.coroutines.Continuation<? super java.lang.Boolean>);
+ method public suspend Object? notify(androidx.bluetooth.GattCharacteristic characteristic, byte[] value, kotlin.coroutines.Continuation<? super kotlin.Unit>);
property public abstract androidx.bluetooth.BluetoothDevice device;
property public abstract kotlinx.coroutines.flow.Flow<androidx.bluetooth.GattServerRequest> requests;
}
diff --git a/bluetooth/bluetooth/api/restricted_current.txt b/bluetooth/bluetooth/api/restricted_current.txt
index c7ef8cd..6f3e787 100644
--- a/bluetooth/bluetooth/api/restricted_current.txt
+++ b/bluetooth/bluetooth/api/restricted_current.txt
@@ -91,7 +91,7 @@
public static interface BluetoothLe.GattServerSessionScope {
method public androidx.bluetooth.BluetoothDevice getDevice();
method public kotlinx.coroutines.flow.Flow<androidx.bluetooth.GattServerRequest> getRequests();
- method public suspend Object? notify(androidx.bluetooth.GattCharacteristic characteristic, byte[] value, kotlin.coroutines.Continuation<? super java.lang.Boolean>);
+ method public suspend Object? notify(androidx.bluetooth.GattCharacteristic characteristic, byte[] value, kotlin.coroutines.Continuation<? super kotlin.Unit>);
property public abstract androidx.bluetooth.BluetoothDevice device;
property public abstract kotlinx.coroutines.flow.Flow<androidx.bluetooth.GattServerRequest> requests;
}
diff --git a/bluetooth/bluetooth/src/androidTest/java/androidx/bluetooth/BluetoothAddressTest.kt b/bluetooth/bluetooth/src/androidTest/java/androidx/bluetooth/BluetoothAddressTest.kt
index 3c0e6e4..562d22e 100644
--- a/bluetooth/bluetooth/src/androidTest/java/androidx/bluetooth/BluetoothAddressTest.kt
+++ b/bluetooth/bluetooth/src/androidTest/java/androidx/bluetooth/BluetoothAddressTest.kt
@@ -18,6 +18,7 @@
import junit.framework.TestCase.assertEquals
import kotlin.test.assertFailsWith
+import kotlin.test.assertNotEquals
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
@@ -80,4 +81,25 @@
BluetoothAddress(invalidAddress, BluetoothAddress.ADDRESS_TYPE_UNKNOWN)
}
}
+
+ @Test
+ fun equality() {
+ val publicAddress = BluetoothAddress(TEST_ADDRESS_PUBLIC,
+ BluetoothAddress.ADDRESS_TYPE_PUBLIC)
+ val sameAddress = BluetoothAddress(TEST_ADDRESS_PUBLIC,
+ BluetoothAddress.ADDRESS_TYPE_PUBLIC)
+ val addressWithDifferentAddress = BluetoothAddress(
+ TEST_ADDRESS_RANDOM_STATIC,
+ BluetoothAddress.ADDRESS_TYPE_PUBLIC
+ )
+ val addressWithDifferentType = BluetoothAddress(
+ TEST_ADDRESS_PUBLIC,
+ BluetoothAddress.ADDRESS_TYPE_RANDOM_STATIC
+ )
+
+ assertEquals(publicAddress, sameAddress)
+ assertEquals(publicAddress.hashCode(), sameAddress.hashCode())
+ assertNotEquals(publicAddress, addressWithDifferentAddress)
+ assertNotEquals(publicAddress, addressWithDifferentType)
+ }
}
diff --git a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/BluetoothLe.kt b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/BluetoothLe.kt
index 153959b..17d1437 100644
--- a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/BluetoothLe.kt
+++ b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/BluetoothLe.kt
@@ -285,6 +285,9 @@
/**
* Writes the characteristic value to the server.
*
+ * It could fail if the [characteristic] doesn't have the write property or the length
+ * of the [value] is greater than the maximum length of an attribute value (512).
+ *
* @param characteristic a remote [GattCharacteristic] to write
* @param value a value to be written.
* @return the result of the write operation
@@ -372,9 +375,9 @@
* @param characteristic the updated characteristic
* @param value the new value of the characteristic
*
- * @return `true` if the notification sent successfully, otherwise `false`
+ * @throws CancellationException if it failed to notify
*/
- suspend fun notify(characteristic: GattCharacteristic, value: ByteArray): Boolean
+ suspend fun notify(characteristic: GattCharacteristic, value: ByteArray)
}
/**
diff --git a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/GattClient.kt b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/GattClient.kt
index bfcfa45..a2e1ce6 100644
--- a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/GattClient.kt
+++ b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/GattClient.kt
@@ -90,10 +90,11 @@
companion object {
private const val TAG = "GattClient"
+ private const val GATT_MAX_ATTR_LENGTH = 512
/**
- * The maximum ATT size(512) + header(3)
+ * The maximum ATT size + header(3)
*/
- private const val GATT_MAX_MTU = 515
+ private const val GATT_MAX_MTU = GATT_MAX_ATTR_LENGTH + 3
private const val CONNECT_TIMEOUT_MS = 30_000L
private val CCCD_UID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")
@@ -289,9 +290,14 @@
else return Result.failure(
IllegalArgumentException("can't write to the characteristic"))
+ if (value.size > GATT_MAX_ATTR_LENGTH) {
+ return Result.failure(IllegalArgumentException("too long value to write"))
+ }
+
return runTask {
fwkAdapter.writeCharacteristic(
- characteristic.fwkCharacteristic, value, writeType)
+ characteristic.fwkCharacteristic, value, writeType
+ )
val res = takeMatchingResult<CallbackResult.OnCharacteristicWrite>(
callbackResultsFlow
) {
@@ -299,7 +305,7 @@
}
if (res.status == BluetoothGatt.GATT_SUCCESS) Result.success(Unit)
// TODO: throw precise reason if we can gather the info
- else Result.failure(CancellationException("fail"))
+ else Result.failure(CancellationException("fail with error = ${res.status}"))
}
}
diff --git a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/GattServer.kt b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/GattServer.kt
index 95a35a7..3d5566a6 100644
--- a/bluetooth/bluetooth/src/main/java/androidx/bluetooth/GattServer.kt
+++ b/bluetooth/bluetooth/src/main/java/androidx/bluetooth/GattServer.kt
@@ -27,11 +27,13 @@
import android.bluetooth.BluetoothGattService as FwkService
import android.bluetooth.BluetoothManager
import android.bluetooth.BluetoothProfile
+import android.bluetooth.BluetoothStatusCodes
import android.content.Context
import android.os.Build
import androidx.annotation.RequiresPermission
import androidx.annotation.RestrictTo
import androidx.annotation.VisibleForTesting
+import java.util.concurrent.CancellationException
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicInteger
import kotlinx.coroutines.CompletableDeferred
@@ -58,7 +60,7 @@
characteristic: FwkCharacteristic,
confirm: Boolean,
value: ByteArray
- )
+ ): Int?
fun sendResponse(
device: FwkDevice,
requestId: Int,
@@ -286,7 +288,7 @@
override suspend fun notify(
characteristic: GattCharacteristic,
value: ByteArray
- ): Boolean {
+ ) {
notifyMutex.withLock {
CompletableDeferred<Boolean>().also {
notifyJob = it
@@ -295,8 +297,13 @@
characteristic.fwkCharacteristic,
false,
value
- )
- return it.await()
+ ).let { notifyResult ->
+ if (notifyResult != BluetoothStatusCodes.SUCCESS) {
+ throw CancellationException("notify failed with " +
+ "error: {$notifyResult}")
+ }
+ }
+ it.await()
}
}
}
@@ -361,9 +368,11 @@
characteristic: FwkCharacteristic,
confirm: Boolean,
value: ByteArray
- ) {
+ ): Int? {
characteristic.value = value
- gattServer?.notifyCharacteristicChanged(device, characteristic, confirm)
+ return gattServer?.notifyCharacteristicChanged(device, characteristic, confirm)?.let {
+ if (it) BluetoothStatusCodes.SUCCESS else BluetoothStatusCodes.ERROR_UNKNOWN
+ }
}
@RequiresPermission(BLUETOOTH_CONNECT)
@@ -385,8 +394,8 @@
characteristic: FwkCharacteristic,
confirm: Boolean,
value: ByteArray
- ) {
- gattServer?.notifyCharacteristicChanged(device, characteristic, confirm, value)
+ ): Int? {
+ return gattServer?.notifyCharacteristicChanged(device, characteristic, confirm, value)
}
}
}
diff --git a/buildSrc-tests/src/test/java/androidx/build/dependencyTracker/ChangeInfoGitClientTest.kt b/buildSrc-tests/src/test/java/androidx/build/dependencyTracker/ChangeInfoGitClientTest.kt
index aedf44140..c5c6a59 100644
--- a/buildSrc-tests/src/test/java/androidx/build/dependencyTracker/ChangeInfoGitClientTest.kt
+++ b/buildSrc-tests/src/test/java/androidx/build/dependencyTracker/ChangeInfoGitClientTest.kt
@@ -17,9 +17,7 @@
package androidx.build.dependencyTracker
import androidx.build.gitclient.ChangeInfoGitClient
-import androidx.build.gitclient.GitCommitRange
import com.google.gson.JsonSyntaxException
-import java.io.File
import junit.framework.TestCase.assertEquals
import org.gradle.api.GradleException
import org.junit.Test
@@ -234,11 +232,11 @@
</manifest>
""",
"frameworks/support")
- return client.findChangedFilesSince("", "", false)
+ return client.findChangedFilesSince("")
}
@Test
- fun getGitLog_hasVersion() {
+ fun getHeadSha_hasVersion() {
checkVersion("""
<manifest>
<project path="prebuilts/internal" name="platform/prebuilts/internal" revision="prebuiltsVersion1"/>
@@ -251,7 +249,7 @@
}
@Test
- fun getGitLog_noVersion() {
+ fun getHeadSha_noVersion() {
var threw = false
try {
checkVersion("""
@@ -268,9 +266,8 @@
fun checkVersion(config: String, expectedVersion: String?) {
assertEquals(expectedVersion, getVersion(config))
}
- fun getVersion(config: String): String? {
+ fun getVersion(config: String): String {
return ChangeInfoGitClient("{}", config, "frameworks/support")
- .getGitLog(GitCommitRange(n = 1), keepMerges = true, projectDir = File("."))
- .getOrNull(0)?.sha
+ .getHeadSha()
}
}
diff --git a/buildSrc-tests/src/test/java/androidx/build/dependencyTracker/GitRunnerGitClientTest.kt b/buildSrc-tests/src/test/java/androidx/build/dependencyTracker/GitRunnerGitClientTest.kt
index eb24eee..7a5b452 100644
--- a/buildSrc-tests/src/test/java/androidx/build/dependencyTracker/GitRunnerGitClientTest.kt
+++ b/buildSrc-tests/src/test/java/androidx/build/dependencyTracker/GitRunnerGitClientTest.kt
@@ -16,19 +16,18 @@
package androidx.build.dependencyTracker
-import androidx.build.gitclient.Commit
import androidx.build.gitclient.GitClient
import androidx.build.gitclient.GitRunnerGitClient
import androidx.build.gitclient.GitRunnerGitClient.Companion.CHANGED_FILES_CMD_PREFIX
import androidx.build.gitclient.GitRunnerGitClient.Companion.PREVIOUS_SUBMITTED_CMD
-import androidx.build.gitclient.GitCommitRange
+import java.io.File
import junit.framework.TestCase.assertEquals
+import junit.framework.TestCase.assertFalse
import junit.framework.TestCase.assertNull
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
-import java.io.File
@RunWith(JUnit4::class)
class GitRunnerGitClientTest {
@@ -70,12 +69,12 @@
convertToFilePath("a", "b", "c.java"),
convertToFilePath("d", "e", "f.java"))
commandRunner.addReply(
- "$CHANGED_FILES_CMD_PREFIX HEAD..mySha",
+ "$CHANGED_FILES_CMD_PREFIX HEAD mySha",
changes.joinToString(System.lineSeparator())
)
assertEquals(
changes,
- client.findChangedFilesSince(sha = "mySha", includeUncommitted = true))
+ client.findChangedFilesSince(sha = "mySha"))
}
@Test
@@ -86,38 +85,9 @@
}
@Test
- fun findChangesSince_twoCls() {
- var changes = listOf(
- convertToFilePath("a", "b", "c.java"),
- convertToFilePath("d", "e", "f.java"))
- commandRunner.addReply(
- "$CHANGED_FILES_CMD_PREFIX otherSha mySha",
- changes.joinToString(System.lineSeparator())
- )
- assertEquals(
- changes,
- client.findChangedFilesSince(
- sha = "mySha",
- top = "otherSha",
- includeUncommitted = false))
- }
-
- @Test
fun checkLatestCommitExists() {
- /* Do not use the MockCommandRunner because it's a better test to check the validity of
- * the git command against the actual git in the repo
- */
- val commitList: List<Commit> = GitRunnerGitClient(workingDir, logger)
- .getGitLog(
- GitCommitRange(
- fromExclusive = "",
- untilInclusive = "HEAD",
- n = 1
- ),
- keepMerges = true,
- projectDir = workingDir
- )
- assertEquals(1, commitList.size)
+ val headCommit: String = GitRunnerGitClient(workingDir, logger).getHeadSha()
+ assertFalse(headCommit.isEmpty())
}
// For both Linux/Windows
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXGradleProperties.kt b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXGradleProperties.kt
index 24e34dc..0ee112e 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/AndroidXGradleProperties.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/AndroidXGradleProperties.kt
@@ -113,6 +113,11 @@
const val KMP_GITHUB_BUILD = "androidx.github.build"
/**
+ * Specifies to give as much memory to Gradle as in a typical CI run
+ */
+const val HIGH_MEMORY = "androidx.highMemory"
+
+/**
* If true, don't require lint-checks project to exist. This should only be set in integration
* tests, to allow them to save time by not configuring extra projects.
*/
@@ -152,6 +157,7 @@
ENABLE_COMPOSE_COMPILER_REPORTS,
DISPLAY_TEST_OUTPUT,
ENABLE_DOCUMENTATION,
+ HIGH_MEMORY,
STUDIO_TYPE,
SUMMARIZE_STANDARD_ERROR,
USE_MAX_DEP_VERSIONS,
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/gitclient/ChangeInfoGitClient.kt b/buildSrc/private/src/main/kotlin/androidx/build/gitclient/ChangeInfoGitClient.kt
index bfb9427..dba8e59 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/gitclient/ChangeInfoGitClient.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/gitclient/ChangeInfoGitClient.kt
@@ -18,7 +18,6 @@
import androidx.build.parseXml
import com.google.gson.Gson
-import java.io.File
import org.gradle.api.GradleException
/**
@@ -101,16 +100,8 @@
/** Finds changed file paths */
override fun findChangedFilesSince(
- sha: String, // unused in this implementation, the data file knows what is new
- top: String, // unused in this implementation, the data file knows what is new
- includeUncommitted: Boolean // unused in this implementation, not needed yet
+ sha: String // unused in this implementation, the data file knows what is new
): List<String> {
- if (includeUncommitted) {
- throw UnsupportedOperationException(
- "ChangeInfoGitClient does not support includeUncommitted == true yet"
- )
- }
-
val fileList = mutableListOf<String>()
val fileSet = mutableSetOf<String>()
for (change in changesInThisRepo) {
@@ -147,24 +138,8 @@
return ""
}
- /** Finds the commits in a certain range */
- override fun getGitLog(
- gitCommitRange: GitCommitRange,
- keepMerges: Boolean,
- projectDir: File?
- ): List<Commit> {
- if (gitCommitRange.n != 1) {
- throw UnsupportedOperationException(
- "ChangeInfoGitClient only supports n = 1, not ${gitCommitRange.n}"
- )
- }
- if (gitCommitRange.untilInclusive != "HEAD") {
- throw UnsupportedOperationException(
- "ChangeInfoGitClient only supports untilInclusive = HEAD, " +
- "not ${gitCommitRange.untilInclusive}"
- )
- }
- return listOf(Commit("_CommitSHA:${extractVersion(versionInfo)}", projectPath))
+ override fun getHeadSha(): String {
+ return extractVersion(versionInfo)
}
}
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/gitclient/GitClient.kt b/buildSrc/private/src/main/kotlin/androidx/build/gitclient/GitClient.kt
index f6d757f..2b883ba 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/gitclient/GitClient.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/gitclient/GitClient.kt
@@ -17,8 +17,6 @@
package androidx.build.gitclient
import androidx.build.getCheckoutRoot
-import androidx.build.releasenotes.getBuganizerLink
-import androidx.build.releasenotes.getChangeIdAOSPLink
import java.io.File
import java.util.concurrent.ConcurrentHashMap
import org.gradle.api.GradleException
@@ -27,34 +25,12 @@
import org.gradle.api.provider.Provider
interface GitClient {
- fun findChangedFilesSince(
- sha: String,
- top: String = "HEAD",
- includeUncommitted: Boolean = false
- ): List<String>
+ fun findChangedFilesSince(sha: String): List<String>
fun findPreviousSubmittedChange(): String?
- fun getGitLog(
- gitCommitRange: GitCommitRange,
- keepMerges: Boolean,
- projectDir: File?
- ): List<Commit>
-
/** Returns the full commit sha for the HEAD of the git repository */
- fun getHeadSha(): String {
- val projectDir = null
- val commitList: List<Commit> =
- getGitLog(
- GitCommitRange(fromExclusive = "", untilInclusive = "HEAD", n = 1),
- keepMerges = true,
- projectDir = projectDir
- )
- if (commitList.isEmpty()) {
- throw RuntimeException("Failed to find git commit for HEAD!")
- }
- return commitList.first().sha
- }
+ fun getHeadSha(): String
/** Abstraction for running execution commands for testability */
interface CommandRunner {
@@ -154,247 +130,3 @@
}
}
}
-
-enum class CommitType {
- NEW_FEATURE,
- API_CHANGE,
- BUG_FIX,
- EXTERNAL_CONTRIBUTION;
-
- companion object {
- fun getTitle(commitType: CommitType): String {
- return when (commitType) {
- NEW_FEATURE -> "New Features"
- API_CHANGE -> "API Changes"
- BUG_FIX -> "Bug Fixes"
- EXTERNAL_CONTRIBUTION -> "External Contribution"
- }
- }
- }
-}
-
-/**
- * Defines the parameters for a git log command
- *
- * @property fromExclusive the oldest SHA at which the git log starts. Set to an empty string to use
- * [n]
- * @property untilInclusive the latest SHA included in the git log. Defaults to HEAD
- * @property n a count of how many commits to go back to. Only used when [fromExclusive] is an empty
- * string
- */
-data class GitCommitRange(
- val fromExclusive: String = "",
- val untilInclusive: String = "HEAD",
- val n: Int = 0
-)
-
-/**
- * Class implementation of a git commit. It uses the input delimiters to parse the commit
- *
- * @property formattedCommitText a string representation of a git commit
- * @property projectDir the project directory for which to parse file paths from a commit
- * @property commitSHADelimiter the term to use to search for the commit SHA
- * @property subjectDelimiter the term to use to search for the subject (aka commit summary)
- * @property changeIdDelimiter the term to use to search for the change-id in the body of the commit
- * message
- * @property authorEmailDelimiter the term to use to search for the author email
- */
-data class Commit(
- val formattedCommitText: String,
- val projectDir: String,
- private val commitSHADelimiter: String = "_CommitSHA:",
- private val subjectDelimiter: String = "_Subject:",
- private val authorEmailDelimiter: String = "_Author:"
-) {
- private val changeIdDelimiter: String = "Change-Id:"
- var bugs: MutableList<Int> = mutableListOf()
- var files: MutableList<String> = mutableListOf()
- var sha: String = ""
- var authorEmail: String = ""
- var changeId: String = ""
- var summary: String = ""
- var type: CommitType = CommitType.BUG_FIX
- var releaseNote: String = ""
- private val releaseNoteDelimiters: List<String> = listOf("Relnote:")
-
- init {
- val listedCommit: List<String> = formattedCommitText.split('\n')
- listedCommit
- .filter { line -> line.trim() != "" }
- .forEach { line -> processCommitLine(line) }
- }
-
- private fun processCommitLine(line: String) {
- if (commitSHADelimiter in line) {
- getSHAFromGitLine(line)
- return
- }
- if (subjectDelimiter in line) {
- getSummary(line)
- return
- }
- if (changeIdDelimiter in line) {
- getChangeIdFromGitLine(line)
- return
- }
- if (authorEmailDelimiter in line) {
- getAuthorEmailFromGitLine(line)
- return
- }
- if (
- "Bug:" in line ||
- "b/" in line ||
- "bug:" in line ||
- "Fixes:" in line ||
- "fixes b/" in line
- ) {
- getBugsFromGitLine(line)
- return
- }
- releaseNoteDelimiters.forEach { delimiter ->
- if (delimiter in line) {
- getReleaseNotesFromGitLine(line, formattedCommitText)
- return
- }
- }
- if (projectDir.trim('/') in line) {
- getFileFromGitLine(line)
- return
- }
- }
-
- private fun isExternalAuthorEmail(authorEmail: String): Boolean {
- return !(authorEmail.contains("@google.com"))
- }
-
- /**
- * Parses SHAs from git commit line, with the format: [Commit.commitSHADelimiter] <commitSHA>
- */
- private fun getSHAFromGitLine(line: String) {
- sha = line.substringAfter(commitSHADelimiter).trim()
- }
-
- /**
- * Parses subject from git commit line, with the format: [Commit.subjectDelimiter]<commit
- * subject>
- */
- private fun getSummary(line: String) {
- summary = line.substringAfter(subjectDelimiter).trim()
- }
-
- /** Parses commit Change-Id lines, with the format: `commit.changeIdDelimiter` <changeId> */
- private fun getChangeIdFromGitLine(line: String) {
- changeId = line.substringAfter(changeIdDelimiter).trim()
- }
-
- /**
- * Parses commit author lines, with the format: [Commit.authorEmailDelimiter][email protected]
- */
- private fun getAuthorEmailFromGitLine(line: String) {
- authorEmail = line.substringAfter(authorEmailDelimiter).trim()
- if (isExternalAuthorEmail(authorEmail)) {
- type = CommitType.EXTERNAL_CONTRIBUTION
- }
- }
-
- /**
- * Parses filepath to get changed files from commit, with the format:
- * {project_directory}/{filepath}
- */
- private fun getFileFromGitLine(filepath: String) {
- files.add(filepath.trim())
- if (filepath.contains("current.txt") && type != CommitType.EXTERNAL_CONTRIBUTION) {
- type = CommitType.API_CHANGE
- }
- }
-
- /** Parses bugs from a git commit message line */
- private fun getBugsFromGitLine(line: String) {
- var formattedLine = line.replace("b/", " ")
- formattedLine = formattedLine.replace(":", " ")
- formattedLine = formattedLine.replace(",", " ")
- var words: List<String> = formattedLine.split(' ')
- words.forEach { word ->
- var possibleBug: Int? = word.toIntOrNull()
- if (possibleBug != null && possibleBug > 1000) {
- bugs.add(possibleBug)
- }
- }
- }
-
- /**
- * Reads in the release notes field from the git commit message line
- *
- * They can have a couple valid formats:
- *
- * `Release notes: This is a one-line release note` `Release Notes: "This is a multi-line
- * release note. This accounts for the use case where the commit cannot be explained in one
- * line" `release notes: "This is a one-line release note. The quotes can be used this way too"`
- */
- private fun getReleaseNotesFromGitLine(line: String, formattedCommitText: String) {
- /* Account for the use of quotes in a release note line
- * No quotes in the Release Note line means it's a one-line release note
- * If there are quotes, assume it's a multi-line release note
- */
- var quoteCountInRelNoteLine: Int = 0
- line.forEach { character ->
- if (character == '"') {
- quoteCountInRelNoteLine++
- }
- }
- if (quoteCountInRelNoteLine == 0) {
- getOneLineReleaseNotesFromGitLine(line)
- } else {
- releaseNoteDelimiters.forEach { delimiter ->
- if (delimiter in line) {
- // Find the starting quote of the release notes quote block
- var releaseNoteStartIndex = formattedCommitText.lastIndexOf(delimiter)
- +delimiter.length
- releaseNoteStartIndex = formattedCommitText.indexOf('"', releaseNoteStartIndex)
- // Move to the character after the first quote
- if (formattedCommitText[releaseNoteStartIndex] == '"') {
- releaseNoteStartIndex++
- }
- // Find the ending quote of the release notes quote block
- var releaseNoteEndIndex = releaseNoteStartIndex + 1
- releaseNoteEndIndex = formattedCommitText.indexOf('"', releaseNoteEndIndex)
- // If there is no closing quote, just use the first line
- if (releaseNoteEndIndex < 0) {
- getOneLineReleaseNotesFromGitLine(line)
- return
- }
- releaseNote =
- formattedCommitText
- .substring(
- startIndex = releaseNoteStartIndex,
- endIndex = releaseNoteEndIndex
- )
- .trim()
- }
- }
- }
- }
-
- private fun getOneLineReleaseNotesFromGitLine(line: String) {
- releaseNoteDelimiters.forEach { delimiter ->
- if (delimiter in line) {
- releaseNote = line.substringAfter(delimiter).trim(' ', '"')
- return
- }
- }
- }
-
- fun getReleaseNoteString(): String {
- var releaseNoteString: String = releaseNote
- releaseNoteString += " ${getChangeIdAOSPLink(changeId)}"
- bugs.forEach { bug -> releaseNoteString += " ${getBuganizerLink(bug)}" }
- return releaseNoteString
- }
-
- override fun toString(): String {
- var commitString: String = summary
- commitString += " ${getChangeIdAOSPLink(changeId)}"
- bugs.forEach { bug -> commitString += " ${getBuganizerLink(bug)}" }
- return commitString
- }
-}
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/gitclient/GitRunnerGitClient.kt b/buildSrc/private/src/main/kotlin/androidx/build/gitclient/GitRunnerGitClient.kt
index abb6a77..85b929d 100644
--- a/buildSrc/private/src/main/kotlin/androidx/build/gitclient/GitRunnerGitClient.kt
+++ b/buildSrc/private/src/main/kotlin/androidx/build/gitclient/GitRunnerGitClient.kt
@@ -32,22 +32,9 @@
RealCommandRunner(workingDir = workingDir, logger = logger)
) : GitClient {
- private val gitRoot: File = findGitDirInParentFilepath(workingDir) ?: workingDir
-
- /** Finds changed file paths since the given sha */
- override fun findChangedFilesSince(
- sha: String,
- top: String,
- includeUncommitted: Boolean
- ): List<String> {
- // use this if we don't want local changes
- return commandRunner.executeAndParse(
- if (includeUncommitted) {
- "$CHANGED_FILES_CMD_PREFIX HEAD..$sha"
- } else {
- "$CHANGED_FILES_CMD_PREFIX $top $sha"
- }
- )
+ /** Finds file paths changed in a commit since the given sha */
+ override fun findChangedFilesSince(sha: String): List<String> {
+ return commandRunner.executeAndParse("$CHANGED_FILES_CMD_PREFIX HEAD $sha")
}
/** checks the history to find the first merge CL. */
@@ -59,99 +46,15 @@
?.firstOrNull()
}
- private fun parseCommitLogString(
- commitLogString: String,
- commitStartDelimiter: String,
- commitSHADelimiter: String,
- subjectDelimiter: String,
- authorEmailDelimiter: String,
- localProjectDir: String
- ): List<Commit> {
- // Split commits string out into individual commits (note: this removes the deliminter)
- val gitLogStringList: List<String>? = commitLogString.split(commitStartDelimiter)
- var commitLog: MutableList<Commit> = mutableListOf()
- gitLogStringList
- ?.filter { gitCommit -> gitCommit.trim() != "" }
- ?.forEach { gitCommit ->
- commitLog.add(
- Commit(
- gitCommit,
- localProjectDir,
- commitSHADelimiter = commitSHADelimiter,
- subjectDelimiter = subjectDelimiter,
- authorEmailDelimiter = authorEmailDelimiter
- )
- )
- }
- return commitLog.toList()
- }
-
- /**
- * Converts a diff log command into a [List<Commit>]
- *
- * @param gitCommitRange the [GitCommitRange] that defines the parameters of the git log command
- * @param keepMerges boolean for whether or not to add merges to the return [List<Commit>].
- * @param projectDir a [File] object that represents the project directory.
- */
- override fun getGitLog(
- gitCommitRange: GitCommitRange,
- keepMerges: Boolean,
- projectDir: File?
- ): List<Commit> {
- val commitStartDelimiter: String = "_CommitStart"
- val commitSHADelimiter: String = "_CommitSHA:"
- val subjectDelimiter: String = "_Subject:"
- val authorEmailDelimiter: String = "_Author:"
- val dateDelimiter: String = "_Date:"
- val bodyDelimiter: String = "_Body:"
- val fullProjectDir = if (projectDir == null) workingDir else projectDir
- val localProjectDir: String = fullProjectDir.relativeTo(gitRoot).toString()
- val relativeProjectDir: String = fullProjectDir.relativeTo(workingDir).toString()
-
- var gitLogOptions: String =
- "--pretty=format:$commitStartDelimiter%n" +
- "$commitSHADelimiter%H%n" +
- "$authorEmailDelimiter%ae%n" +
- "$dateDelimiter%ad%n" +
- "$subjectDelimiter%s%n" +
- "$bodyDelimiter%b" +
- if (!keepMerges) {
- " --no-merges"
- } else {
- ""
- }
- var gitLogCmd: String
- if (gitCommitRange.fromExclusive != "") {
- gitLogCmd =
- "$GIT_LOG_CMD_PREFIX $gitLogOptions " +
- "${gitCommitRange.fromExclusive}..${gitCommitRange.untilInclusive}" +
- " -- ./$relativeProjectDir"
- } else {
- gitLogCmd =
- "$GIT_LOG_CMD_PREFIX $gitLogOptions ${gitCommitRange.untilInclusive} -n " +
- "${gitCommitRange.n} -- ./$relativeProjectDir"
- }
+ override fun getHeadSha(): String {
+ val gitLogCmd = "git log --name-only --pretty=format:%H HEAD -n 1 -- ./"
val gitLogString: String = commandRunner.execute(gitLogCmd)
- val commits =
- parseCommitLogString(
- gitLogString,
- commitStartDelimiter,
- commitSHADelimiter,
- subjectDelimiter,
- authorEmailDelimiter,
- localProjectDir
- )
- if (commits.isEmpty()) {
- // Probably an error; log this
+ if (gitLogString.isEmpty()) {
logger?.warn(
- "No git commits found! Ran this command: '" +
- gitLogCmd +
- "' and received this output: '" +
- gitLogString +
- "'"
+ "No git commits found! Ran this command: '$gitLogCmd ' and received no output"
)
}
- return commits
+ return gitLogString
}
private class RealCommandRunner(private val workingDir: File, private val logger: Logger?) :
@@ -181,15 +84,13 @@
}
override fun executeAndParse(command: String): List<String> {
- val response = execute(command).split(System.lineSeparator()).filterNot { it.isEmpty() }
- return response
+ return execute(command).split(System.lineSeparator()).filterNot { it.isEmpty() }
}
}
companion object {
const val PREVIOUS_SUBMITTED_CMD = "git log -1 --merges --oneline"
const val CHANGED_FILES_CMD_PREFIX = "git diff --name-only"
- const val GIT_LOG_CMD_PREFIX = "git log --name-only"
}
}
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/releasenotes/Markdown.kt b/buildSrc/private/src/main/kotlin/androidx/build/releasenotes/Markdown.kt
deleted file mode 100644
index b1f3e6f0..0000000
--- a/buildSrc/private/src/main/kotlin/androidx/build/releasenotes/Markdown.kt
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.build.releasenotes
-
-/** Classes for generating markdown */
-enum class HeaderType {
- H1,
- H2,
- H3,
- H4,
- H5,
- H6;
-
- companion object {
- fun getHeaderTag(tag: HeaderType): String {
- when (tag) {
- H1 -> return ("#")
- H2 -> return ("##")
- H3 -> return ("###")
- H4 -> return ("####")
- H5 -> return ("#####")
- H6 -> return ("######")
- }
- }
- }
-}
-
-open class MarkdownHeader {
- var markdownType: HeaderType = HeaderType.H1
- var text: String = ""
-
- @Override
- override fun toString(): String {
- return HeaderType.getHeaderTag(markdownType) + ' ' + text
- }
-
- fun print() {
- println(toString())
- }
-}
-
-open class MarkdownLink {
- var linkText: String = ""
- var linkUrl: String = ""
-
- @Override
- override fun toString(): String {
- return "([$linkText]($linkUrl))"
- }
-
- fun print() {
- println(toString())
- }
-}
-
-open class MarkdownBoldText(inputText: String) {
- var text: String = ""
-
- init {
- text = inputText
- }
-
- override fun toString(): String {
- return "**$text**"
- }
-
- fun print() {
- println(toString())
- }
-}
-
-open class MarkdownComment(inputText: String) {
- var text: String = ""
-
- init {
- text = inputText
- }
-
- override fun toString(): String {
- return "{# $text #}"
- }
-
- fun print() {
- println(toString())
- }
-}
diff --git a/buildSrc/private/src/main/kotlin/androidx/build/releasenotes/ReleaseNoteMarkdown.kt b/buildSrc/private/src/main/kotlin/androidx/build/releasenotes/ReleaseNoteMarkdown.kt
deleted file mode 100644
index ce85adf..0000000
--- a/buildSrc/private/src/main/kotlin/androidx/build/releasenotes/ReleaseNoteMarkdown.kt
+++ /dev/null
@@ -1,288 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.build.releasenotes
-
-import androidx.build.gitclient.Commit
-import androidx.build.gitclient.CommitType
-import java.time.LocalDate
-import java.time.format.DateTimeFormatter
-
-/** Classes for generating androidx release note specific markdown */
-
-/**
- * Markdown class for a Library Header in the format:
- *
- * ### Version <version> {:#<version>}
- */
-class LibraryHeader(groupId: String, version: String) : MarkdownHeader() {
- init {
- markdownType = HeaderType.H3
- text = "$groupId Version $version {:#$version}"
- }
-}
-
-/**
- * Generates the markdown list of commits with sections defined by enum [CommitType], in the format:
- *
- * **New Features**
- * - <[Commit.summary]> <[getChangeIdAOSPLink]> <[getBuganizerLink] 1> <[getBuganizerLink] 2>...
- *
- * **API Changes**
- * - <[Commit.summary]> <[getChangeIdAOSPLink]> <[getBuganizerLink] 1> <[getBuganizerLink] 2>...
- *
- * **Bug Fixes**
- * - <[Commit.summary]> <[getChangeIdAOSPLink]> <[getBuganizerLink] 1> <[getBuganizerLink] 2>...
- *
- * **External Contribution**
- * - <[Commit.summary]> <[getChangeIdAOSPLink]> <[getBuganizerLink] 1> <[getBuganizerLink] 2>...
- */
-class CommitMarkdownList(private var includeAllCommits: Boolean = false) {
- private var commits: MutableList<Commit> = mutableListOf()
-
- fun add(commit: Commit) {
- commits.add(commit)
- }
-
- fun getListItemStr(): String {
- return "- "
- }
-
- private fun makeReleaseNotesSection(sectionCommitType: CommitType): String {
- var sectionHeader: MarkdownBoldText =
- MarkdownBoldText(CommitType.getTitle(sectionCommitType))
- var markdownStringSection: String = ""
- commits
- .filter { commit -> commit.type == sectionCommitType }
- .forEach { commit ->
- // While we are choosing to ignore Release Note field
- val commitString: String =
- getListItemStr() +
- if (commit.releaseNote.isNotEmpty()) commit.getReleaseNoteString()
- else commit.toString()
- if (includeAllCommits || commit.releaseNote.isNotEmpty()) {
- markdownStringSection = markdownStringSection + commitString
- if (markdownStringSection.last() != '\n') {
- markdownStringSection += '\n'
- }
- }
- /* If we are not ignoring Release Note fields (meaning we are respecting it) and
- * the commit does not contain a Release Note field, then don't include the commit
- * in the release notes.
- */
- }
- markdownStringSection =
- if (markdownStringSection.isEmpty()) {
- "\n${MarkdownComment(sectionHeader.toString())}\n\n$markdownStringSection"
- } else {
- "\n$sectionHeader\n\n$markdownStringSection"
- }
- return markdownStringSection
- }
-
- @Override
- override fun toString(): String {
- var markdownString: String = ""
- CommitType.values().forEach { commitType ->
- markdownString += makeReleaseNotesSection(commitType)
- }
- return markdownString
- }
-
- fun print() {
- println(toString())
- }
-}
-
-/**
- * @param startSHA the SHA at which to start the diff log (exclusive)
- * @param endSHA the last SHA to include in the diff log (inclusive)
- * @param projectDir the local directory of the project, in relation to frameworks/support
- * @return A [MarkdownLink] to the public Gitiles diff log
- */
-fun getGitilesDiffLogLink(startSHA: String, endSHA: String, projectDir: String): MarkdownLink {
- val baseGitilesUrl: String =
- "https://android.googlesource.com/platform/frameworks/support/+log/"
- /* The root project directory is already existent in the url path, so the directory here
- * should be relative to frameworks/support/.
- */
- if (projectDir.contains("frameworks/support")) {
- throw RuntimeException(
- "Gitiles directory should only contain the directory structure" +
- "within frameworks/support/*, but received incorrect directory: $projectDir"
- )
- }
- // Remove extra preceeding directory slashes, if they exist
- var verifiedProjectDir = projectDir
- while (verifiedProjectDir.first() == '/') {
- verifiedProjectDir = verifiedProjectDir.removePrefix("/")
- }
- var gitilesLink: MarkdownLink = MarkdownLink()
- gitilesLink.linkText = "here"
- gitilesLink.linkUrl = "$baseGitilesUrl$startSHA..$endSHA/$verifiedProjectDir"
- return gitilesLink
-}
-
-/**
- * @param changeId The Gerrit Change-Id to link to
- * @return A [MarkdownLink] to AOSP Gerrit
- */
-fun getChangeIdAOSPLink(changeId: String): MarkdownLink {
- val baseAOSPUrl: String = "https://android-review.googlesource.com/#/q/"
- var aospLink: MarkdownLink = MarkdownLink()
- aospLink.linkText = changeId.take(6)
- aospLink.linkUrl = "$baseAOSPUrl$changeId"
- return aospLink
-}
-
-/**
- * @param bugId the Id of the buganizer issue
- * @return A [MarkdownLink] to the public buganizer issue tracker
- *
- * Note: This method does not check if the bug is public
- */
-fun getBuganizerLink(bugId: Int): MarkdownLink {
- val baseBuganizerUrl: String = "https://issuetracker.google.com/issues/"
- var buganizerLink: MarkdownLink = MarkdownLink()
- buganizerLink.linkText = "b/$bugId"
- buganizerLink.linkUrl = "$baseBuganizerUrl$bugId"
- return buganizerLink
-}
-
-/**
- * Data class to contain an array of LibraryReleaseNotes when serializing collections of release
- * notes
- */
-data class LibraryReleaseNotesList(val list: MutableList<LibraryReleaseNotes> = mutableListOf())
-
-/**
- * Structured release notes class, that connects all parts of the release notes. Create release
- * notes in the format:
- * <pre>
- * <[LibraryHeader]>
- * <Date>
- *
- * `androidx.<groupId>:<artifactId>:<version>` is released. The commits included in this version
- * can be found <[MarkdownLink]>.
- *
- * <[CommitMarkdownList]>
- * </pre>
- *
- * @param includeAllCommits Set to true to include all commits, both with and without a release note
- * field in the commit message. Defaults to false, which means only commits with a release note
- * field are included in the release notes.
- * @property groupId Library GroupId.
- * @property artifactIds List of ArtifactIds included in these release notes.
- * @property version Version of the library, assuming all artifactIds have the same version.
- * @property releaseDate Date the release will go live. Defaults to the current date.
- * @property fromSHA The oldest SHA to include in the release notes.
- * @property untilSHA The newest SHA to be included in the release notes.
- * @property projectDir The filepath relative to the parent directory of the .git directory.
- * @property commitList The initial list of Commits to include in these release notes. Defaults to
- * an empty list. Users can always add more commits with [LibraryReleaseNotes.addCommit]
- * @property requiresSameVersion True if the groupId of this module requires the same version for
- * all artifactIds in the groupId. When true, uses the GroupId for the release notes header. When
- * false, uses the list of artifactIds for the header.
- */
-class LibraryReleaseNotes(
- val groupId: String,
- val artifactIds: MutableList<String>,
- val version: String,
- val releaseDate: LocalDate,
- val fromSHA: String,
- val untilSHA: String,
- val projectDir: String,
- val commitList: List<Commit> = listOf(),
- val requiresSameVersion: Boolean,
- includeAllCommits: Boolean = false
-) {
- private var diffLogLink: MarkdownLink
- private var header: LibraryHeader
- private var commits: MutableList<Commit> = mutableListOf()
- private var commitMarkdownList: CommitMarkdownList = CommitMarkdownList(includeAllCommits)
- private var summary: String = ""
- private var bugsFixed: MutableList<Int> = mutableListOf()
-
- init {
- if (version == "" || groupId == "") {
- throw RuntimeException(
- "Tried to create Library Release Notes Header without setting" +
- "the groupId or version!"
- )
- }
- if (fromSHA == "" || untilSHA == "") {
- throw RuntimeException("Tried to create Library Release Notes with an empty SHA!")
- }
- header =
- if (requiresSameVersion) {
- LibraryHeader(groupId, version)
- } else {
- LibraryHeader(artifactIds.joinToString(), version)
- }
- diffLogLink = getGitilesDiffLogLink(fromSHA, untilSHA, projectDir)
- if (commitList.isNotEmpty()) {
- commitList.forEach { commit -> addCommit(commit) }
- }
- }
-
- fun getFormattedDate(): String {
- val formatter = DateTimeFormatter.ofPattern("MMMM d, yyyy")
- return formatter.format(releaseDate)
- }
-
- fun getFormattedReleaseSummary(): String {
- val numberArtifacts = artifactIds.size
- for (i: Int in 0..(numberArtifacts - 1)) {
- var currentArtifactId: String = artifactIds[i]
- when (numberArtifacts) {
- 1 -> {
- summary = "`$groupId:$currentArtifactId:$version` is released. "
- }
- 2 -> {
- if (i == 0) {
- summary = "`$groupId:$currentArtifactId:$version` and "
- }
- if (i == 1) {
- summary += "`$groupId:$currentArtifactId:$version` are released. "
- }
- }
- else -> {
- if (i < numberArtifacts - 1) {
- summary += "`$groupId:$currentArtifactId:$version`, "
- } else {
- summary += "and `$groupId:$currentArtifactId:$version` are released. "
- }
- }
- }
- }
-
- summary += "The commits included in this version can be found $diffLogLink.\n"
- return summary
- }
-
- fun addCommit(newCommit: Commit) {
- newCommit.bugs.forEach { bug -> bugsFixed.add(bug) }
- commits.add(newCommit)
- commitMarkdownList.add(newCommit)
- }
-
- override fun toString(): String {
- return "$header\n" +
- "${getFormattedDate()}\n\n" +
- getFormattedReleaseSummary() +
- "$commitMarkdownList"
- }
-}
diff --git a/busytown/androidx_with_metalava.sh b/busytown/androidx_with_metalava.sh
index 156fc64..5084b28 100755
--- a/busytown/androidx_with_metalava.sh
+++ b/busytown/androidx_with_metalava.sh
@@ -2,6 +2,13 @@
set -e
SCRIPT_PATH="$(cd $(dirname $0) && pwd)"
+# Use this flag to temporarily disable `checkApi`
+# while landing Metalava w/ breaking API changes
+DURING_METALAVA_UPDATE=true
+
+if [ ! $DURING_METALAVA_UPDATE ]
+then
$SCRIPT_PATH/impl/build-metalava-and-androidx.sh \
listTaskOutputs \
checkApi
+fi
diff --git a/busytown/impl/build-metalava-and-androidx.sh b/busytown/impl/build-metalava-and-androidx.sh
index c2652ba..597edf1 100755
--- a/busytown/impl/build-metalava-and-androidx.sh
+++ b/busytown/impl/build-metalava-and-androidx.sh
@@ -44,6 +44,9 @@
buildMetalava
+# allow androidx build to reach the network for any new metalava dependencies
+export ALLOW_PUBLIC_REPOS=true
+
# Mac grep doesn't support -P, so use perl version of `grep -oP "(?<=metalavaVersion=).*"`
METALAVA_VERSION_FILE="$METALAVA_DIR/version.properties"
export METALAVA_VERSION=`perl -nle'print $& while m{(?<=metalavaVersion=).*}g' $METALAVA_VERSION_FILE`
diff --git a/busytown/impl/build-studio-and-androidx.sh b/busytown/impl/build-studio-and-androidx.sh
index 6f97a00..ceb9186 100755
--- a/busytown/impl/build-studio-and-androidx.sh
+++ b/busytown/impl/build-studio-and-androidx.sh
@@ -27,6 +27,12 @@
mkdir -p "$DIST_DIR"
export DIST_DIR="$DIST_DIR"
+if [ "$CHANGE_INFO" != "" ]; then
+ cp "$CHANGE_INFO" "$DIST_DIR/"
+fi
+if [ "$MANIFEST" == "" ]; then
+ export MANIFEST="$DIST_DIR/manifest_${BUILD_NUMBER}.xml"
+fi
# resolve GRADLE_USER_HOME
export GRADLE_USER_HOME="$OUT_DIR/gradle"
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/Exif.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/Exif.java
index b26e75f..729fa09 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/Exif.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/Exif.java
@@ -50,6 +50,9 @@
/** Timestamp value indicating a timestamp value that is either not set or not valid */
public static final long INVALID_TIMESTAMP = -1;
+ // Forked from ExifInterface.TAG_THUMBNAIL_ORIENTATION. The value is library-internal so we
+ // can't depend on it directly.
+ public static final String TAG_THUMBNAIL_ORIENTATION = "ThumbnailOrientation";
private static final String TAG = Exif.class.getSimpleName();
@@ -94,7 +97,7 @@
ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH,
ExifInterface.TAG_THUMBNAIL_IMAGE_LENGTH,
ExifInterface.TAG_THUMBNAIL_IMAGE_WIDTH,
- ExifInterface.TAG_THUMBNAIL_ORIENTATION);
+ TAG_THUMBNAIL_ORIENTATION);
private final ExifInterface mExifInterface;
@@ -863,7 +866,7 @@
ExifInterface.TAG_INTEROPERABILITY_INDEX,
ExifInterface.TAG_THUMBNAIL_IMAGE_LENGTH,
ExifInterface.TAG_THUMBNAIL_IMAGE_WIDTH,
- ExifInterface.TAG_THUMBNAIL_ORIENTATION,
+ TAG_THUMBNAIL_ORIENTATION,
ExifInterface.TAG_DNG_VERSION,
ExifInterface.TAG_DEFAULT_CROP_SIZE,
ExifInterface.TAG_ORF_THUMBNAIL_IMAGE,
diff --git a/collection/collection/src/commonMain/kotlin/androidx/collection/PackingUtils.kt b/collection/collection/src/commonMain/kotlin/androidx/collection/PackingUtils.kt
index 00d1b22..48b8171 100644
--- a/collection/collection/src/commonMain/kotlin/androidx/collection/PackingUtils.kt
+++ b/collection/collection/src/commonMain/kotlin/androidx/collection/PackingUtils.kt
@@ -21,8 +21,8 @@
* Packs two Float values into one Long value for use in inline classes.
*/
internal inline fun packFloats(val1: Float, val2: Float): Long {
- val v1 = val1.toBits().toLong()
- val v2 = val2.toBits().toLong()
+ val v1 = val1.toRawBits().toLong()
+ val v2 = val2.toRawBits().toLong()
return (v1 shl 32) or (v2 and 0xFFFFFFFF)
}
diff --git a/compose/animation/animation-core/api/current.txt b/compose/animation/animation-core/api/current.txt
index bb16d4a..a3b72cd 100644
--- a/compose/animation/animation-core/api/current.txt
+++ b/compose/animation/animation-core/api/current.txt
@@ -438,6 +438,9 @@
@SuppressCompatibility @kotlin.RequiresOptIn(message="This API is internal to library.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY, kotlin.annotation.AnnotationTarget.FIELD, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER}) public @interface InternalAnimationApi {
}
+ public abstract sealed class KeyframeBaseEntity<T> {
+ }
+
@androidx.compose.runtime.Immutable public final class KeyframesSpec<T> implements androidx.compose.animation.core.DurationBasedAnimationSpec<T> {
ctor public KeyframesSpec(androidx.compose.animation.core.KeyframesSpec.KeyframesSpecConfig<T> config);
method public androidx.compose.animation.core.KeyframesSpec.KeyframesSpecConfig<T> getConfig();
@@ -445,18 +448,22 @@
property public final androidx.compose.animation.core.KeyframesSpec.KeyframesSpecConfig<T> config;
}
- public static final class KeyframesSpec.KeyframeEntity<T> {
+ public static final class KeyframesSpec.KeyframeEntity<T> extends androidx.compose.animation.core.KeyframeBaseEntity<T> {
}
- public static final class KeyframesSpec.KeyframesSpecConfig<T> {
+ public static final class KeyframesSpec.KeyframesSpecConfig<T> extends androidx.compose.animation.core.KeyframesSpecBaseConfig<T,androidx.compose.animation.core.KeyframesSpec.KeyframeEntity<T>> {
ctor public KeyframesSpec.KeyframesSpecConfig();
- method public infix androidx.compose.animation.core.KeyframesSpec.KeyframeEntity<T> at(T, @IntRange(from=0L) int timeStamp);
- method public infix androidx.compose.animation.core.KeyframesSpec.KeyframeEntity<T> atFraction(T, float fraction);
- method @IntRange(from=0L) public int getDelayMillis();
- method @IntRange(from=0L) public int getDurationMillis();
- method public void setDelayMillis(int);
- method public void setDurationMillis(int);
- method public infix void with(androidx.compose.animation.core.KeyframesSpec.KeyframeEntity<T>, androidx.compose.animation.core.Easing easing);
+ method @Deprecated public infix void with(androidx.compose.animation.core.KeyframesSpec.KeyframeEntity<T>, androidx.compose.animation.core.Easing easing);
+ }
+
+ public abstract sealed class KeyframesSpecBaseConfig<T, E extends androidx.compose.animation.core.KeyframeBaseEntity<T>> {
+ method public final infix E at(T, @IntRange(from=0L) int timeStamp);
+ method public final infix E atFraction(T, float fraction);
+ method @IntRange(from=0L) public final int getDelayMillis();
+ method @IntRange(from=0L) public final int getDurationMillis();
+ method public final void setDelayMillis(int);
+ method public final void setDurationMillis(int);
+ method public final infix E using(E, androidx.compose.animation.core.Easing easing);
property @IntRange(from=0L) public final int delayMillis;
property @IntRange(from=0L) public final int durationMillis;
}
diff --git a/compose/animation/animation-core/api/restricted_current.txt b/compose/animation/animation-core/api/restricted_current.txt
index b454084..aed261f 100644
--- a/compose/animation/animation-core/api/restricted_current.txt
+++ b/compose/animation/animation-core/api/restricted_current.txt
@@ -438,6 +438,9 @@
@SuppressCompatibility @kotlin.RequiresOptIn(message="This API is internal to library.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.CLASS, kotlin.annotation.AnnotationTarget.FUNCTION, kotlin.annotation.AnnotationTarget.PROPERTY, kotlin.annotation.AnnotationTarget.FIELD, kotlin.annotation.AnnotationTarget.PROPERTY_GETTER}) public @interface InternalAnimationApi {
}
+ public abstract sealed class KeyframeBaseEntity<T> {
+ }
+
@androidx.compose.runtime.Immutable public final class KeyframesSpec<T> implements androidx.compose.animation.core.DurationBasedAnimationSpec<T> {
ctor public KeyframesSpec(androidx.compose.animation.core.KeyframesSpec.KeyframesSpecConfig<T> config);
method public androidx.compose.animation.core.KeyframesSpec.KeyframesSpecConfig<T> getConfig();
@@ -445,18 +448,22 @@
property public final androidx.compose.animation.core.KeyframesSpec.KeyframesSpecConfig<T> config;
}
- public static final class KeyframesSpec.KeyframeEntity<T> {
+ public static final class KeyframesSpec.KeyframeEntity<T> extends androidx.compose.animation.core.KeyframeBaseEntity<T> {
}
- public static final class KeyframesSpec.KeyframesSpecConfig<T> {
+ public static final class KeyframesSpec.KeyframesSpecConfig<T> extends androidx.compose.animation.core.KeyframesSpecBaseConfig<T,androidx.compose.animation.core.KeyframesSpec.KeyframeEntity<T>> {
ctor public KeyframesSpec.KeyframesSpecConfig();
- method public infix androidx.compose.animation.core.KeyframesSpec.KeyframeEntity<T> at(T, @IntRange(from=0L) int timeStamp);
- method public infix androidx.compose.animation.core.KeyframesSpec.KeyframeEntity<T> atFraction(T, float fraction);
- method @IntRange(from=0L) public int getDelayMillis();
- method @IntRange(from=0L) public int getDurationMillis();
- method public void setDelayMillis(int);
- method public void setDurationMillis(int);
- method public infix void with(androidx.compose.animation.core.KeyframesSpec.KeyframeEntity<T>, androidx.compose.animation.core.Easing easing);
+ method @Deprecated public infix void with(androidx.compose.animation.core.KeyframesSpec.KeyframeEntity<T>, androidx.compose.animation.core.Easing easing);
+ }
+
+ public abstract sealed class KeyframesSpecBaseConfig<T, E extends androidx.compose.animation.core.KeyframeBaseEntity<T>> {
+ method public final infix E at(T, @IntRange(from=0L) int timeStamp);
+ method public final infix E atFraction(T, float fraction);
+ method @IntRange(from=0L) public final int getDelayMillis();
+ method @IntRange(from=0L) public final int getDurationMillis();
+ method public final void setDelayMillis(int);
+ method public final void setDurationMillis(int);
+ method public final infix E using(E, androidx.compose.animation.core.Easing easing);
property @IntRange(from=0L) public final int delayMillis;
property @IntRange(from=0L) public final int durationMillis;
}
diff --git a/compose/animation/animation-core/build.gradle b/compose/animation/animation-core/build.gradle
index 09f7c49..7c19839 100644
--- a/compose/animation/animation-core/build.gradle
+++ b/compose/animation/animation-core/build.gradle
@@ -40,6 +40,7 @@
implementation(project(":compose:ui:ui"))
implementation(project(":compose:ui:ui-unit"))
implementation(project(":compose:ui:ui-util"))
+ implementation(project(":collection:collection"))
implementation(libs.kotlinStdlibCommon)
api(libs.kotlinCoroutinesCore)
}
diff --git a/compose/animation/animation-core/samples/src/main/java/androidx/compose/animation/core/samples/InfiniteTransitionSamples.kt b/compose/animation/animation-core/samples/src/main/java/androidx/compose/animation/core/samples/InfiniteTransitionSamples.kt
index 1977ca9..4973e4f 100644
--- a/compose/animation/animation-core/samples/src/main/java/androidx/compose/animation/core/samples/InfiniteTransitionSamples.kt
+++ b/compose/animation/animation-core/samples/src/main/java/androidx/compose/animation/core/samples/InfiniteTransitionSamples.kt
@@ -109,7 +109,7 @@
animation = keyframes {
durationMillis = 500
0.dp at 200 // ms
- 80.dp at 300 with FastOutLinearInEasing
+ 80.dp at 300 using FastOutLinearInEasing
}
// Use the default RepeatMode.Restart to start from 0.dp after each iteration
)
diff --git a/compose/animation/animation-core/samples/src/main/java/androidx/compose/animation/core/samples/KeyframesBuilderSample.kt b/compose/animation/animation-core/samples/src/main/java/androidx/compose/animation/core/samples/KeyframesBuilderSample.kt
index 6839179..b461a6e 100644
--- a/compose/animation/animation-core/samples/src/main/java/androidx/compose/animation/core/samples/KeyframesBuilderSample.kt
+++ b/compose/animation/animation-core/samples/src/main/java/androidx/compose/animation/core/samples/KeyframesBuilderSample.kt
@@ -19,6 +19,7 @@
import androidx.annotation.Sampled
import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.KeyframesSpec
+import androidx.compose.animation.core.KeyframesSpecBaseConfig
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.LinearOutSlowInEasing
import androidx.compose.animation.core.keyframes
@@ -55,8 +56,8 @@
// time between 50 and 100ms
keyframes<Float> {
durationMillis = 100
- 0f at 0 with FastOutSlowInEasing
- 1.5f at 50 with LinearOutSlowInEasing
+ 0f at 0 using FastOutSlowInEasing
+ 1.5f at 50 using LinearOutSlowInEasing
1f at 100
}
}
@@ -67,8 +68,21 @@
// time between 50 and 100ms
keyframes<DpOffset> {
durationMillis = 200
- DpOffset(0.dp, 0.dp) at 0 with LinearEasing
- DpOffset(500.dp, 100.dp) at 100 with LinearOutSlowInEasing
+ DpOffset(0.dp, 0.dp) at 0 using LinearEasing
+ DpOffset(500.dp, 100.dp) at 100 using LinearOutSlowInEasing
DpOffset(400.dp, 50.dp) at 150
}
}
+
+@Sampled
+fun KeyframesSpecBaseConfig<Float, KeyframesSpec.KeyframeEntity<Float>>.floatAtSample() {
+ 0.8f at 150 // ms
+}
+
+@Sampled
+fun KeyframesSpecBaseConfig<Float, KeyframesSpec.KeyframeEntity<Float>>.floatAtFractionSample() {
+ // Make sure to set the duration before calling `atFraction` otherwise the keyframe will be set
+ // based on the default duration
+ durationMillis = 300
+ 0.8f atFraction 0.50f // half of the overall duration set
+}
diff --git a/compose/animation/animation-core/src/androidUnitTest/kotlin/androidx/compose/animation/core/KeyframeAnimationTest.kt b/compose/animation/animation-core/src/androidUnitTest/kotlin/androidx/compose/animation/core/KeyframeAnimationTest.kt
index dc1a67a..072294a 100644
--- a/compose/animation/animation-core/src/androidUnitTest/kotlin/androidx/compose/animation/core/KeyframeAnimationTest.kt
+++ b/compose/animation/animation-core/src/androidUnitTest/kotlin/androidx/compose/animation/core/KeyframeAnimationTest.kt
@@ -78,7 +78,7 @@
val easing = FastOutSlowInEasing
val animation = keyframes<Float> {
durationMillis = 100
- 0f at 0 with easing
+ 0f at 0 using easing
1f at durationMillis
}.vectorize(Float.VectorConverter)
@@ -90,7 +90,7 @@
val easing = FastOutSlowInEasing
val animation = keyframes<Float> {
durationMillis = 200
- 1f at 100 with easing
+ 1f at 100 using easing
2f at durationMillis
}.vectorize(Float.VectorConverter)
@@ -101,7 +101,7 @@
fun firstPartIsLinearWithEasingOnTheSecondPart() {
val animation = keyframes<Float> {
durationMillis = 100
- 0.5f at 50 with FastOutSlowInEasing
+ 0.5f at 50 using FastOutSlowInEasing
1f at durationMillis
}.vectorize(Float.VectorConverter)
@@ -113,7 +113,7 @@
val easing = FastOutLinearInEasing
val animation = keyframes<AnimationVector2D> {
durationMillis = 400
- AnimationVector(200f, 300f) at 200 with easing
+ AnimationVector(200f, 300f) at 200 using easing
}.vectorize(TwoWayConverter<AnimationVector2D, AnimationVector2D>({ it }, { it }))
val start = AnimationVector(0f, 0f)
@@ -144,7 +144,7 @@
val config: KeyframesSpec.KeyframesSpecConfig<Float>.() -> Unit = {
durationMillis = 500
0f at 100
- 0.5f at 200 with FastOutLinearInEasing
+ 0.5f at 200 using FastOutLinearInEasing
0.8f at 300
1f at durationMillis
}
@@ -156,7 +156,7 @@
val animationRedeclareConfig = keyframes<Float> {
durationMillis = 500
0f at 100
- 0.5f at 200 with FastOutLinearInEasing
+ 0.5f at 200 using FastOutLinearInEasing
0.8f at 300
1f at durationMillis
}
@@ -174,7 +174,7 @@
val animation = keyframes<Float> {
durationMillis = 500
0f at 100
- 0.5f at 200 with FastOutLinearInEasing
+ 0.5f at 200 using FastOutLinearInEasing
0.8f at 300
1f at durationMillis
}
@@ -182,14 +182,14 @@
val animationAlteredDuration = keyframes<Float> {
durationMillis = 700
0f at 100
- 0.5f at 200 with FastOutLinearInEasing
+ 0.5f at 200 using FastOutLinearInEasing
0.8f at 300
1f at durationMillis
}
val animationAlteredEasing = keyframes<Float> {
durationMillis = 500
- 0f at 100 with FastOutSlowInEasing
+ 0f at 100 using FastOutSlowInEasing
0.5f at 200
0.8f at 300
1f at durationMillis
@@ -198,7 +198,7 @@
val animationAlteredKeyframes = keyframes<Float> {
durationMillis = 500
0f at 100
- 0.3f at 200 with FastOutLinearInEasing
+ 0.3f at 200 using FastOutLinearInEasing
0.8f at 400
1f at durationMillis
}
@@ -230,7 +230,7 @@
fun percentageBasedKeyframesWithEasing() {
val animation = keyframes<Float> {
durationMillis = 100
- 0.5f atFraction 0.5f with FastOutSlowInEasing
+ 0.5f atFraction 0.5f using FastOutSlowInEasing
1f atFraction 1f
}.vectorize(Float.VectorConverter)
diff --git a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationSpec.kt b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationSpec.kt
index e280595..9244266 100644
--- a/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationSpec.kt
+++ b/compose/animation/animation-core/src/commonMain/kotlin/androidx/compose/animation/core/AnimationSpec.kt
@@ -17,6 +17,7 @@
package androidx.compose.animation.core
import androidx.annotation.IntRange
+import androidx.collection.mutableIntObjectMapOf
import androidx.compose.animation.core.AnimationConstants.DefaultDurationMillis
import androidx.compose.animation.core.KeyframesSpec.KeyframesSpecConfig
import androidx.compose.runtime.Immutable
@@ -392,6 +393,85 @@
}
/**
+ * Shared configuration class used as DSL for keyframe based animations.
+ */
+sealed class KeyframesSpecBaseConfig<T, E : KeyframeBaseEntity<T>> {
+ /**
+ * Duration of the animation in milliseconds. The minimum is `0` and defaults to
+ * [DefaultDurationMillis]
+ */
+ @get:IntRange(from = 0)
+ var durationMillis: Int = DefaultDurationMillis
+
+ /**
+ * The amount of time that the animation should be delayed. The minimum is `0` and defaults
+ * to 0.
+ */
+ @get:IntRange(from = 0)
+ var delayMillis: Int = 0
+
+ internal val keyframes = mutableIntObjectMapOf<E>()
+
+ /**
+ * Method used to delegate instantiation of [E] to implementing classes.
+ */
+ internal abstract fun createEntityFor(value: T): E
+
+ /**
+ * Adds a keyframe so that animation value will be [this] at time: [timeStamp]. For example:
+ *
+ * @sample androidx.compose.animation.core.samples.floatAtSample
+ *
+ * @param timeStamp The time in the during when animation should reach value: [this], with
+ * a minimum value of `0`.
+ * @return an instance of [E] so a custom [Easing] can be added by the [using] method.
+ */
+ infix fun T.at(@IntRange(from = 0) timeStamp: Int): E {
+ val entity = createEntityFor(this)
+ keyframes[timeStamp] = entity
+ return entity
+ }
+
+ /**
+ * Adds a keyframe so that the animation value will be the value specified at a fraction of the
+ * total [durationMillis] set. It's recommended that you always set [durationMillis] before
+ * calling [atFraction]. For example:
+ *
+ * @sample androidx.compose.animation.core.samples.floatAtFractionSample
+ *
+ * @param fraction The fraction when the animation should reach specified value.
+ * @return an instance of [E] so a custom [Easing] can be added by the [using] method
+ */
+ infix fun T.atFraction(fraction: Float): E {
+ return at((durationMillis * fraction).roundToInt())
+ }
+
+ /**
+ * Adds an [Easing] for the interval started with the just provided timestamp. For example:
+ * 0f at 50 using LinearEasing
+ *
+ * @sample androidx.compose.animation.core.samples.KeyframesBuilderWithEasing
+ * @param easing [Easing] to be used for the next interval.
+ * @return the same [E] instance so that other implementations can expand on the builder pattern
+ */
+ infix fun E.using(easing: Easing): E {
+ this.easing = easing
+ return this
+ }
+}
+
+/**
+ * Base holder class for building a keyframes animation.
+ */
+sealed class KeyframeBaseEntity<T>(
+ internal val value: T,
+ internal var easing: Easing
+) {
+ internal fun <V : AnimationVector> toPair(convertToVector: (T) -> V) =
+ convertToVector.invoke(value) to easing
+}
+
+/**
* [KeyframesSpec] creates a [VectorizedKeyframesSpec] animation.
*
* [VectorizedKeyframesSpec] animates based on the values defined at different timestamps in
@@ -417,48 +497,8 @@
* @sample androidx.compose.animation.core.samples.KeyframesBuilderForPosition
* @see keyframes
*/
- class KeyframesSpecConfig<T> {
- /**
- * Duration of the animation in milliseconds. The minimum is `0` and defaults to
- * [DefaultDurationMillis]
- */
- @get:IntRange(from = 0)
- var durationMillis: Int = DefaultDurationMillis
-
- /**
- * The amount of time that the animation should be delayed. The minimum is `0` and defaults
- * to 0.
- */
- @get:IntRange(from = 0)
- var delayMillis: Int = 0
-
- internal val keyframes = mutableMapOf<Int, KeyframeEntity<T>>()
-
- /**
- * Adds a keyframe so that animation value will be [this] at time: [timeStamp]. For example:
- * 0.8f at 150 // ms
- *
- * @param timeStamp The time in the during when animation should reach value: [this], with
- * a minimum value of `0`.
- * @return an [KeyframeEntity] so a custom [Easing] can be added by [with] method.
- */
- // TODO: Need a IntRange equivalent annotation
- infix fun T.at(@IntRange(from = 0) timeStamp: Int): KeyframeEntity<T> {
- return KeyframeEntity(this).also {
- keyframes[timeStamp] = it
- }
- }
-
- /**
- * Adds a keyframe so that the animation value will be the value specified at a fraction of the total
- * [durationMillis] set. For example:
- * 0.8f atFraction 0.50f // half of the overall duration set
- * @param fraction The fraction when the animation should reach specified value.
- * @return an [KeyframeEntity] so a custom [Easing] can be added by [with] method
- */
- infix fun T.atFraction(fraction: Float): KeyframeEntity<T> {
- return at((durationMillis * fraction).roundToInt())
- }
+ class KeyframesSpecConfig<T> : KeyframesSpecBaseConfig<T, KeyframeEntity<T>>() {
+ override fun createEntityFor(value: T): KeyframeEntity<T> = KeyframeEntity(value)
/**
* Adds an [Easing] for the interval started with the just provided timestamp. For example:
@@ -466,7 +506,14 @@
*
* @sample androidx.compose.animation.core.samples.KeyframesBuilderWithEasing
* @param easing [Easing] to be used for the next interval.
+ * @return the same [KeyframeEntity] instance so that other implementations can expand on
+ * the builder pattern
*/
+ @Deprecated(
+ message = "Use version that returns an instance of the entity so it can be re-used" +
+ " in other keyframe builders.",
+ replaceWith = ReplaceWith("this using easing") // Expected usage pattern
+ )
infix fun KeyframeEntity<T>.with(easing: Easing) {
this.easing = easing
}
@@ -482,8 +529,7 @@
}
override fun equals(other: Any?): Boolean {
- return other is KeyframesSpec<*> &&
- config == other.config
+ return other is KeyframesSpec<*> && config == other.config
}
override fun hashCode(): Int {
@@ -493,11 +539,15 @@
override fun <V : AnimationVector> vectorize(
converter: TwoWayConverter<T, V>
): VectorizedKeyframesSpec<V> {
+ @SuppressWarnings("PrimitiveInCollection") // Consumed by stable public API
+ val vectorizedKeyframes = mutableMapOf<Int, Pair<V, Easing>>()
+ config.keyframes.forEach { key, value ->
+ vectorizedKeyframes[key] = value.toPair(converter.convertToVector)
+ }
return VectorizedKeyframesSpec(
- config.keyframes.mapValues {
- it.value.toPair(converter.convertToVector)
- },
- config.durationMillis, config.delayMillis
+ keyframes = vectorizedKeyframes,
+ durationMillis = config.durationMillis,
+ delayMillis = config.delayMillis
)
}
@@ -505,11 +555,9 @@
* Holder class for building a keyframes animation.
*/
class KeyframeEntity<T> internal constructor(
- internal val value: T,
- internal var easing: Easing = LinearEasing
- ) {
- internal fun <V : AnimationVector> toPair(convertToVector: (T) -> V) =
- convertToVector.invoke(value) to easing
+ value: T,
+ easing: Easing = LinearEasing
+ ) : KeyframeBaseEntity<T>(value = value, easing = easing) {
override fun equals(other: Any?): Boolean {
return other is KeyframeEntity<*> && other.value == value && other.easing == easing
diff --git a/compose/animation/animation-graphics/src/androidInstrumentedTest/kotlin/androidx/compose/animation/graphics/vector/AnimatorAnimationSpecsTest.kt b/compose/animation/animation-graphics/src/androidInstrumentedTest/kotlin/androidx/compose/animation/graphics/vector/AnimatorAnimationSpecsTest.kt
index fb49907..f77439a 100644
--- a/compose/animation/animation-graphics/src/androidInstrumentedTest/kotlin/androidx/compose/animation/graphics/vector/AnimatorAnimationSpecsTest.kt
+++ b/compose/animation/animation-graphics/src/androidInstrumentedTest/kotlin/androidx/compose/animation/graphics/vector/AnimatorAnimationSpecsTest.kt
@@ -78,9 +78,9 @@
transitionSpec = {
keyframes {
durationMillis = 1000
- 0f at 0 with LinearEasing
- 100f at 100 with LinearEasing
- 1000f at 1000 with LinearEasing
+ 0f at 0 using LinearEasing
+ 100f at 100 using LinearEasing
+ 1000f at 1000 using LinearEasing
}
}
) {
@@ -91,9 +91,9 @@
transitionSpec = {
keyframes<Float> {
durationMillis = 1000
- 1000f at 0 with LinearEasing
- 100f at 900 with LinearEasing
- 0f at 1000 with LinearEasing
+ 1000f at 0 using LinearEasing
+ 100f at 900 using LinearEasing
+ 0f at 1000 using LinearEasing
}.reversed(1000)
}
) {
@@ -117,8 +117,8 @@
transitionSpec = {
keyframes {
durationMillis = 1000
- 0f at 0 with LinearEasing
- 1000f at 500 with LinearEasing
+ 0f at 0 using LinearEasing
+ 1000f at 500 using LinearEasing
}
}
) {
@@ -129,9 +129,9 @@
transitionSpec = {
keyframes<Float> {
durationMillis = 1000
- 1000f at 0 with LinearEasing
- 1000f at 500 with LinearEasing
- 0f at 1000 with LinearEasing
+ 1000f at 0 using LinearEasing
+ 1000f at 500 using LinearEasing
+ 0f at 1000 using LinearEasing
}.reversed(1000)
}
) {
@@ -186,8 +186,8 @@
transitionSpec = {
keyframes {
durationMillis = 1000
- 0f at 0 with LinearEasing
- 1000f at 1000 with LinearEasing
+ 0f at 0 using LinearEasing
+ 1000f at 1000 using LinearEasing
}
}
) {
@@ -200,13 +200,13 @@
listOf(
0 to keyframes {
durationMillis = 300
- 0f at 0 with LinearEasing
- 300f at 300 with LinearEasing
+ 0f at 0 using LinearEasing
+ 300f at 300 using LinearEasing
},
300 to keyframes {
durationMillis = 700
- 300f at 0 with LinearEasing
- 1000f at 700 with LinearEasing
+ 300f at 0 using LinearEasing
+ 1000f at 700 using LinearEasing
}
)
)
diff --git a/compose/animation/animation-graphics/src/commonMain/kotlin/androidx/compose/animation/graphics/vector/Animator.kt b/compose/animation/animation-graphics/src/commonMain/kotlin/androidx/compose/animation/graphics/vector/Animator.kt
index d4ffb83..56f2de5 100644
--- a/compose/animation/animation-graphics/src/commonMain/kotlin/androidx/compose/animation/graphics/vector/Animator.kt
+++ b/compose/animation/animation-graphics/src/commonMain/kotlin/androidx/compose/animation/graphics/vector/Animator.kt
@@ -384,7 +384,7 @@
return keyframes {
durationMillis = duration
animatorKeyframes.fastForEach { keyframe ->
- keyframe.value at (duration * keyframe.fraction).toInt() with keyframe.interpolator
+ keyframe.value at (duration * keyframe.fraction).toInt() using keyframe.interpolator
}
}
}
@@ -404,7 +404,7 @@
return keyframes {
durationMillis = duration
animatorKeyframes.fastForEach { keyframe ->
- keyframe.value at (duration * keyframe.fraction).toInt() with keyframe.interpolator
+ keyframe.value at (duration * keyframe.fraction).toInt() using keyframe.interpolator
}
}
}
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/statetransition/RepeatedRotationDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/statetransition/RepeatedRotationDemo.kt
index eccdd75..96a75fd 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/statetransition/RepeatedRotationDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/statetransition/RepeatedRotationDemo.kt
@@ -74,7 +74,7 @@
iterations = 10,
animation = keyframes {
durationMillis = 1000
- 0f at 0 with LinearEasing
+ 0f at 0 using LinearEasing
360f at 1000
}
)
diff --git a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/vectorgraphics/AnimatedVectorGraphicsDemo.kt b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/vectorgraphics/AnimatedVectorGraphicsDemo.kt
index 4ffe170..8e59114 100644
--- a/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/vectorgraphics/AnimatedVectorGraphicsDemo.kt
+++ b/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/vectorgraphics/AnimatedVectorGraphicsDemo.kt
@@ -111,7 +111,7 @@
keyframes {
durationMillis = duration
0f at 0
- 360f at duration with LinearEasing
+ 360f at duration using LinearEasing
}
} else {
spring(
@@ -175,7 +175,7 @@
keyframes {
durationMillis = duration
Color.Red at 0
- Color.Blue at duration with LinearEasing
+ Color.Blue at duration using LinearEasing
}
} else {
spring()
diff --git a/compose/animation/animation/src/androidInstrumentedTest/kotlin/androidx/compose/animation/AnimatedContentTest.kt b/compose/animation/animation/src/androidInstrumentedTest/kotlin/androidx/compose/animation/AnimatedContentTest.kt
index 2f62fcb..469f5c7 100644
--- a/compose/animation/animation/src/androidInstrumentedTest/kotlin/androidx/compose/animation/AnimatedContentTest.kt
+++ b/compose/animation/animation/src/androidInstrumentedTest/kotlin/androidx/compose/animation/AnimatedContentTest.kt
@@ -115,9 +115,9 @@
SizeTransform { initialSize, targetSize ->
keyframes {
durationMillis = 320
- IntSize(targetSize.width, initialSize.height) at 160 with
+ IntSize(targetSize.width, initialSize.height) at 160 using
LinearEasing
- targetSize at 320 with LinearEasing
+ targetSize at 320 using LinearEasing
}
}
} else {
@@ -809,8 +809,8 @@
} using SizeTransform { initialSize, targetSize ->
keyframes {
durationMillis = 300
- initialSize at 100 with LinearEasing
- targetSize at 200 with LinearEasing
+ initialSize at 100 using LinearEasing
+ targetSize at 200 using LinearEasing
}
}
}) {
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/LoremIpsum.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/LoremIpsum.kt
index 42511ed..62c54c3 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/LoremIpsum.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/LoremIpsum.kt
@@ -26,9 +26,10 @@
fun loremIpsum(
language: Language = Language.Latin,
- wordCount: Int = language.words.size
+ wordCount: Int = language.words.size,
+ separator: CharSequence = " ",
): String =
- loremIpsumWords(language).joinToString(separator = " ", limit = wordCount, truncated = "")
+ loremIpsumWords(language).joinToString(separator = separator, limit = wordCount, truncated = "")
/**
* An infinite [Sequence] of words of Lorem Ipsum text.
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/TextDemos.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/TextDemos.kt
index f7bfe30..b7ef02f 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/TextDemos.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text/TextDemos.kt
@@ -25,6 +25,7 @@
import androidx.compose.foundation.demos.text2.DecorationBoxDemos
import androidx.compose.foundation.demos.text2.KeyboardOptionsDemos
import androidx.compose.foundation.demos.text2.ScrollableDemos
+import androidx.compose.foundation.demos.text2.ScrollableDemosRtl
import androidx.compose.foundation.demos.text2.SwapFieldSameStateDemo
import androidx.compose.foundation.demos.text2.TextFieldLineLimitsDemos
import androidx.compose.foundation.samples.BasicTextField2UndoSample
@@ -141,7 +142,10 @@
ComposableDemo("Keyboard Options") { KeyboardOptionsDemos() },
ComposableDemo("Decoration Box") { DecorationBoxDemos() },
ComposableDemo("Line limits") { TextFieldLineLimitsDemos() },
- ComposableDemo("Scroll") { ScrollableDemos() },
+ DemoCategory("Scroll", listOf(
+ ComposableDemo("Ltr") { ScrollableDemos() },
+ ComposableDemo("Rtl") { ScrollableDemosRtl() },
+ )),
ComposableDemo("Filters") { BasicTextField2FilterDemos() },
ComposableDemo("Secure Field") { BasicSecureTextFieldDemos() },
ComposableDemo("Swap the field but reuse the state") { SwapFieldSameStateDemo() },
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/DecorationBoxDemos.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/DecorationBoxDemos.kt
index af8e58c..00eb85c 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/DecorationBoxDemos.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/DecorationBoxDemos.kt
@@ -40,7 +40,7 @@
@Composable
fun DecorationBoxDemos() {
- Column {
+ Column(Modifier.padding(16.dp)) {
TagLine(tag = "Simple Decoration w/ Label")
SimpleDecorationWithLabel()
diff --git a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/ScrollDemos.kt b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/ScrollDemos.kt
index b144879..0888591 100644
--- a/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/ScrollDemos.kt
+++ b/compose/foundation/foundation/integration-tests/foundation-demos/src/main/java/androidx/compose/foundation/demos/text2/ScrollDemos.kt
@@ -17,8 +17,8 @@
package androidx.compose.foundation.demos.text2
import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.demos.text.Language
import androidx.compose.foundation.demos.text.TagLine
-import androidx.compose.foundation.demos.text.fontSize8
import androidx.compose.foundation.demos.text.loremIpsum
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
@@ -31,18 +31,15 @@
import androidx.compose.foundation.text2.input.TextFieldLineLimits.MultiLine
import androidx.compose.foundation.text2.input.TextFieldLineLimits.SingleLine
import androidx.compose.foundation.text2.input.TextFieldState
-import androidx.compose.material.Button
import androidx.compose.material.Slider
-import androidx.compose.material.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus.FocusRequester
-import androidx.compose.ui.focus.focusRequester
-import androidx.compose.ui.text.TextRange
+import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.coerceIn
+import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import kotlin.math.roundToInt
@@ -62,11 +59,6 @@
}
item {
- TagLine(tag = "SingleLine Vertical Scroll")
- SingleLineVerticalScrollableTextField()
- }
-
- item {
TagLine(tag = "MultiLine Vertical Scroll")
MultiLineVerticalScrollableTextField()
}
@@ -80,19 +72,28 @@
TagLine(tag = "Shared Hoisted ScrollState")
SharedHoistedScroll()
}
+ }
+}
- item {
- TagLine(tag = "Selectable with no interaction")
- SelectionWithNoInteraction()
- }
+@Composable
+fun ScrollableDemosRtl() {
+ CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
+ ScrollableDemos()
}
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun SingleLineHorizontalScrollableTextField() {
- val state = remember {
- TextFieldState(loremIpsum(wordCount = 100))
+ val layoutDirection = LocalLayoutDirection.current
+ val language = if (layoutDirection == LayoutDirection.Ltr) Language.Latin else Language.Hebrew
+ val state = remember(language) {
+ TextFieldState(
+ loremIpsum(
+ wordCount = 100,
+ language = language
+ )
+ )
}
BasicTextField2(
state = state,
@@ -102,12 +103,19 @@
)
}
-// TODO this is not supported currently. Add tests for this when supported.
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun SingleLineHorizontalScrollableTextFieldWithNewlines() {
- val state = remember {
- TextFieldState("This \ntext \ncontains \nnewlines \nbut \nis \nsingle-line.")
+ val layoutDirection = LocalLayoutDirection.current
+ val language = if (layoutDirection == LayoutDirection.Ltr) Language.Latin else Language.Hebrew
+ val state = remember(language) {
+ TextFieldState(
+ loremIpsum(
+ wordCount = 20,
+ language = language,
+ separator = "\n"
+ )
+ )
}
BasicTextField2(
state = state,
@@ -118,22 +126,16 @@
@OptIn(ExperimentalFoundationApi::class)
@Composable
-fun SingleLineVerticalScrollableTextField() {
- val state = remember {
- TextFieldState("When content gets long, this field should scroll vertically\n".repeat(10))
- }
- BasicTextField2(
- state = state,
- textStyle = TextStyle(fontSize = 24.sp),
- lineLimits = MultiLine(maxHeightInLines = 1)
- )
-}
-
-@OptIn(ExperimentalFoundationApi::class)
-@Composable
fun MultiLineVerticalScrollableTextField() {
- val state = remember {
- TextFieldState(loremIpsum(wordCount = 200))
+ val layoutDirection = LocalLayoutDirection.current
+ val language = if (layoutDirection == LayoutDirection.Ltr) Language.Latin else Language.Hebrew
+ val state = remember(language) {
+ TextFieldState(
+ loremIpsum(
+ wordCount = 200,
+ language = language
+ )
+ )
}
BasicTextField2(
state = state,
@@ -146,8 +148,15 @@
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun HoistedHorizontalScroll() {
- val state = remember {
- TextFieldState("When content gets long, this field should scroll horizontally")
+ val layoutDirection = LocalLayoutDirection.current
+ val language = if (layoutDirection == LayoutDirection.Ltr) Language.Latin else Language.Hebrew
+ val state = remember(language) {
+ TextFieldState(
+ loremIpsum(
+ wordCount = 20,
+ language = language
+ )
+ )
}
val scrollState = rememberScrollState()
val coroutineScope = rememberCoroutineScope()
@@ -172,11 +181,23 @@
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun SharedHoistedScroll() {
- val state1 = remember {
- TextFieldState("When content gets long, this field should scroll horizontally")
+ val layoutDirection = LocalLayoutDirection.current
+ val language = if (layoutDirection == LayoutDirection.Ltr) Language.Latin else Language.Hebrew
+ val state1 = remember(language) {
+ TextFieldState(
+ loremIpsum(
+ wordCount = 20,
+ language = language
+ )
+ )
}
- val state2 = remember {
- TextFieldState("When content gets long, this field should scroll horizontally")
+ val state2 = remember(language) {
+ TextFieldState(
+ loremIpsum(
+ wordCount = 20,
+ language = language
+ )
+ )
}
val scrollState = rememberScrollState()
val coroutineScope = rememberCoroutineScope()
@@ -204,46 +225,3 @@
)
}
}
-
-@OptIn(ExperimentalFoundationApi::class)
-@Composable
-fun SelectionWithNoInteraction() {
- val state =
- remember { TextFieldState("Hello, World!", initialSelectionInChars = TextRange(1, 5)) }
- val focusRequester = remember { FocusRequester() }
- Column {
- Button(onClick = { focusRequester.requestFocus() }) {
- Text("Focus")
- }
- Button(onClick = {
- state.edit {
- selectCharsIn(
- TextRange(
- state.text.selectionInChars.start - 1,
- state.text.selectionInChars.end
- ).coerceIn(0, state.text.length)
- )
- }
- }) {
- Text("Increase Selection to Left")
- }
- Button(onClick = {
- state.edit {
- selectCharsIn(
- TextRange(
- state.text.selectionInChars.start,
- state.text.selectionInChars.end + 1
- ).coerceIn(0, state.text.length)
- )
- }
- }) {
- Text("Increase Selection to Right")
- }
- BasicTextField2(
- state = state,
- modifier = demoTextFieldModifiers.focusRequester(focusRequester),
- textStyle = TextStyle(fontSize = fontSize8),
- lineLimits = SingleLine
- )
- }
-}
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/AndroidExternalSurfaceTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/AndroidExternalSurfaceTest.kt
index 59bb5f8..3254191 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/AndroidExternalSurfaceTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/AndroidExternalSurfaceTest.kt
@@ -16,6 +16,9 @@
package androidx.compose.foundation
+import android.app.Activity
+import android.content.Context
+import android.content.ContextWrapper
import android.graphics.Bitmap
import android.graphics.PorterDuff
import android.graphics.Rect
@@ -26,6 +29,7 @@
import android.view.PixelCopy
import android.view.Surface
import android.view.View
+import android.view.Window
import androidx.annotation.RequiresApi
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
@@ -55,10 +59,12 @@
import androidx.core.graphics.ColorUtils
import androidx.core.graphics.createBitmap
import androidx.test.core.internal.os.HandlerExecutor
+import androidx.test.core.view.forceRedraw
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import androidx.test.filters.SdkSuppress
import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.platform.graphics.HardwareRendererCompat
import java.util.concurrent.CountDownLatch
import java.util.concurrent.ExecutionException
import java.util.concurrent.TimeUnit
@@ -508,52 +514,57 @@
val node = fetchSemanticsNode()
val view = (node.root as ViewRootForTest).view
+ val window = view.context.getActivityWindow()
- val bitmapFuture: ResolvableFuture<Bitmap> = ResolvableFuture.create()
+ return withDrawingEnabled {
+ val bitmapFuture: ResolvableFuture<Bitmap> = ResolvableFuture.create()
- val mainExecutor = HandlerExecutor(Handler(Looper.getMainLooper()))
- mainExecutor.execute {
- Choreographer.getInstance().postFrameCallback {
- val location = IntArray(2)
- view.getLocationOnScreen(location)
+ val mainExecutor = HandlerExecutor(Handler(Looper.getMainLooper()))
+ mainExecutor.execute {
+ window.decorView.forceRedraw()
- val bounds = node.boundsInRoot.translate(
- location[0].toFloat(),
- location[1].toFloat()
- )
+ Choreographer.getInstance().postFrameCallback {
+ val location = IntArray(2)
+ view.getLocationOnScreen(location)
- // do multiple retries of uiAutomation.takeScreenshot because it is known to return null
- // on API 31+ b/257274080
- var bitmap: Bitmap? = null
- var i = 0
- while (i < 3 && bitmap == null) {
- bitmap = uiAutomation.takeScreenshot()
- i++
- }
-
- if (bitmap != null) {
- bitmap = Bitmap.createBitmap(
- bitmap,
- bounds.left.toInt(),
- bounds.top.toInt(),
- bounds.width.toInt(),
- bounds.height.toInt()
+ val bounds = node.boundsInRoot.translate(
+ location[0].toFloat(),
+ location[1].toFloat()
)
- bitmapFuture.set(bitmap)
- } else {
- if (hasSecureSurfaces) {
- // may be null on older API levels when a secure surface is showing
- bitmapFuture.set(null)
+
+ // do multiple retries of uiAutomation.takeScreenshot because it is known to return null
+ // on API 31+ b/257274080
+ var bitmap: Bitmap? = null
+ var i = 0
+ while (i < 3 && bitmap == null) {
+ bitmap = uiAutomation.takeScreenshot()
+ i++
}
- // if we don't show secure surfaces, let the future timeout on get()
+
+ if (bitmap != null) {
+ bitmap = Bitmap.createBitmap(
+ bitmap,
+ bounds.left.toInt(),
+ bounds.top.toInt(),
+ bounds.width.toInt(),
+ bounds.height.toInt()
+ )
+ bitmapFuture.set(bitmap)
+ } else {
+ if (hasSecureSurfaces) {
+ // may be null on older API levels when a secure surface is showing
+ bitmapFuture.set(null)
+ }
+ // if we don't show secure surfaces, let the future timeout on get()
+ }
}
}
- }
- return try {
- bitmapFuture.get(5, TimeUnit.SECONDS)?.asImageBitmap()
- } catch (e: ExecutionException) {
- null
+ try {
+ bitmapFuture.get(5, TimeUnit.SECONDS)?.asImageBitmap()
+ } catch (e: ExecutionException) {
+ null
+ }
}
}
@@ -587,3 +598,31 @@
return bitmap.asImageBitmap()
}
+
+private fun <R> withDrawingEnabled(block: () -> R): R {
+ val wasDrawingEnabled = HardwareRendererCompat.isDrawingEnabled()
+ try {
+ if (!wasDrawingEnabled) {
+ HardwareRendererCompat.setDrawingEnabled(true)
+ }
+ return block.invoke()
+ } finally {
+ if (!wasDrawingEnabled) {
+ HardwareRendererCompat.setDrawingEnabled(false)
+ }
+ }
+}
+
+private fun Context.getActivityWindow(): Window {
+ fun Context.getActivity(): Activity {
+ return when (this) {
+ is Activity -> this
+ is ContextWrapper -> this.baseContext.getActivity()
+ else -> throw IllegalStateException(
+ "Context is not an Activity context, but a ${javaClass.simpleName} context. " +
+ "An Activity context is required to get a Window instance"
+ )
+ }
+ }
+ return getActivity().window
+}
diff --git a/compose/material/material-icons-core/api/current.txt b/compose/material/material-icons-core/api/current.txt
index e9e6d3b..c80a502 100644
--- a/compose/material/material-icons-core/api/current.txt
+++ b/compose/material/material-icons-core/api/current.txt
@@ -54,8 +54,8 @@
}
public final class IconsKt {
- method public static inline androidx.compose.ui.graphics.vector.ImageVector materialIcon(String name, boolean autoMirror, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.vector.ImageVector.Builder,androidx.compose.ui.graphics.vector.ImageVector.Builder> block);
- method public static inline androidx.compose.ui.graphics.vector.ImageVector materialIcon(String name, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.vector.ImageVector.Builder,androidx.compose.ui.graphics.vector.ImageVector.Builder> block);
+ method public static inline androidx.compose.ui.graphics.vector.ImageVector materialIcon(String name, optional boolean autoMirror, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.vector.ImageVector.Builder,androidx.compose.ui.graphics.vector.ImageVector.Builder> block);
+ method @Deprecated public static inline androidx.compose.ui.graphics.vector.ImageVector materialIcon(String name, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.vector.ImageVector.Builder,androidx.compose.ui.graphics.vector.ImageVector.Builder> block);
method public static inline androidx.compose.ui.graphics.vector.ImageVector.Builder materialPath(androidx.compose.ui.graphics.vector.ImageVector.Builder, optional float fillAlpha, optional float strokeAlpha, optional int pathFillType, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.vector.PathBuilder,kotlin.Unit> pathBuilder);
}
diff --git a/compose/material/material-icons-core/api/restricted_current.txt b/compose/material/material-icons-core/api/restricted_current.txt
index d5e5415..06acb8e 100644
--- a/compose/material/material-icons-core/api/restricted_current.txt
+++ b/compose/material/material-icons-core/api/restricted_current.txt
@@ -54,8 +54,8 @@
}
public final class IconsKt {
- method public static inline androidx.compose.ui.graphics.vector.ImageVector materialIcon(String name, boolean autoMirror, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.vector.ImageVector.Builder,androidx.compose.ui.graphics.vector.ImageVector.Builder> block);
- method public static inline androidx.compose.ui.graphics.vector.ImageVector materialIcon(String name, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.vector.ImageVector.Builder,androidx.compose.ui.graphics.vector.ImageVector.Builder> block);
+ method public static inline androidx.compose.ui.graphics.vector.ImageVector materialIcon(String name, optional boolean autoMirror, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.vector.ImageVector.Builder,androidx.compose.ui.graphics.vector.ImageVector.Builder> block);
+ method @Deprecated public static inline androidx.compose.ui.graphics.vector.ImageVector materialIcon(String name, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.vector.ImageVector.Builder,androidx.compose.ui.graphics.vector.ImageVector.Builder> block);
method public static inline androidx.compose.ui.graphics.vector.ImageVector.Builder materialPath(androidx.compose.ui.graphics.vector.ImageVector.Builder, optional float fillAlpha, optional float strokeAlpha, optional int pathFillType, kotlin.jvm.functions.Function1<? super androidx.compose.ui.graphics.vector.PathBuilder,kotlin.Unit> pathBuilder);
field @kotlin.PublishedApi internal static final float MaterialIconDimension = 24.0f;
}
diff --git a/compose/material/material-icons-core/src/commonMain/kotlin/androidx/compose/material/icons/Icons.kt b/compose/material/material-icons-core/src/commonMain/kotlin/androidx/compose/material/icons/Icons.kt
index 3573a8d..30f8b7d 100644
--- a/compose/material/material-icons-core/src/commonMain/kotlin/androidx/compose/material/icons/Icons.kt
+++ b/compose/material/material-icons-core/src/commonMain/kotlin/androidx/compose/material/icons/Icons.kt
@@ -191,6 +191,10 @@
* @param name the full name of the generated icon
* @param block builder lambda to add paths to this vector asset
*/
+@Deprecated(
+ "Maintained for binary compatibility. Use version with autoMirror instead.",
+ level = DeprecationLevel.HIDDEN
+)
inline fun materialIcon(
name: String,
block: ImageVector.Builder.() -> ImageVector.Builder
@@ -207,7 +211,7 @@
*/
inline fun materialIcon(
name: String,
- autoMirror: Boolean,
+ autoMirror: Boolean = false,
block: ImageVector.Builder.() -> ImageVector.Builder
): ImageVector = ImageVector.Builder(
name = name,
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ProgressIndicator.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ProgressIndicator.kt
index f10015e..9be72a8 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ProgressIndicator.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/ProgressIndicator.kt
@@ -151,7 +151,7 @@
infiniteRepeatable(
animation = keyframes {
durationMillis = LinearAnimationDuration
- 0f at FirstLineHeadDelay with FirstLineHeadEasing
+ 0f at FirstLineHeadDelay using FirstLineHeadEasing
1f at FirstLineHeadDuration + FirstLineHeadDelay
}
)
@@ -162,7 +162,7 @@
infiniteRepeatable(
animation = keyframes {
durationMillis = LinearAnimationDuration
- 0f at FirstLineTailDelay with FirstLineTailEasing
+ 0f at FirstLineTailDelay using FirstLineTailEasing
1f at FirstLineTailDuration + FirstLineTailDelay
}
)
@@ -173,7 +173,7 @@
infiniteRepeatable(
animation = keyframes {
durationMillis = LinearAnimationDuration
- 0f at SecondLineHeadDelay with SecondLineHeadEasing
+ 0f at SecondLineHeadDelay using SecondLineHeadEasing
1f at SecondLineHeadDuration + SecondLineHeadDelay
}
)
@@ -184,7 +184,7 @@
infiniteRepeatable(
animation = keyframes {
durationMillis = LinearAnimationDuration
- 0f at SecondLineTailDelay with SecondLineTailEasing
+ 0f at SecondLineTailDelay using SecondLineTailEasing
1f at SecondLineTailDuration + SecondLineTailDelay
}
)
@@ -398,7 +398,7 @@
infiniteRepeatable(
animation = keyframes {
durationMillis = HeadAndTailAnimationDuration + HeadAndTailDelayDuration
- 0f at 0 with CircularEasing
+ 0f at 0 using CircularEasing
JumpRotationAngle at HeadAndTailAnimationDuration
}
)
@@ -410,7 +410,7 @@
infiniteRepeatable(
animation = keyframes {
durationMillis = HeadAndTailAnimationDuration + HeadAndTailDelayDuration
- 0f at HeadAndTailDelayDuration with CircularEasing
+ 0f at HeadAndTailDelayDuration using CircularEasing
JumpRotationAngle at durationMillis
}
)
diff --git a/compose/material3/material3-adaptive/api/current.txt b/compose/material3/material3-adaptive/api/current.txt
index b33820f..cc2cb3d 100644
--- a/compose/material3/material3-adaptive/api/current.txt
+++ b/compose/material3/material3-adaptive/api/current.txt
@@ -11,23 +11,6 @@
property public final androidx.compose.material3.adaptive.AdaptStrategy Hide;
}
- @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Immutable public final class AdaptiveLayoutDirective {
- ctor public AdaptiveLayoutDirective(int maxHorizontalPartitions, androidx.compose.material3.adaptive.GutterSizes gutterSizes, int maxVerticalPartitions, java.util.List<androidx.compose.ui.geometry.Rect> excludedBounds);
- method public java.util.List<androidx.compose.ui.geometry.Rect> getExcludedBounds();
- method public androidx.compose.material3.adaptive.GutterSizes getGutterSizes();
- method public int getMaxHorizontalPartitions();
- method public int getMaxVerticalPartitions();
- property public final java.util.List<androidx.compose.ui.geometry.Rect> excludedBounds;
- property public final androidx.compose.material3.adaptive.GutterSizes gutterSizes;
- property public final int maxHorizontalPartitions;
- property public final int maxVerticalPartitions;
- }
-
- public final class AdaptiveLayoutDirectiveKt {
- method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public static androidx.compose.material3.adaptive.AdaptiveLayoutDirective calculateDenseAdaptiveLayoutDirective(androidx.compose.material3.adaptive.WindowAdaptiveInfo windowAdaptiveInfo, optional int hingePolicy);
- method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public static androidx.compose.material3.adaptive.AdaptiveLayoutDirective calculateStandardAdaptiveLayoutDirective(androidx.compose.material3.adaptive.WindowAdaptiveInfo windowAdaptiveInfo, optional int hingePolicy);
- }
-
public final class AndroidPosture_androidKt {
method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public static androidx.compose.material3.adaptive.Posture calculatePosture(java.util.List<? extends androidx.window.layout.FoldingFeature> foldingFeatures);
}
@@ -75,7 +58,7 @@
public final class ListDetailPaneScaffoldKt {
method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void ListDetailPaneScaffold(androidx.compose.material3.adaptive.ListDetailPaneScaffoldState layoutState, kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.PaneAdaptedValue,kotlin.Unit> listPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.PaneAdaptedValue,kotlin.Unit>? extraPane, kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.PaneAdaptedValue,kotlin.Unit> detailPane);
- method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.ListDetailPaneScaffoldState rememberListDetailPaneScaffoldState(optional androidx.compose.material3.adaptive.AdaptiveLayoutDirective layoutDirectives, optional androidx.compose.material3.adaptive.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional java.util.List<? extends androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole> initialFocusHistory);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.ListDetailPaneScaffoldState rememberListDetailPaneScaffoldState(optional androidx.compose.material3.adaptive.PaneScaffoldDirective scaffoldDirective, optional androidx.compose.material3.adaptive.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional java.util.List<? extends androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole> initialFocusHistory);
}
@SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public enum ListDetailPaneScaffoldRole {
@@ -88,12 +71,12 @@
@SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Stable public interface ListDetailPaneScaffoldState {
method public boolean canNavigateBack(optional boolean layoutValueMustChange);
- method public androidx.compose.material3.adaptive.AdaptiveLayoutDirective getLayoutDirective();
method public androidx.compose.material3.adaptive.ThreePaneScaffoldValue getLayoutValue();
+ method public androidx.compose.material3.adaptive.PaneScaffoldDirective getScaffoldDirective();
method public boolean navigateBack(optional boolean popUntilLayoutValueChange);
method public void navigateTo(androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole pane);
- property public abstract androidx.compose.material3.adaptive.AdaptiveLayoutDirective layoutDirective;
property public abstract androidx.compose.material3.adaptive.ThreePaneScaffoldValue layoutValue;
+ property public abstract androidx.compose.material3.adaptive.PaneScaffoldDirective scaffoldDirective;
}
@SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public final class NavigationSuiteColors {
@@ -164,6 +147,23 @@
property public final String Hidden;
}
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Immutable public final class PaneScaffoldDirective {
+ ctor public PaneScaffoldDirective(int maxHorizontalPartitions, androidx.compose.material3.adaptive.GutterSizes gutterSizes, int maxVerticalPartitions, java.util.List<androidx.compose.ui.geometry.Rect> excludedBounds);
+ method public java.util.List<androidx.compose.ui.geometry.Rect> getExcludedBounds();
+ method public androidx.compose.material3.adaptive.GutterSizes getGutterSizes();
+ method public int getMaxHorizontalPartitions();
+ method public int getMaxVerticalPartitions();
+ property public final java.util.List<androidx.compose.ui.geometry.Rect> excludedBounds;
+ property public final androidx.compose.material3.adaptive.GutterSizes gutterSizes;
+ property public final int maxHorizontalPartitions;
+ property public final int maxVerticalPartitions;
+ }
+
+ public final class PaneScaffoldDirectiveKt {
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public static androidx.compose.material3.adaptive.PaneScaffoldDirective calculateDensePaneScaffoldDirective(androidx.compose.material3.adaptive.WindowAdaptiveInfo windowAdaptiveInfo, optional int hingePolicy);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public static androidx.compose.material3.adaptive.PaneScaffoldDirective calculateStandardPaneScaffoldDirective(androidx.compose.material3.adaptive.WindowAdaptiveInfo windowAdaptiveInfo, optional int hingePolicy);
+ }
+
@SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public interface PaneScaffoldScope {
method public androidx.compose.ui.Modifier preferredWidth(androidx.compose.ui.Modifier, float width);
}
@@ -187,7 +187,7 @@
public final class SupportingPaneScaffoldKt {
method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void SupportingPaneScaffold(androidx.compose.material3.adaptive.SupportingPaneScaffoldState scaffoldState, kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.PaneAdaptedValue,kotlin.Unit> supportingPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.PaneAdaptedValue,kotlin.Unit>? extraPane, kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.PaneAdaptedValue,kotlin.Unit> mainPane);
- method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.SupportingPaneScaffoldState rememberSupportingPaneScaffoldState(optional androidx.compose.material3.adaptive.AdaptiveLayoutDirective layoutDirectives, optional androidx.compose.material3.adaptive.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional java.util.List<? extends androidx.compose.material3.adaptive.SupportingPaneScaffoldRole> initialFocusHistory);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.SupportingPaneScaffoldState rememberSupportingPaneScaffoldState(optional androidx.compose.material3.adaptive.PaneScaffoldDirective scaffoldDirective, optional androidx.compose.material3.adaptive.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional java.util.List<? extends androidx.compose.material3.adaptive.SupportingPaneScaffoldRole> initialFocusHistory);
}
@SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public enum SupportingPaneScaffoldRole {
@@ -200,12 +200,12 @@
@SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Stable public interface SupportingPaneScaffoldState {
method public boolean canNavigateBack(optional boolean layoutValueMustChange);
- method public androidx.compose.material3.adaptive.AdaptiveLayoutDirective getLayoutDirective();
method public androidx.compose.material3.adaptive.ThreePaneScaffoldValue getLayoutValue();
+ method public androidx.compose.material3.adaptive.PaneScaffoldDirective getScaffoldDirective();
method public boolean navigateBack(optional boolean popUntilLayoutValueChange);
method public void navigateTo(androidx.compose.material3.adaptive.SupportingPaneScaffoldRole pane);
- property public abstract androidx.compose.material3.adaptive.AdaptiveLayoutDirective layoutDirective;
property public abstract androidx.compose.material3.adaptive.ThreePaneScaffoldValue layoutValue;
+ property public abstract androidx.compose.material3.adaptive.PaneScaffoldDirective scaffoldDirective;
}
@SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public final class ThreePaneScaffoldAdaptStrategies {
@@ -237,7 +237,7 @@
}
public final class ThreePaneScaffoldKt {
- method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void ThreePaneScaffold(androidx.compose.ui.Modifier modifier, androidx.compose.material3.adaptive.AdaptiveLayoutDirective layoutDirective, androidx.compose.material3.adaptive.ThreePaneScaffoldValue scaffoldValue, androidx.compose.material3.adaptive.ThreePaneScaffoldArrangement arrangement, kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.PaneAdaptedValue,kotlin.Unit> secondaryPane, optional kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.PaneAdaptedValue,kotlin.Unit>? tertiaryPane, kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.PaneAdaptedValue,kotlin.Unit> primaryPane);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void ThreePaneScaffold(androidx.compose.ui.Modifier modifier, androidx.compose.material3.adaptive.PaneScaffoldDirective scaffoldDirective, androidx.compose.material3.adaptive.ThreePaneScaffoldValue scaffoldValue, androidx.compose.material3.adaptive.ThreePaneScaffoldArrangement arrangement, kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.PaneAdaptedValue,kotlin.Unit> secondaryPane, optional kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.PaneAdaptedValue,kotlin.Unit>? tertiaryPane, kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.PaneAdaptedValue,kotlin.Unit> primaryPane);
}
@SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public enum ThreePaneScaffoldRole {
diff --git a/compose/material3/material3-adaptive/api/restricted_current.txt b/compose/material3/material3-adaptive/api/restricted_current.txt
index b33820f..cc2cb3d 100644
--- a/compose/material3/material3-adaptive/api/restricted_current.txt
+++ b/compose/material3/material3-adaptive/api/restricted_current.txt
@@ -11,23 +11,6 @@
property public final androidx.compose.material3.adaptive.AdaptStrategy Hide;
}
- @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Immutable public final class AdaptiveLayoutDirective {
- ctor public AdaptiveLayoutDirective(int maxHorizontalPartitions, androidx.compose.material3.adaptive.GutterSizes gutterSizes, int maxVerticalPartitions, java.util.List<androidx.compose.ui.geometry.Rect> excludedBounds);
- method public java.util.List<androidx.compose.ui.geometry.Rect> getExcludedBounds();
- method public androidx.compose.material3.adaptive.GutterSizes getGutterSizes();
- method public int getMaxHorizontalPartitions();
- method public int getMaxVerticalPartitions();
- property public final java.util.List<androidx.compose.ui.geometry.Rect> excludedBounds;
- property public final androidx.compose.material3.adaptive.GutterSizes gutterSizes;
- property public final int maxHorizontalPartitions;
- property public final int maxVerticalPartitions;
- }
-
- public final class AdaptiveLayoutDirectiveKt {
- method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public static androidx.compose.material3.adaptive.AdaptiveLayoutDirective calculateDenseAdaptiveLayoutDirective(androidx.compose.material3.adaptive.WindowAdaptiveInfo windowAdaptiveInfo, optional int hingePolicy);
- method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public static androidx.compose.material3.adaptive.AdaptiveLayoutDirective calculateStandardAdaptiveLayoutDirective(androidx.compose.material3.adaptive.WindowAdaptiveInfo windowAdaptiveInfo, optional int hingePolicy);
- }
-
public final class AndroidPosture_androidKt {
method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public static androidx.compose.material3.adaptive.Posture calculatePosture(java.util.List<? extends androidx.window.layout.FoldingFeature> foldingFeatures);
}
@@ -75,7 +58,7 @@
public final class ListDetailPaneScaffoldKt {
method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void ListDetailPaneScaffold(androidx.compose.material3.adaptive.ListDetailPaneScaffoldState layoutState, kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.PaneAdaptedValue,kotlin.Unit> listPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.PaneAdaptedValue,kotlin.Unit>? extraPane, kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.PaneAdaptedValue,kotlin.Unit> detailPane);
- method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.ListDetailPaneScaffoldState rememberListDetailPaneScaffoldState(optional androidx.compose.material3.adaptive.AdaptiveLayoutDirective layoutDirectives, optional androidx.compose.material3.adaptive.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional java.util.List<? extends androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole> initialFocusHistory);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.ListDetailPaneScaffoldState rememberListDetailPaneScaffoldState(optional androidx.compose.material3.adaptive.PaneScaffoldDirective scaffoldDirective, optional androidx.compose.material3.adaptive.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional java.util.List<? extends androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole> initialFocusHistory);
}
@SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public enum ListDetailPaneScaffoldRole {
@@ -88,12 +71,12 @@
@SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Stable public interface ListDetailPaneScaffoldState {
method public boolean canNavigateBack(optional boolean layoutValueMustChange);
- method public androidx.compose.material3.adaptive.AdaptiveLayoutDirective getLayoutDirective();
method public androidx.compose.material3.adaptive.ThreePaneScaffoldValue getLayoutValue();
+ method public androidx.compose.material3.adaptive.PaneScaffoldDirective getScaffoldDirective();
method public boolean navigateBack(optional boolean popUntilLayoutValueChange);
method public void navigateTo(androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole pane);
- property public abstract androidx.compose.material3.adaptive.AdaptiveLayoutDirective layoutDirective;
property public abstract androidx.compose.material3.adaptive.ThreePaneScaffoldValue layoutValue;
+ property public abstract androidx.compose.material3.adaptive.PaneScaffoldDirective scaffoldDirective;
}
@SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public final class NavigationSuiteColors {
@@ -164,6 +147,23 @@
property public final String Hidden;
}
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Immutable public final class PaneScaffoldDirective {
+ ctor public PaneScaffoldDirective(int maxHorizontalPartitions, androidx.compose.material3.adaptive.GutterSizes gutterSizes, int maxVerticalPartitions, java.util.List<androidx.compose.ui.geometry.Rect> excludedBounds);
+ method public java.util.List<androidx.compose.ui.geometry.Rect> getExcludedBounds();
+ method public androidx.compose.material3.adaptive.GutterSizes getGutterSizes();
+ method public int getMaxHorizontalPartitions();
+ method public int getMaxVerticalPartitions();
+ property public final java.util.List<androidx.compose.ui.geometry.Rect> excludedBounds;
+ property public final androidx.compose.material3.adaptive.GutterSizes gutterSizes;
+ property public final int maxHorizontalPartitions;
+ property public final int maxVerticalPartitions;
+ }
+
+ public final class PaneScaffoldDirectiveKt {
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public static androidx.compose.material3.adaptive.PaneScaffoldDirective calculateDensePaneScaffoldDirective(androidx.compose.material3.adaptive.WindowAdaptiveInfo windowAdaptiveInfo, optional int hingePolicy);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public static androidx.compose.material3.adaptive.PaneScaffoldDirective calculateStandardPaneScaffoldDirective(androidx.compose.material3.adaptive.WindowAdaptiveInfo windowAdaptiveInfo, optional int hingePolicy);
+ }
+
@SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public interface PaneScaffoldScope {
method public androidx.compose.ui.Modifier preferredWidth(androidx.compose.ui.Modifier, float width);
}
@@ -187,7 +187,7 @@
public final class SupportingPaneScaffoldKt {
method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void SupportingPaneScaffold(androidx.compose.material3.adaptive.SupportingPaneScaffoldState scaffoldState, kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.PaneAdaptedValue,kotlin.Unit> supportingPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.PaneAdaptedValue,kotlin.Unit>? extraPane, kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.PaneAdaptedValue,kotlin.Unit> mainPane);
- method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.SupportingPaneScaffoldState rememberSupportingPaneScaffoldState(optional androidx.compose.material3.adaptive.AdaptiveLayoutDirective layoutDirectives, optional androidx.compose.material3.adaptive.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional java.util.List<? extends androidx.compose.material3.adaptive.SupportingPaneScaffoldRole> initialFocusHistory);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.SupportingPaneScaffoldState rememberSupportingPaneScaffoldState(optional androidx.compose.material3.adaptive.PaneScaffoldDirective scaffoldDirective, optional androidx.compose.material3.adaptive.ThreePaneScaffoldAdaptStrategies adaptStrategies, optional java.util.List<? extends androidx.compose.material3.adaptive.SupportingPaneScaffoldRole> initialFocusHistory);
}
@SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public enum SupportingPaneScaffoldRole {
@@ -200,12 +200,12 @@
@SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Stable public interface SupportingPaneScaffoldState {
method public boolean canNavigateBack(optional boolean layoutValueMustChange);
- method public androidx.compose.material3.adaptive.AdaptiveLayoutDirective getLayoutDirective();
method public androidx.compose.material3.adaptive.ThreePaneScaffoldValue getLayoutValue();
+ method public androidx.compose.material3.adaptive.PaneScaffoldDirective getScaffoldDirective();
method public boolean navigateBack(optional boolean popUntilLayoutValueChange);
method public void navigateTo(androidx.compose.material3.adaptive.SupportingPaneScaffoldRole pane);
- property public abstract androidx.compose.material3.adaptive.AdaptiveLayoutDirective layoutDirective;
property public abstract androidx.compose.material3.adaptive.ThreePaneScaffoldValue layoutValue;
+ property public abstract androidx.compose.material3.adaptive.PaneScaffoldDirective scaffoldDirective;
}
@SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public final class ThreePaneScaffoldAdaptStrategies {
@@ -237,7 +237,7 @@
}
public final class ThreePaneScaffoldKt {
- method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void ThreePaneScaffold(androidx.compose.ui.Modifier modifier, androidx.compose.material3.adaptive.AdaptiveLayoutDirective layoutDirective, androidx.compose.material3.adaptive.ThreePaneScaffoldValue scaffoldValue, androidx.compose.material3.adaptive.ThreePaneScaffoldArrangement arrangement, kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.PaneAdaptedValue,kotlin.Unit> secondaryPane, optional kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.PaneAdaptedValue,kotlin.Unit>? tertiaryPane, kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.PaneAdaptedValue,kotlin.Unit> primaryPane);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void ThreePaneScaffold(androidx.compose.ui.Modifier modifier, androidx.compose.material3.adaptive.PaneScaffoldDirective scaffoldDirective, androidx.compose.material3.adaptive.ThreePaneScaffoldValue scaffoldValue, androidx.compose.material3.adaptive.ThreePaneScaffoldArrangement arrangement, kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.PaneAdaptedValue,kotlin.Unit> secondaryPane, optional kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.PaneAdaptedValue,kotlin.Unit>? tertiaryPane, kotlin.jvm.functions.Function2<? super androidx.compose.material3.adaptive.ThreePaneScaffoldScope,? super androidx.compose.material3.adaptive.PaneAdaptedValue,kotlin.Unit> primaryPane);
}
@SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public enum ThreePaneScaffoldRole {
diff --git a/compose/material3/material3-adaptive/samples/src/main/java/androidx/compose/material3-adaptive/samples/ThreePaneScaffoldSample.kt b/compose/material3/material3-adaptive/samples/src/main/java/androidx/compose/material3-adaptive/samples/ThreePaneScaffoldSample.kt
index 943a1a3..42618e3 100644
--- a/compose/material3/material3-adaptive/samples/src/main/java/androidx/compose/material3-adaptive/samples/ThreePaneScaffoldSample.kt
+++ b/compose/material3/material3-adaptive/samples/src/main/java/androidx/compose/material3-adaptive/samples/ThreePaneScaffoldSample.kt
@@ -33,7 +33,7 @@
import androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole
import androidx.compose.material3.adaptive.ThreePaneScaffold
import androidx.compose.material3.adaptive.ThreePaneScaffoldDefaults
-import androidx.compose.material3.adaptive.calculateStandardAdaptiveLayoutDirective
+import androidx.compose.material3.adaptive.calculateStandardPaneScaffoldDirective
import androidx.compose.material3.adaptive.calculateThreePaneScaffoldValue
import androidx.compose.material3.adaptive.calculateWindowAdaptiveInfo
import androidx.compose.material3.adaptive.rememberListDetailPaneScaffoldState
@@ -162,11 +162,11 @@
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
internal fun ThreePaneScaffoldSample() {
- val layoutDirective = calculateStandardAdaptiveLayoutDirective(calculateWindowAdaptiveInfo())
+ val scaffoldDirective = calculateStandardPaneScaffoldDirective(calculateWindowAdaptiveInfo())
ThreePaneScaffold(
modifier = Modifier.fillMaxSize(),
- layoutDirective = layoutDirective,
- scaffoldValue = calculateThreePaneScaffoldValue(layoutDirective.maxHorizontalPartitions),
+ scaffoldDirective = scaffoldDirective,
+ scaffoldValue = calculateThreePaneScaffoldValue(scaffoldDirective.maxHorizontalPartitions),
arrangement = ThreePaneScaffoldDefaults.ListDetailLayoutArrangement,
secondaryPane = {
Surface(
diff --git a/compose/material3/material3-adaptive/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/ListDetailPaneScaffoldStateTest.kt b/compose/material3/material3-adaptive/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/ListDetailPaneScaffoldStateTest.kt
index 2b8e76d..49dbf29 100644
--- a/compose/material3/material3-adaptive/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/ListDetailPaneScaffoldStateTest.kt
+++ b/compose/material3/material3-adaptive/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/ListDetailPaneScaffoldStateTest.kt
@@ -41,7 +41,7 @@
composeRule.setContent {
layoutState = rememberListDetailPaneScaffoldState(
- layoutDirectives = MockSinglePaneLayoutDirective
+ scaffoldDirective = MockSinglePaneScaffoldDirective
)
canNavigateBack = layoutState.canNavigateBack()
}
@@ -64,7 +64,7 @@
composeRule.setContent {
layoutState = rememberListDetailPaneScaffoldState(
- layoutDirectives = MockDualPaneLayoutDirective
+ scaffoldDirective = MockDualPaneScaffoldDirective
)
canNavigateBack = layoutState.canNavigateBack()
}
@@ -87,7 +87,7 @@
composeRule.setContent {
layoutState = rememberListDetailPaneScaffoldState(
- layoutDirectives = MockSinglePaneLayoutDirective
+ scaffoldDirective = MockSinglePaneScaffoldDirective
)
canNavigateBack = layoutState.canNavigateBack()
}
@@ -114,7 +114,7 @@
composeRule.setContent {
layoutState = rememberListDetailPaneScaffoldState(
- layoutDirectives = MockDualPaneLayoutDirective,
+ scaffoldDirective = MockDualPaneScaffoldDirective,
initialFocusHistory = listOf(
ListDetailPaneScaffoldRole.List,
ListDetailPaneScaffoldRole.Detail,
@@ -133,7 +133,7 @@
composeRule.setContent {
layoutState = rememberListDetailPaneScaffoldState(
- layoutDirectives = MockDualPaneLayoutDirective,
+ scaffoldDirective = MockDualPaneScaffoldDirective,
initialFocusHistory = listOf(
ListDetailPaneScaffoldRole.List,
ListDetailPaneScaffoldRole.Detail,
@@ -155,11 +155,11 @@
@Test
fun singlePaneToDualPaneLayout_enforceLayoutValueChange_cannotNavigateBack() {
lateinit var layoutState: ListDetailPaneScaffoldState
- val mockCurrentLayoutDirective = mutableStateOf(MockSinglePaneLayoutDirective)
+ val mockCurrentScaffoldDirective = mutableStateOf(MockSinglePaneScaffoldDirective)
composeRule.setContent {
layoutState = rememberListDetailPaneScaffoldState(
- layoutDirectives = mockCurrentLayoutDirective.value,
+ scaffoldDirective = mockCurrentScaffoldDirective.value,
initialFocusHistory = listOf(
ListDetailPaneScaffoldRole.List,
ListDetailPaneScaffoldRole.Detail,
@@ -169,7 +169,7 @@
composeRule.runOnIdle {
assertThat(layoutState.layoutValue.primary).isEqualTo(PaneAdaptedValue.Expanded)
// Switches to dual pane
- mockCurrentLayoutDirective.value = MockDualPaneLayoutDirective
+ mockCurrentScaffoldDirective.value = MockDualPaneScaffoldDirective
}
composeRule.runOnIdle {
@@ -179,7 +179,7 @@
}
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
-private val MockSinglePaneLayoutDirective = AdaptiveLayoutDirective(
+private val MockSinglePaneScaffoldDirective = PaneScaffoldDirective(
maxHorizontalPartitions = 1,
gutterSizes = GutterSizes(0.dp, 0.dp),
maxVerticalPartitions = 1,
@@ -187,7 +187,7 @@
)
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
-private val MockDualPaneLayoutDirective = AdaptiveLayoutDirective(
+private val MockDualPaneScaffoldDirective = PaneScaffoldDirective(
maxHorizontalPartitions = 2,
gutterSizes = GutterSizes(16.dp, 16.dp),
maxVerticalPartitions = 1,
diff --git a/compose/material3/material3-adaptive/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/SupportingPaneScaffoldStateTest.kt b/compose/material3/material3-adaptive/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/SupportingPaneScaffoldStateTest.kt
index 894e528..6e21d9e 100644
--- a/compose/material3/material3-adaptive/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/SupportingPaneScaffoldStateTest.kt
+++ b/compose/material3/material3-adaptive/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/SupportingPaneScaffoldStateTest.kt
@@ -41,7 +41,7 @@
composeRule.setContent {
layoutState = rememberSupportingPaneScaffoldState(
- layoutDirectives = MockSinglePaneLayoutDirective
+ scaffoldDirective = MockSinglePaneScaffoldDirective
)
canNavigateBack = layoutState.canNavigateBack()
}
@@ -64,7 +64,7 @@
composeRule.setContent {
layoutState = rememberSupportingPaneScaffoldState(
- layoutDirectives = MockDualPaneLayoutDirective
+ scaffoldDirective = MockDualPaneScaffoldDirective
)
canNavigateBack = layoutState.canNavigateBack()
}
@@ -87,7 +87,7 @@
composeRule.setContent {
layoutState = rememberSupportingPaneScaffoldState(
- layoutDirectives = MockSinglePaneLayoutDirective
+ scaffoldDirective = MockSinglePaneScaffoldDirective
)
canNavigateBack = layoutState.canNavigateBack()
}
@@ -114,7 +114,7 @@
composeRule.setContent {
layoutState = rememberSupportingPaneScaffoldState(
- layoutDirectives = MockDualPaneLayoutDirective,
+ scaffoldDirective = MockDualPaneScaffoldDirective,
initialFocusHistory = listOf(
SupportingPaneScaffoldRole.Supporting,
SupportingPaneScaffoldRole.Main,
@@ -133,7 +133,7 @@
composeRule.setContent {
layoutState = rememberSupportingPaneScaffoldState(
- layoutDirectives = MockDualPaneLayoutDirective,
+ scaffoldDirective = MockDualPaneScaffoldDirective,
initialFocusHistory = listOf(
SupportingPaneScaffoldRole.Supporting,
SupportingPaneScaffoldRole.Main,
@@ -155,11 +155,11 @@
@Test
fun singlePaneToDualPaneLayout_enforceLayoutValueChange_cannotNavigateBack() {
lateinit var layoutState: SupportingPaneScaffoldState
- val mockCurrentLayoutDirective = mutableStateOf(MockSinglePaneLayoutDirective)
+ val mockCurrentScaffoldDirective = mutableStateOf(MockSinglePaneScaffoldDirective)
composeRule.setContent {
layoutState = rememberSupportingPaneScaffoldState(
- layoutDirectives = mockCurrentLayoutDirective.value,
+ scaffoldDirective = mockCurrentScaffoldDirective.value,
initialFocusHistory = listOf(
SupportingPaneScaffoldRole.Supporting,
SupportingPaneScaffoldRole.Main,
@@ -169,7 +169,7 @@
composeRule.runOnIdle {
assertThat(layoutState.layoutValue.primary).isEqualTo(PaneAdaptedValue.Expanded)
// Switches to dual pane
- mockCurrentLayoutDirective.value = MockDualPaneLayoutDirective
+ mockCurrentScaffoldDirective.value = MockDualPaneScaffoldDirective
}
composeRule.runOnIdle {
@@ -179,7 +179,7 @@
}
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
-private val MockSinglePaneLayoutDirective = AdaptiveLayoutDirective(
+private val MockSinglePaneScaffoldDirective = PaneScaffoldDirective(
maxHorizontalPartitions = 1,
gutterSizes = GutterSizes(0.dp, 0.dp),
maxVerticalPartitions = 1,
@@ -187,7 +187,7 @@
)
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
-private val MockDualPaneLayoutDirective = AdaptiveLayoutDirective(
+private val MockDualPaneScaffoldDirective = PaneScaffoldDirective(
maxHorizontalPartitions = 2,
gutterSizes = GutterSizes(16.dp, 16.dp),
maxVerticalPartitions = 1,
diff --git a/compose/material3/material3-adaptive/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffoldScreenshotTest.kt b/compose/material3/material3-adaptive/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffoldScreenshotTest.kt
index 7ff97c2..c5793d7 100644
--- a/compose/material3/material3-adaptive/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffoldScreenshotTest.kt
+++ b/compose/material3/material3-adaptive/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffoldScreenshotTest.kt
@@ -43,14 +43,14 @@
@Test
fun threePaneScaffold_listDetailArrangement_standard() {
rule.setContent {
- val layoutDirective = calculateStandardAdaptiveLayoutDirective(
+ val scaffoldDirective = calculateStandardPaneScaffoldDirective(
calculateWindowAdaptiveInfo()
)
val scaffoldValue = calculateThreePaneScaffoldValue(
- layoutDirective.maxHorizontalPartitions
+ scaffoldDirective.maxHorizontalPartitions
)
SampleThreePaneScaffold(
- layoutDirective,
+ scaffoldDirective,
scaffoldValue,
ThreePaneScaffoldDefaults.ListDetailLayoutArrangement
)
@@ -64,14 +64,14 @@
@Test
fun threePaneScaffold_listDetailArrangement_dense() {
rule.setContent {
- val layoutDirective = calculateDenseAdaptiveLayoutDirective(
+ val scaffoldDirective = calculateDensePaneScaffoldDirective(
calculateWindowAdaptiveInfo()
)
val scaffoldValue = calculateThreePaneScaffoldValue(
- layoutDirective.maxHorizontalPartitions
+ scaffoldDirective.maxHorizontalPartitions
)
SampleThreePaneScaffold(
- layoutDirective,
+ scaffoldDirective,
scaffoldValue,
ThreePaneScaffoldDefaults.ListDetailLayoutArrangement
)
diff --git a/compose/material3/material3-adaptive/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffoldTest.kt b/compose/material3/material3-adaptive/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffoldTest.kt
index 4b175bf..54b5ecf 100644
--- a/compose/material3/material3-adaptive/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffoldTest.kt
+++ b/compose/material3/material3-adaptive/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffoldTest.kt
@@ -104,7 +104,7 @@
}
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
-private val MockLayoutDirective = AdaptiveLayoutDirective(
+private val MockScaffoldDirective = PaneScaffoldDirective(
maxHorizontalPartitions = 1,
gutterSizes = GutterSizes(0.dp, 0.dp),
maxVerticalPartitions = 1,
@@ -117,7 +117,7 @@
@Composable
private fun SampleThreePaneScaffold(scaffoldValue: ThreePaneScaffoldValue) {
SampleThreePaneScaffold(
- MockLayoutDirective,
+ MockScaffoldDirective,
scaffoldValue,
ThreePaneScaffoldDefaults.ListDetailLayoutArrangement
)
@@ -126,13 +126,13 @@
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
internal fun SampleThreePaneScaffold(
- layoutDirective: AdaptiveLayoutDirective,
+ scaffoldDirective: PaneScaffoldDirective,
scaffoldValue: ThreePaneScaffoldValue,
arrangement: ThreePaneScaffoldArrangement
) {
ThreePaneScaffold(
modifier = Modifier.fillMaxSize().testTag(ThreePaneScaffoldTestTag),
- layoutDirective = layoutDirective,
+ scaffoldDirective = scaffoldDirective,
scaffoldValue = scaffoldValue,
arrangement = arrangement,
secondaryPane = {
diff --git a/compose/material3/material3-adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/ListDetailPaneScaffold.kt b/compose/material3/material3-adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/ListDetailPaneScaffold.kt
index bc93c71..4ac47a5 100644
--- a/compose/material3/material3-adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/ListDetailPaneScaffold.kt
+++ b/compose/material3/material3-adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/ListDetailPaneScaffold.kt
@@ -45,7 +45,7 @@
) {
ThreePaneScaffold(
modifier = modifier.fillMaxSize(),
- layoutDirective = layoutState.layoutDirective,
+ scaffoldDirective = layoutState.scaffoldDirective,
scaffoldValue = layoutState.layoutValue,
arrangement = ThreePaneScaffoldDefaults.ListDetailLayoutArrangement,
secondaryPane = listPane,
@@ -86,7 +86,7 @@
* which works independently from any navigation frameworks. Developers can also integrate with
* other navigation frameworks by implementing this interface.
*
- * @property layoutDirective the current layout directives that the associated
+ * @property scaffoldDirective the current layout directives that the associated
* [ListDetailPaneScaffold] needs to follow. It's supposed to be automatically updated
* when the window configuration changes.
* @property layoutValue the current layout value of the associated [ListDetailPaneScaffold], which
@@ -95,7 +95,7 @@
@ExperimentalMaterial3AdaptiveApi
@Stable
interface ListDetailPaneScaffoldState {
- val layoutDirective: AdaptiveLayoutDirective
+ val scaffoldDirective: PaneScaffoldDirective
val layoutValue: ThreePaneScaffoldValue
/**
@@ -124,7 +124,7 @@
private class DefaultListDetailPaneScaffoldState(
val internalState: DefaultThreePaneScaffoldState
) : ListDetailPaneScaffoldState {
- override val layoutDirective get() = internalState.layoutDirective
+ override val scaffoldDirective get() = internalState.scaffoldDirective
override val layoutValue get() = internalState.layoutValue
override fun navigateTo(pane: ListDetailPaneScaffoldRole) {
@@ -144,8 +144,8 @@
* used independently from any navigation frameworks and it will address the navigation purely
* inside the [ListDetailPaneScaffold].
*
- * @param layoutDirectives the current layout directives to follow. The default value will be
- * Calculated with [calculateStandardAdaptiveLayoutDirective] using [WindowAdaptiveInfo]
+ * @param scaffoldDirective the current layout directives to follow. The default value will be
+ * Calculated with [calculateStandardPaneScaffoldDirective] using [WindowAdaptiveInfo]
* retrieved from the current context.
* @param adaptStrategies adaptation strategies of each pane.
* @param initialFocusHistory the initial focus history of the scaffold, by default it will be just
@@ -154,14 +154,14 @@
@ExperimentalMaterial3AdaptiveApi
@Composable
fun rememberListDetailPaneScaffoldState(
- layoutDirectives: AdaptiveLayoutDirective =
- calculateStandardAdaptiveLayoutDirective(calculateWindowAdaptiveInfo()),
+ scaffoldDirective: PaneScaffoldDirective =
+ calculateStandardPaneScaffoldDirective(calculateWindowAdaptiveInfo()),
adaptStrategies: ThreePaneScaffoldAdaptStrategies =
ListDetailPaneScaffoldDefaults.adaptStrategies(),
initialFocusHistory: List<ListDetailPaneScaffoldRole> = listOf(ListDetailPaneScaffoldRole.List)
): ListDetailPaneScaffoldState {
val internalState = rememberDefaultThreePaneScaffoldState(
- layoutDirectives,
+ scaffoldDirective,
adaptStrategies,
initialFocusHistory.fastMap { it.threePaneScaffoldRole }
)
diff --git a/compose/material3/material3-adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/SupportingPaneScaffold.kt b/compose/material3/material3-adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/SupportingPaneScaffold.kt
index 64eb93b8..33997bf 100644
--- a/compose/material3/material3-adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/SupportingPaneScaffold.kt
+++ b/compose/material3/material3-adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/SupportingPaneScaffold.kt
@@ -30,7 +30,7 @@
) {
ThreePaneScaffold(
modifier = modifier.fillMaxSize(),
- layoutDirective = scaffoldState.layoutDirective,
+ scaffoldDirective = scaffoldState.scaffoldDirective,
scaffoldValue = scaffoldState.layoutValue,
arrangement = ThreePaneScaffoldDefaults.SupportingPaneLayoutArrangement,
secondaryPane = supportingPane,
@@ -71,7 +71,7 @@
* which works independently from any navigation frameworks. Developers can also integrate with
* other navigation frameworks by implementing this interface.
*
- * @property layoutDirective the current layout directives that the associated
+ * @property scaffoldDirective the current layout directives that the associated
* [SupportingPaneScaffold] needs to follow. It's supposed to be automatically updated
* when the window configuration changes.
* @property layoutValue the current layout value of the associated [SupportingPaneScaffold], which
@@ -80,7 +80,7 @@
@ExperimentalMaterial3AdaptiveApi
@Stable
interface SupportingPaneScaffoldState {
- val layoutDirective: AdaptiveLayoutDirective
+ val scaffoldDirective: PaneScaffoldDirective
val layoutValue: ThreePaneScaffoldValue
/**
@@ -109,7 +109,7 @@
private class DefaultSupportingPaneScaffoldState(
val internalState: DefaultThreePaneScaffoldState
) : SupportingPaneScaffoldState {
- override val layoutDirective get() = internalState.layoutDirective
+ override val scaffoldDirective get() = internalState.scaffoldDirective
override val layoutValue get() = internalState.layoutValue
override fun navigateTo(pane: SupportingPaneScaffoldRole) {
@@ -129,8 +129,8 @@
* used independently from any navigation frameworks and it will address the navigation purely
* inside the [SupportingPaneScaffold].
*
- * @param layoutDirectives the current layout directives to follow. The default value will be
- * Calculated with [calculateStandardAdaptiveLayoutDirective] using [WindowAdaptiveInfo]
+ * @param scaffoldDirective the current layout directives to follow. The default value will be
+ * Calculated with [calculateStandardPaneScaffoldDirective] using [WindowAdaptiveInfo]
* retrieved from the current context.
* @param adaptStrategies adaptation strategies of each pane.
* @param initialFocusHistory the initial focus history of the scaffold, by default it will be just
@@ -139,14 +139,14 @@
@ExperimentalMaterial3AdaptiveApi
@Composable
fun rememberSupportingPaneScaffoldState(
- layoutDirectives: AdaptiveLayoutDirective =
- calculateStandardAdaptiveLayoutDirective(calculateWindowAdaptiveInfo()),
+ scaffoldDirective: PaneScaffoldDirective =
+ calculateStandardPaneScaffoldDirective(calculateWindowAdaptiveInfo()),
adaptStrategies: ThreePaneScaffoldAdaptStrategies =
SupportingPaneScaffoldDefaults.adaptStrategies(),
initialFocusHistory: List<SupportingPaneScaffoldRole> = listOf(SupportingPaneScaffoldRole.Main)
): SupportingPaneScaffoldState {
val internalState = rememberDefaultThreePaneScaffoldState(
- layoutDirectives,
+ scaffoldDirective,
adaptStrategies,
initialFocusHistory.fastMap { it.threePaneScaffoldRole }
)
diff --git a/compose/material3/material3-adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffold.android.kt b/compose/material3/material3-adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffold.android.kt
index 0175dc06..c047f2d 100644
--- a/compose/material3/material3-adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffold.android.kt
+++ b/compose/material3/material3-adaptive/src/androidMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffold.android.kt
@@ -12,7 +12,7 @@
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
internal class DefaultThreePaneScaffoldState(
initialFocusHistory: List<ThreePaneScaffoldRole>,
- initialLayoutDirective: AdaptiveLayoutDirective,
+ initialScaffoldDirective: PaneScaffoldDirective,
initialAdaptStrategies: ThreePaneScaffoldAdaptStrategies,
) {
@@ -20,7 +20,7 @@
addAll(initialFocusHistory)
}
- var layoutDirective by mutableStateOf(initialLayoutDirective)
+ var scaffoldDirective by mutableStateOf(initialScaffoldDirective)
var adaptStrategies by mutableStateOf(initialAdaptStrategies)
val currentFocus: ThreePaneScaffoldRole?
@@ -69,7 +69,7 @@
focus: ThreePaneScaffoldRole?
): ThreePaneScaffoldValue =
calculateThreePaneScaffoldValue(
- layoutDirective.maxHorizontalPartitions,
+ scaffoldDirective.maxHorizontalPartitions,
adaptStrategies,
focus
)
@@ -79,7 +79,7 @@
* To keep focus history saved
*/
fun saver(
- initialLayoutDirective: AdaptiveLayoutDirective,
+ initialScaffoldDirective: PaneScaffoldDirective,
initialAdaptStrategies: ThreePaneScaffoldAdaptStrategies
): Saver<DefaultThreePaneScaffoldState, *> = listSaver(
save = {
@@ -88,7 +88,7 @@
restore = {
DefaultThreePaneScaffoldState(
initialFocusHistory = it,
- initialLayoutDirective = initialLayoutDirective,
+ initialScaffoldDirective = initialScaffoldDirective,
initialAdaptStrategies = initialAdaptStrategies
)
}
@@ -99,22 +99,22 @@
@ExperimentalMaterial3AdaptiveApi
@Composable
internal fun rememberDefaultThreePaneScaffoldState(
- layoutDirectives: AdaptiveLayoutDirective,
+ scaffoldDirective: PaneScaffoldDirective,
adaptStrategies: ThreePaneScaffoldAdaptStrategies,
initialFocusHistory: List<ThreePaneScaffoldRole>
): DefaultThreePaneScaffoldState =
rememberSaveable(
saver = DefaultThreePaneScaffoldState.saver(
- layoutDirectives,
+ scaffoldDirective,
adaptStrategies,
)
) {
DefaultThreePaneScaffoldState(
initialFocusHistory = initialFocusHistory,
- initialLayoutDirective = layoutDirectives,
+ initialScaffoldDirective = scaffoldDirective,
initialAdaptStrategies = adaptStrategies
)
}.apply {
- this.layoutDirective = layoutDirectives
+ this.scaffoldDirective = scaffoldDirective
this.adaptStrategies = adaptStrategies
}
diff --git a/compose/material3/material3-adaptive/src/androidUnitTest/kotlin/androidx/compose/material3/adaptive/AdaptiveLayoutDirectiveTest.kt b/compose/material3/material3-adaptive/src/androidUnitTest/kotlin/androidx/compose/material3/adaptive/AdaptiveLayoutDirectiveTest.kt
deleted file mode 100644
index 63a9db7..0000000
--- a/compose/material3/material3-adaptive/src/androidUnitTest/kotlin/androidx/compose/material3/adaptive/AdaptiveLayoutDirectiveTest.kt
+++ /dev/null
@@ -1,317 +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.compose.material3.adaptive
-
-import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi
-import androidx.compose.material3.windowsizeclass.WindowSizeClass
-import androidx.compose.ui.geometry.Rect
-import androidx.compose.ui.unit.DpSize
-import androidx.compose.ui.unit.dp
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-
-@OptIn(ExperimentalMaterial3AdaptiveApi::class, ExperimentalMaterial3WindowSizeClassApi::class)
-@RunWith(JUnit4::class)
-class AdaptiveLayoutDirectiveTest {
- @Test
- fun test_calculateStandardAdaptiveLayoutDirective_compactWidth() {
- val layoutDirective = calculateStandardAdaptiveLayoutDirective(
- WindowAdaptiveInfo(
- WindowSizeClass.calculateFromSize(DpSize(400.dp, 800.dp)),
- Posture()
- )
- )
-
- assertThat(layoutDirective.maxHorizontalPartitions).isEqualTo(1)
- assertThat(layoutDirective.maxVerticalPartitions).isEqualTo(1)
- assertThat(layoutDirective.gutterSizes.outerVertical).isEqualTo(16.dp)
- assertThat(layoutDirective.gutterSizes.innerVertical).isEqualTo(0.dp)
- assertThat(layoutDirective.gutterSizes.outerHorizontal).isEqualTo(16.dp)
- assertThat(layoutDirective.gutterSizes.innerHorizontal).isEqualTo(0.dp)
- }
-
- @Test
- fun test_calculateStandardAdaptiveLayoutDirective_mediumWidth() {
- val layoutDirective = calculateStandardAdaptiveLayoutDirective(
- WindowAdaptiveInfo(
- WindowSizeClass.calculateFromSize(DpSize(750.dp, 900.dp)),
- Posture()
- )
- )
-
- assertThat(layoutDirective.maxHorizontalPartitions).isEqualTo(1)
- assertThat(layoutDirective.maxVerticalPartitions).isEqualTo(1)
- assertThat(layoutDirective.gutterSizes.outerVertical).isEqualTo(24.dp)
- assertThat(layoutDirective.gutterSizes.innerVertical).isEqualTo(0.dp)
- assertThat(layoutDirective.gutterSizes.outerHorizontal).isEqualTo(24.dp)
- assertThat(layoutDirective.gutterSizes.innerHorizontal).isEqualTo(0.dp)
- }
-
- @Test
- fun test_calculateStandardAdaptiveLayoutDirective_expandedWidth() {
- val layoutDirective = calculateStandardAdaptiveLayoutDirective(
- WindowAdaptiveInfo(
- WindowSizeClass.calculateFromSize(DpSize(1200.dp, 800.dp)),
- Posture()
- )
- )
-
- assertThat(layoutDirective.maxHorizontalPartitions).isEqualTo(2)
- assertThat(layoutDirective.maxVerticalPartitions).isEqualTo(1)
- assertThat(layoutDirective.gutterSizes.outerVertical).isEqualTo(24.dp)
- assertThat(layoutDirective.gutterSizes.innerVertical).isEqualTo(24.dp)
- assertThat(layoutDirective.gutterSizes.outerHorizontal).isEqualTo(24.dp)
- assertThat(layoutDirective.gutterSizes.innerHorizontal).isEqualTo(0.dp)
- }
-
- @Test
- fun test_calculateStandardAdaptiveLayoutDirective_tabletop() {
- val layoutDirective = calculateStandardAdaptiveLayoutDirective(
- WindowAdaptiveInfo(
- WindowSizeClass.calculateFromSize(DpSize(700.dp, 800.dp)),
- Posture(isTabletop = true)
- )
- )
-
- assertThat(layoutDirective.maxHorizontalPartitions).isEqualTo(1)
- assertThat(layoutDirective.maxVerticalPartitions).isEqualTo(2)
- assertThat(layoutDirective.gutterSizes.outerVertical).isEqualTo(24.dp)
- assertThat(layoutDirective.gutterSizes.innerVertical).isEqualTo(0.dp)
- assertThat(layoutDirective.gutterSizes.outerHorizontal).isEqualTo(24.dp)
- assertThat(layoutDirective.gutterSizes.innerHorizontal).isEqualTo(24.dp)
- }
-
- @Test
- fun test_calculateDenseAdaptiveLayoutDirective_compactWidth() {
- val layoutDirective = calculateDenseAdaptiveLayoutDirective(
- WindowAdaptiveInfo(
- WindowSizeClass.calculateFromSize(DpSize(400.dp, 800.dp)),
- Posture()
- )
- )
-
- assertThat(layoutDirective.maxHorizontalPartitions).isEqualTo(1)
- assertThat(layoutDirective.maxVerticalPartitions).isEqualTo(1)
- assertThat(layoutDirective.gutterSizes.outerVertical).isEqualTo(16.dp)
- assertThat(layoutDirective.gutterSizes.innerVertical).isEqualTo(0.dp)
- assertThat(layoutDirective.gutterSizes.outerHorizontal).isEqualTo(16.dp)
- assertThat(layoutDirective.gutterSizes.innerHorizontal).isEqualTo(0.dp)
- }
-
- @Test
- fun test_calculateDenseAdaptiveLayoutDirective_mediumWidth() {
- val layoutDirective = calculateDenseAdaptiveLayoutDirective(
- WindowAdaptiveInfo(
- WindowSizeClass.calculateFromSize(DpSize(750.dp, 900.dp)),
- Posture()
- )
- )
-
- assertThat(layoutDirective.maxHorizontalPartitions).isEqualTo(2)
- assertThat(layoutDirective.maxVerticalPartitions).isEqualTo(1)
- assertThat(layoutDirective.gutterSizes.outerVertical).isEqualTo(24.dp)
- assertThat(layoutDirective.gutterSizes.innerVertical).isEqualTo(24.dp)
- assertThat(layoutDirective.gutterSizes.outerHorizontal).isEqualTo(24.dp)
- assertThat(layoutDirective.gutterSizes.innerHorizontal).isEqualTo(0.dp)
- }
-
- @Test
- fun test_calculateDenseAdaptiveLayoutDirective_expandedWidth() {
- val layoutDirective = calculateDenseAdaptiveLayoutDirective(
- WindowAdaptiveInfo(
- WindowSizeClass.calculateFromSize(DpSize(1200.dp, 800.dp)),
- Posture()
- )
- )
-
- assertThat(layoutDirective.maxHorizontalPartitions).isEqualTo(2)
- assertThat(layoutDirective.maxVerticalPartitions).isEqualTo(1)
- assertThat(layoutDirective.gutterSizes.outerVertical).isEqualTo(24.dp)
- assertThat(layoutDirective.gutterSizes.innerVertical).isEqualTo(24.dp)
- assertThat(layoutDirective.gutterSizes.outerHorizontal).isEqualTo(24.dp)
- assertThat(layoutDirective.gutterSizes.innerHorizontal).isEqualTo(0.dp)
- }
-
- @Test
- fun test_calculateDenseAdaptiveLayoutDirective_tabletop() {
- val layoutDirective = calculateDenseAdaptiveLayoutDirective(
- WindowAdaptiveInfo(
- WindowSizeClass.calculateFromSize(DpSize(700.dp, 800.dp)),
- Posture(isTabletop = true)
- )
- )
-
- assertThat(layoutDirective.maxHorizontalPartitions).isEqualTo(2)
- assertThat(layoutDirective.maxVerticalPartitions).isEqualTo(2)
- assertThat(layoutDirective.gutterSizes.outerVertical).isEqualTo(24.dp)
- assertThat(layoutDirective.gutterSizes.innerVertical).isEqualTo(24.dp)
- assertThat(layoutDirective.gutterSizes.outerHorizontal).isEqualTo(24.dp)
- assertThat(layoutDirective.gutterSizes.innerHorizontal).isEqualTo(24.dp)
- }
-
- @Test
- fun test_calculateStandardAdaptiveLayoutDirective_alwaysAvoidHinge() {
- val occludingHingeBounds = listOf(
- Rect(0F, 0F, 1F, 1F),
- Rect(1F, 1F, 2F, 2F),
- )
- val allHingeBounds = listOf(
- Rect(0F, 0F, 1F, 1F),
- Rect(1F, 1F, 2F, 2F),
- Rect(2F, 2F, 3F, 3F)
- )
- val layoutDirective = calculateStandardAdaptiveLayoutDirective(
- WindowAdaptiveInfo(
- WindowSizeClass.calculateFromSize(DpSize(700.dp, 800.dp)),
- Posture(
- allHingeBounds = allHingeBounds,
- occludingHingeBounds = occludingHingeBounds
- )
- ),
- HingePolicy.AlwaysAvoid
- )
-
- assertThat(layoutDirective.excludedBounds).isEqualTo(allHingeBounds)
- }
-
- @Test
- fun test_calculateStandardAdaptiveLayoutDirective_avoidOccludingHinge() {
- val occludingHingeBounds = listOf(
- Rect(0F, 0F, 1F, 1F),
- Rect(1F, 1F, 2F, 2F),
- )
- val allHingeBounds = listOf(
- Rect(0F, 0F, 1F, 1F),
- Rect(1F, 1F, 2F, 2F),
- Rect(2F, 2F, 3F, 3F)
- )
- val layoutDirective = calculateStandardAdaptiveLayoutDirective(
- WindowAdaptiveInfo(
- WindowSizeClass.calculateFromSize(DpSize(700.dp, 800.dp)),
- Posture(
- allHingeBounds = allHingeBounds,
- occludingHingeBounds = occludingHingeBounds
- )
- ),
- HingePolicy.AvoidOccluding
- )
-
- assertThat(layoutDirective.excludedBounds).isEqualTo(occludingHingeBounds)
- }
-
- @Test
- fun test_calculateStandardAdaptiveLayoutDirective_neverAvoidHinge() {
- val occludingHingeBounds = listOf(
- Rect(0F, 0F, 1F, 1F),
- Rect(1F, 1F, 2F, 2F),
- )
- val allHingeBounds = listOf(
- Rect(0F, 0F, 1F, 1F),
- Rect(1F, 1F, 2F, 2F),
- Rect(2F, 2F, 3F, 3F)
- )
- val layoutDirective = calculateStandardAdaptiveLayoutDirective(
- WindowAdaptiveInfo(
- WindowSizeClass.calculateFromSize(DpSize(700.dp, 800.dp)),
- Posture(
- allHingeBounds = allHingeBounds,
- occludingHingeBounds = occludingHingeBounds
- )
- ),
- HingePolicy.NeverAvoid
- )
-
- assertThat(layoutDirective.excludedBounds).isEmpty()
- }
-
- @Test
- fun test_calculateDenseAdaptiveLayoutDirective_alwaysAvoidHinge() {
- val occludingHingeBounds = listOf(
- Rect(0F, 0F, 1F, 1F),
- Rect(1F, 1F, 2F, 2F),
- )
- val allHingeBounds = listOf(
- Rect(0F, 0F, 1F, 1F),
- Rect(1F, 1F, 2F, 2F),
- Rect(2F, 2F, 3F, 3F)
- )
- val layoutDirective = calculateDenseAdaptiveLayoutDirective(
- WindowAdaptiveInfo(
- WindowSizeClass.calculateFromSize(DpSize(700.dp, 800.dp)),
- Posture(
- allHingeBounds = allHingeBounds,
- occludingHingeBounds = occludingHingeBounds
- )
- ),
- HingePolicy.AlwaysAvoid
- )
-
- assertThat(layoutDirective.excludedBounds).isEqualTo(allHingeBounds)
- }
-
- @Test
- fun test_calculateDenseAdaptiveLayoutDirective_avoidOccludingHinge() {
- val occludingHingeBounds = listOf(
- Rect(0F, 0F, 1F, 1F),
- Rect(1F, 1F, 2F, 2F),
- )
- val allHingeBounds = listOf(
- Rect(0F, 0F, 1F, 1F),
- Rect(1F, 1F, 2F, 2F),
- Rect(2F, 2F, 3F, 3F)
- )
- val layoutDirective = calculateDenseAdaptiveLayoutDirective(
- WindowAdaptiveInfo(
- WindowSizeClass.calculateFromSize(DpSize(700.dp, 800.dp)),
- Posture(
- allHingeBounds = allHingeBounds,
- occludingHingeBounds = occludingHingeBounds
- )
- ),
- HingePolicy.AvoidOccluding
- )
-
- assertThat(layoutDirective.excludedBounds).isEqualTo(occludingHingeBounds)
- }
-
- @Test
- fun test_calculateDenseAdaptiveLayoutDirective_neverAvoidHinge() {
- val occludingHingeBounds = listOf(
- Rect(0F, 0F, 1F, 1F),
- Rect(1F, 1F, 2F, 2F),
- )
- val allHingeBounds = listOf(
- Rect(0F, 0F, 1F, 1F),
- Rect(1F, 1F, 2F, 2F),
- Rect(2F, 2F, 3F, 3F)
- )
- val layoutDirective = calculateDenseAdaptiveLayoutDirective(
- WindowAdaptiveInfo(
- WindowSizeClass.calculateFromSize(DpSize(700.dp, 800.dp)),
- Posture(
- allHingeBounds = allHingeBounds,
- occludingHingeBounds = occludingHingeBounds
- )
- ),
- HingePolicy.NeverAvoid
- )
-
- assertThat(layoutDirective.excludedBounds).isEmpty()
- }
-}
diff --git a/compose/material3/material3-adaptive/src/androidUnitTest/kotlin/androidx/compose/material3/adaptive/NavigationSuiteScaffoldTest.kt b/compose/material3/material3-adaptive/src/androidUnitTest/kotlin/androidx/compose/material3/adaptive/NavigationSuiteScaffoldTest.kt
index 359f940..6ab65eb 100644
--- a/compose/material3/material3-adaptive/src/androidUnitTest/kotlin/androidx/compose/material3/adaptive/NavigationSuiteScaffoldTest.kt
+++ b/compose/material3/material3-adaptive/src/androidUnitTest/kotlin/androidx/compose/material3/adaptive/NavigationSuiteScaffoldTest.kt
@@ -81,7 +81,7 @@
)
assertThat(NavigationSuiteScaffoldDefaults.calculateFromAdaptiveInfo(mockAdaptiveInfo))
- .isEqualTo(NavigationSuiteType.NavigationBar)
+ .isEqualTo(NavigationSuiteType.NavigationRail)
}
@Test
@@ -92,7 +92,7 @@
)
assertThat(NavigationSuiteScaffoldDefaults.calculateFromAdaptiveInfo(mockAdaptiveInfo))
- .isEqualTo(NavigationSuiteType.NavigationBar)
+ .isEqualTo(NavigationSuiteType.NavigationRail)
}
@Test
diff --git a/compose/material3/material3-adaptive/src/androidUnitTest/kotlin/androidx/compose/material3/adaptive/PaneScaffoldDirectiveTest.kt b/compose/material3/material3-adaptive/src/androidUnitTest/kotlin/androidx/compose/material3/adaptive/PaneScaffoldDirectiveTest.kt
new file mode 100644
index 0000000..68c5553
--- /dev/null
+++ b/compose/material3/material3-adaptive/src/androidUnitTest/kotlin/androidx/compose/material3/adaptive/PaneScaffoldDirectiveTest.kt
@@ -0,0 +1,317 @@
+/*
+ * 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.material3.adaptive
+
+import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi
+import androidx.compose.material3.windowsizeclass.WindowSizeClass
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.unit.DpSize
+import androidx.compose.ui.unit.dp
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@OptIn(ExperimentalMaterial3AdaptiveApi::class, ExperimentalMaterial3WindowSizeClassApi::class)
+@RunWith(JUnit4::class)
+class PaneScaffoldDirectiveTest {
+ @Test
+ fun test_calculateStandardPaneScaffoldDirective_compactWidth() {
+ val scaffoldDirective = calculateStandardPaneScaffoldDirective(
+ WindowAdaptiveInfo(
+ WindowSizeClass.calculateFromSize(DpSize(400.dp, 800.dp)),
+ Posture()
+ )
+ )
+
+ assertThat(scaffoldDirective.maxHorizontalPartitions).isEqualTo(1)
+ assertThat(scaffoldDirective.maxVerticalPartitions).isEqualTo(1)
+ assertThat(scaffoldDirective.gutterSizes.outerVertical).isEqualTo(16.dp)
+ assertThat(scaffoldDirective.gutterSizes.innerVertical).isEqualTo(0.dp)
+ assertThat(scaffoldDirective.gutterSizes.outerHorizontal).isEqualTo(16.dp)
+ assertThat(scaffoldDirective.gutterSizes.innerHorizontal).isEqualTo(0.dp)
+ }
+
+ @Test
+ fun test_calculateStandardPaneScaffoldDirective_mediumWidth() {
+ val scaffoldDirective = calculateStandardPaneScaffoldDirective(
+ WindowAdaptiveInfo(
+ WindowSizeClass.calculateFromSize(DpSize(750.dp, 900.dp)),
+ Posture()
+ )
+ )
+
+ assertThat(scaffoldDirective.maxHorizontalPartitions).isEqualTo(1)
+ assertThat(scaffoldDirective.maxVerticalPartitions).isEqualTo(1)
+ assertThat(scaffoldDirective.gutterSizes.outerVertical).isEqualTo(24.dp)
+ assertThat(scaffoldDirective.gutterSizes.innerVertical).isEqualTo(0.dp)
+ assertThat(scaffoldDirective.gutterSizes.outerHorizontal).isEqualTo(24.dp)
+ assertThat(scaffoldDirective.gutterSizes.innerHorizontal).isEqualTo(0.dp)
+ }
+
+ @Test
+ fun test_calculateStandardPaneScaffoldDirective_expandedWidth() {
+ val scaffoldDirective = calculateStandardPaneScaffoldDirective(
+ WindowAdaptiveInfo(
+ WindowSizeClass.calculateFromSize(DpSize(1200.dp, 800.dp)),
+ Posture()
+ )
+ )
+
+ assertThat(scaffoldDirective.maxHorizontalPartitions).isEqualTo(2)
+ assertThat(scaffoldDirective.maxVerticalPartitions).isEqualTo(1)
+ assertThat(scaffoldDirective.gutterSizes.outerVertical).isEqualTo(24.dp)
+ assertThat(scaffoldDirective.gutterSizes.innerVertical).isEqualTo(24.dp)
+ assertThat(scaffoldDirective.gutterSizes.outerHorizontal).isEqualTo(24.dp)
+ assertThat(scaffoldDirective.gutterSizes.innerHorizontal).isEqualTo(0.dp)
+ }
+
+ @Test
+ fun test_calculateStandardPaneScaffoldDirective_tabletop() {
+ val scaffoldDirective = calculateStandardPaneScaffoldDirective(
+ WindowAdaptiveInfo(
+ WindowSizeClass.calculateFromSize(DpSize(700.dp, 800.dp)),
+ Posture(isTabletop = true)
+ )
+ )
+
+ assertThat(scaffoldDirective.maxHorizontalPartitions).isEqualTo(1)
+ assertThat(scaffoldDirective.maxVerticalPartitions).isEqualTo(2)
+ assertThat(scaffoldDirective.gutterSizes.outerVertical).isEqualTo(24.dp)
+ assertThat(scaffoldDirective.gutterSizes.innerVertical).isEqualTo(0.dp)
+ assertThat(scaffoldDirective.gutterSizes.outerHorizontal).isEqualTo(24.dp)
+ assertThat(scaffoldDirective.gutterSizes.innerHorizontal).isEqualTo(24.dp)
+ }
+
+ @Test
+ fun test_calculateDensePaneScaffoldDirective_compactWidth() {
+ val scaffoldDirective = calculateDensePaneScaffoldDirective(
+ WindowAdaptiveInfo(
+ WindowSizeClass.calculateFromSize(DpSize(400.dp, 800.dp)),
+ Posture()
+ )
+ )
+
+ assertThat(scaffoldDirective.maxHorizontalPartitions).isEqualTo(1)
+ assertThat(scaffoldDirective.maxVerticalPartitions).isEqualTo(1)
+ assertThat(scaffoldDirective.gutterSizes.outerVertical).isEqualTo(16.dp)
+ assertThat(scaffoldDirective.gutterSizes.innerVertical).isEqualTo(0.dp)
+ assertThat(scaffoldDirective.gutterSizes.outerHorizontal).isEqualTo(16.dp)
+ assertThat(scaffoldDirective.gutterSizes.innerHorizontal).isEqualTo(0.dp)
+ }
+
+ @Test
+ fun test_calculateDensePaneScaffoldDirective_mediumWidth() {
+ val scaffoldDirective = calculateDensePaneScaffoldDirective(
+ WindowAdaptiveInfo(
+ WindowSizeClass.calculateFromSize(DpSize(750.dp, 900.dp)),
+ Posture()
+ )
+ )
+
+ assertThat(scaffoldDirective.maxHorizontalPartitions).isEqualTo(2)
+ assertThat(scaffoldDirective.maxVerticalPartitions).isEqualTo(1)
+ assertThat(scaffoldDirective.gutterSizes.outerVertical).isEqualTo(24.dp)
+ assertThat(scaffoldDirective.gutterSizes.innerVertical).isEqualTo(24.dp)
+ assertThat(scaffoldDirective.gutterSizes.outerHorizontal).isEqualTo(24.dp)
+ assertThat(scaffoldDirective.gutterSizes.innerHorizontal).isEqualTo(0.dp)
+ }
+
+ @Test
+ fun test_calculateDensePaneScaffoldDirective_expandedWidth() {
+ val scaffoldDirective = calculateDensePaneScaffoldDirective(
+ WindowAdaptiveInfo(
+ WindowSizeClass.calculateFromSize(DpSize(1200.dp, 800.dp)),
+ Posture()
+ )
+ )
+
+ assertThat(scaffoldDirective.maxHorizontalPartitions).isEqualTo(2)
+ assertThat(scaffoldDirective.maxVerticalPartitions).isEqualTo(1)
+ assertThat(scaffoldDirective.gutterSizes.outerVertical).isEqualTo(24.dp)
+ assertThat(scaffoldDirective.gutterSizes.innerVertical).isEqualTo(24.dp)
+ assertThat(scaffoldDirective.gutterSizes.outerHorizontal).isEqualTo(24.dp)
+ assertThat(scaffoldDirective.gutterSizes.innerHorizontal).isEqualTo(0.dp)
+ }
+
+ @Test
+ fun test_calculateDensePaneScaffoldDirective_tabletop() {
+ val scaffoldDirective = calculateDensePaneScaffoldDirective(
+ WindowAdaptiveInfo(
+ WindowSizeClass.calculateFromSize(DpSize(700.dp, 800.dp)),
+ Posture(isTabletop = true)
+ )
+ )
+
+ assertThat(scaffoldDirective.maxHorizontalPartitions).isEqualTo(2)
+ assertThat(scaffoldDirective.maxVerticalPartitions).isEqualTo(2)
+ assertThat(scaffoldDirective.gutterSizes.outerVertical).isEqualTo(24.dp)
+ assertThat(scaffoldDirective.gutterSizes.innerVertical).isEqualTo(24.dp)
+ assertThat(scaffoldDirective.gutterSizes.outerHorizontal).isEqualTo(24.dp)
+ assertThat(scaffoldDirective.gutterSizes.innerHorizontal).isEqualTo(24.dp)
+ }
+
+ @Test
+ fun test_calculateStandardPaneScaffoldDirective_alwaysAvoidHinge() {
+ val occludingHingeBounds = listOf(
+ Rect(0F, 0F, 1F, 1F),
+ Rect(1F, 1F, 2F, 2F),
+ )
+ val allHingeBounds = listOf(
+ Rect(0F, 0F, 1F, 1F),
+ Rect(1F, 1F, 2F, 2F),
+ Rect(2F, 2F, 3F, 3F)
+ )
+ val scaffoldDirective = calculateStandardPaneScaffoldDirective(
+ WindowAdaptiveInfo(
+ WindowSizeClass.calculateFromSize(DpSize(700.dp, 800.dp)),
+ Posture(
+ allHingeBounds = allHingeBounds,
+ occludingHingeBounds = occludingHingeBounds
+ )
+ ),
+ HingePolicy.AlwaysAvoid
+ )
+
+ assertThat(scaffoldDirective.excludedBounds).isEqualTo(allHingeBounds)
+ }
+
+ @Test
+ fun test_calculateStandardPaneScaffoldDirective_avoidOccludingHinge() {
+ val occludingHingeBounds = listOf(
+ Rect(0F, 0F, 1F, 1F),
+ Rect(1F, 1F, 2F, 2F),
+ )
+ val allHingeBounds = listOf(
+ Rect(0F, 0F, 1F, 1F),
+ Rect(1F, 1F, 2F, 2F),
+ Rect(2F, 2F, 3F, 3F)
+ )
+ val scaffoldDirective = calculateStandardPaneScaffoldDirective(
+ WindowAdaptiveInfo(
+ WindowSizeClass.calculateFromSize(DpSize(700.dp, 800.dp)),
+ Posture(
+ allHingeBounds = allHingeBounds,
+ occludingHingeBounds = occludingHingeBounds
+ )
+ ),
+ HingePolicy.AvoidOccluding
+ )
+
+ assertThat(scaffoldDirective.excludedBounds).isEqualTo(occludingHingeBounds)
+ }
+
+ @Test
+ fun test_calculateStandardPaneScaffoldDirective_neverAvoidHinge() {
+ val occludingHingeBounds = listOf(
+ Rect(0F, 0F, 1F, 1F),
+ Rect(1F, 1F, 2F, 2F),
+ )
+ val allHingeBounds = listOf(
+ Rect(0F, 0F, 1F, 1F),
+ Rect(1F, 1F, 2F, 2F),
+ Rect(2F, 2F, 3F, 3F)
+ )
+ val scaffoldDirective = calculateStandardPaneScaffoldDirective(
+ WindowAdaptiveInfo(
+ WindowSizeClass.calculateFromSize(DpSize(700.dp, 800.dp)),
+ Posture(
+ allHingeBounds = allHingeBounds,
+ occludingHingeBounds = occludingHingeBounds
+ )
+ ),
+ HingePolicy.NeverAvoid
+ )
+
+ assertThat(scaffoldDirective.excludedBounds).isEmpty()
+ }
+
+ @Test
+ fun test_calculateDensePaneScaffoldDirective_alwaysAvoidHinge() {
+ val occludingHingeBounds = listOf(
+ Rect(0F, 0F, 1F, 1F),
+ Rect(1F, 1F, 2F, 2F),
+ )
+ val allHingeBounds = listOf(
+ Rect(0F, 0F, 1F, 1F),
+ Rect(1F, 1F, 2F, 2F),
+ Rect(2F, 2F, 3F, 3F)
+ )
+ val scaffoldDirective = calculateDensePaneScaffoldDirective(
+ WindowAdaptiveInfo(
+ WindowSizeClass.calculateFromSize(DpSize(700.dp, 800.dp)),
+ Posture(
+ allHingeBounds = allHingeBounds,
+ occludingHingeBounds = occludingHingeBounds
+ )
+ ),
+ HingePolicy.AlwaysAvoid
+ )
+
+ assertThat(scaffoldDirective.excludedBounds).isEqualTo(allHingeBounds)
+ }
+
+ @Test
+ fun test_calculateDensePaneScaffoldDirective_avoidOccludingHinge() {
+ val occludingHingeBounds = listOf(
+ Rect(0F, 0F, 1F, 1F),
+ Rect(1F, 1F, 2F, 2F),
+ )
+ val allHingeBounds = listOf(
+ Rect(0F, 0F, 1F, 1F),
+ Rect(1F, 1F, 2F, 2F),
+ Rect(2F, 2F, 3F, 3F)
+ )
+ val scaffoldDirective = calculateDensePaneScaffoldDirective(
+ WindowAdaptiveInfo(
+ WindowSizeClass.calculateFromSize(DpSize(700.dp, 800.dp)),
+ Posture(
+ allHingeBounds = allHingeBounds,
+ occludingHingeBounds = occludingHingeBounds
+ )
+ ),
+ HingePolicy.AvoidOccluding
+ )
+
+ assertThat(scaffoldDirective.excludedBounds).isEqualTo(occludingHingeBounds)
+ }
+
+ @Test
+ fun test_calculateDensePaneScaffoldDirective_neverAvoidHinge() {
+ val occludingHingeBounds = listOf(
+ Rect(0F, 0F, 1F, 1F),
+ Rect(1F, 1F, 2F, 2F),
+ )
+ val allHingeBounds = listOf(
+ Rect(0F, 0F, 1F, 1F),
+ Rect(1F, 1F, 2F, 2F),
+ Rect(2F, 2F, 3F, 3F)
+ )
+ val scaffoldDirective = calculateDensePaneScaffoldDirective(
+ WindowAdaptiveInfo(
+ WindowSizeClass.calculateFromSize(DpSize(700.dp, 800.dp)),
+ Posture(
+ allHingeBounds = allHingeBounds,
+ occludingHingeBounds = occludingHingeBounds
+ )
+ ),
+ HingePolicy.NeverAvoid
+ )
+
+ assertThat(scaffoldDirective.excludedBounds).isEmpty()
+ }
+}
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 0d60c95..677926b 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
@@ -43,6 +43,7 @@
import androidx.compose.material3.contentColorFor
import androidx.compose.material3.windowsizeclass.WindowHeightSizeClass.Companion.Compact
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass.Companion.Expanded
+import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass.Companion.Medium
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.collection.MutableVector
@@ -362,7 +363,9 @@
return with(adaptiveInfo) {
if (posture.isTabletop || windowSizeClass.heightSizeClass == Compact) {
NavigationSuiteType.NavigationBar
- } else if (windowSizeClass.widthSizeClass == Expanded) {
+ } else if (windowSizeClass.widthSizeClass == Expanded ||
+ windowSizeClass.widthSizeClass == Medium
+ ) {
NavigationSuiteType.NavigationRail
} else {
NavigationSuiteType.NavigationBar
diff --git a/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/AdaptiveLayoutDirective.kt b/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/PaneScaffoldDirective.kt
similarity index 92%
rename from compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/AdaptiveLayoutDirective.kt
rename to compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/PaneScaffoldDirective.kt
index d012a95a..ca23073 100644
--- a/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/AdaptiveLayoutDirective.kt
+++ b/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/PaneScaffoldDirective.kt
@@ -23,7 +23,7 @@
import androidx.compose.ui.unit.dp
/**
- * Calculates the standard [AdaptiveLayoutDirective] from a given [WindowAdaptiveInfo]. Use this
+ * Calculates the standard [PaneScaffoldDirective] from a given [WindowAdaptiveInfo]. Use this
* method with [calculateWindowAdaptiveInfo] to acquire Material-recommended adaptive layout
* settings of the current activity window.
*
@@ -33,14 +33,14 @@
* @param windowAdaptiveInfo [WindowAdaptiveInfo] that collects useful information in making
* layout adaptation decisions like [WindowSizeClass].
* @param hingePolicy [HingePolicy] that decides how layouts are supposed to address hinges.
- * @return an [AdaptiveLayoutDirective] to be used to decide adaptive layout states.
+ * @return an [PaneScaffoldDirective] to be used to decide adaptive layout states.
*/
// TODO(b/285144647): Add more details regarding the use scenarios of this function.
@ExperimentalMaterial3AdaptiveApi
-fun calculateStandardAdaptiveLayoutDirective(
+fun calculateStandardPaneScaffoldDirective(
windowAdaptiveInfo: WindowAdaptiveInfo,
hingePolicy: HingePolicy = HingePolicy.AvoidSeparating
-): AdaptiveLayoutDirective {
+): PaneScaffoldDirective {
val maxHorizontalPartitions: Int
val gutterOuterVertical: Dp
val gutterInnerVertical: Dp
@@ -73,7 +73,7 @@
gutterInnerHorizontal = 0.dp
}
- return AdaptiveLayoutDirective(
+ return PaneScaffoldDirective(
maxHorizontalPartitions,
GutterSizes(
gutterOuterVertical, gutterInnerVertical, innerHorizontal = gutterInnerHorizontal
@@ -84,7 +84,7 @@
}
/**
- * Calculates the dense-mode [AdaptiveLayoutDirective] from a given [WindowAdaptiveInfo]. Use this
+ * Calculates the dense-mode [PaneScaffoldDirective] from a given [WindowAdaptiveInfo]. Use this
* method with [calculateWindowAdaptiveInfo] to acquire Material-recommended dense-mode adaptive
* layout settings of the current activity window.
*
@@ -94,14 +94,14 @@
* @param windowAdaptiveInfo [WindowAdaptiveInfo] that collects useful information in making
* layout adaptation decisions like [WindowSizeClass].
* @param hingePolicy [HingePolicy] that decides how layouts are supposed to address hinges.
- * @return an [AdaptiveLayoutDirective] to be used to decide adaptive layout states.
+ * @return an [PaneScaffoldDirective] to be used to decide adaptive layout states.
*/
// TODO(b/285144647): Add more details regarding the use scenarios of this function.
@ExperimentalMaterial3AdaptiveApi
-fun calculateDenseAdaptiveLayoutDirective(
+fun calculateDensePaneScaffoldDirective(
windowAdaptiveInfo: WindowAdaptiveInfo,
hingePolicy: HingePolicy = HingePolicy.AvoidSeparating
-): AdaptiveLayoutDirective {
+): PaneScaffoldDirective {
val maxHorizontalPartitions: Int
val gutterOuterVertical: Dp
val gutterInnerVertical: Dp
@@ -134,7 +134,7 @@
gutterInnerHorizontal = 0.dp
}
- return AdaptiveLayoutDirective(
+ return PaneScaffoldDirective(
maxHorizontalPartitions,
GutterSizes(
gutterOuterVertical, gutterInnerVertical, innerHorizontal = gutterInnerHorizontal
@@ -155,10 +155,10 @@
}
/**
- * Top-level directives about how an adaptive layout should be arranged and spaced, like how many
+ * Top-level directives about how a pane scaffold should be arranged and spaced, like how many
* partitions the layout can be split into and what should be the gutter size.
*
- * @constructor create an instance of [AdaptiveLayoutDirective]
+ * @constructor create an instance of [PaneScaffoldDirective]
* @param maxHorizontalPartitions the max number of partitions along the horizontal axis the layout
* can be split into.
* @param gutterSizes the gutter sizes between panes the layout should preserve.
@@ -169,7 +169,7 @@
*/
@ExperimentalMaterial3AdaptiveApi
@Immutable
-class AdaptiveLayoutDirective(
+class PaneScaffoldDirective(
val maxHorizontalPartitions: Int,
val gutterSizes: GutterSizes,
val maxVerticalPartitions: Int,
@@ -177,7 +177,7 @@
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
- if (other !is AdaptiveLayoutDirective) return false
+ if (other !is PaneScaffoldDirective) return false
if (maxHorizontalPartitions != other.maxHorizontalPartitions) return false
if (gutterSizes != other.gutterSizes) return false
if (maxVerticalPartitions != other.maxVerticalPartitions) return false
diff --git a/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffold.kt b/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffold.kt
index 9d35814..66b5b3a 100644
--- a/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffold.kt
+++ b/compose/material3/material3-adaptive/src/commonMain/kotlin/androidx/compose/material3/adaptive/ThreePaneScaffold.kt
@@ -54,18 +54,18 @@
/**
* A pane scaffold composable that can display up to three panes according to the instructions
* provided by [ThreePaneScaffoldValue] in the order that [ThreePaneScaffoldArrangement] specifies,
- * and allocate margins and spacers according to [AdaptiveLayoutDirective].
+ * and allocate margins and spacers according to [PaneScaffoldDirective].
*
* [ThreePaneScaffold] is the base composable functions of adaptive programming. Developers can
* freely pipeline the relevant adaptive signals and use them as input of the scaffold function
* to render the final adaptive layout.
*
- * It's recommended to use [ThreePaneScaffold] with [calculateStandardAdaptiveLayoutDirective],
+ * It's recommended to use [ThreePaneScaffold] with [calculateStandardPaneScaffoldDirective],
* [calculateThreePaneScaffoldValue] to follow the Material design guidelines on adaptive
* programming.
*
* @param modifier The modifier to be applied to the layout.
- * @param layoutDirective The top-level directives about how the scaffold should arrange its panes.
+ * @param scaffoldDirective The top-level directives about how the scaffold should arrange its panes.
* @param scaffoldValue The current adapted value of the scaffold.
* @param arrangement The arrangement of the panes in the scaffold.
* @param secondaryPane The content of the secondary pane that has a priority lower then the primary
@@ -77,7 +77,7 @@
@Composable
fun ThreePaneScaffold(
modifier: Modifier,
- layoutDirective: AdaptiveLayoutDirective,
+ scaffoldDirective: PaneScaffoldDirective,
scaffoldValue: ThreePaneScaffoldValue,
arrangement: ThreePaneScaffoldArrangement,
secondaryPane: @Composable ThreePaneScaffoldScope.(PaneAdaptedValue) -> Unit,
@@ -146,8 +146,8 @@
)
val measurePolicy =
- remember { ThreePaneContentMeasurePolicy(layoutDirective, scaffoldValue, arrangement) }
- measurePolicy.layoutDirective = layoutDirective
+ remember { ThreePaneContentMeasurePolicy(scaffoldDirective, scaffoldValue, arrangement) }
+ measurePolicy.scaffoldDirective = scaffoldDirective
measurePolicy.scaffoldValue = scaffoldValue
measurePolicy.arrangement = arrangement
@@ -320,7 +320,7 @@
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
private class ThreePaneContentMeasurePolicy(
- var layoutDirective: AdaptiveLayoutDirective,
+ var scaffoldDirective: PaneScaffoldDirective,
var scaffoldValue: ThreePaneScaffoldValue,
var arrangement: ThreePaneScaffoldArrangement
) : MultiContentMeasurePolicy {
@@ -368,10 +368,10 @@
it == PaneAdaptedValue.Hidden
}
- val outerVerticalGutterSize = layoutDirective.gutterSizes.outerVertical.roundToPx()
- val innerVerticalGutterSize = layoutDirective.gutterSizes.innerVertical.roundToPx()
+ val outerVerticalGutterSize = scaffoldDirective.gutterSizes.outerVertical.roundToPx()
+ val innerVerticalGutterSize = scaffoldDirective.gutterSizes.innerVertical.roundToPx()
val outerHorizontalGutterSize =
- layoutDirective.gutterSizes.outerHorizontal.roundToPx()
+ scaffoldDirective.gutterSizes.outerHorizontal.roundToPx()
val outerBounds = IntRect(
outerVerticalGutterSize,
outerHorizontalGutterSize,
@@ -379,7 +379,7 @@
constraints.maxHeight - outerHorizontalGutterSize
)
- if (layoutDirective.excludedBounds.isNotEmpty()) {
+ if (scaffoldDirective.excludedBounds.isNotEmpty()) {
val layoutBounds = coordinates!!.boundsInWindow()
val layoutPhysicalPartitions = mutableListOf<Rect>()
var actualLeft = layoutBounds.left + outerVerticalGutterSize
@@ -387,7 +387,7 @@
val actualTop = layoutBounds.top + outerHorizontalGutterSize
val actualBottom = layoutBounds.bottom - outerHorizontalGutterSize
// Assume hinge bounds are sorted from left to right, non-overlapped.
- layoutDirective.excludedBounds.fastForEach { hingeBound ->
+ scaffoldDirective.excludedBounds.fastForEach { hingeBound ->
if (hingeBound.left <= actualLeft) {
// The hinge is at the left of the layout, adjust the left edge of
// the current partition to the actual displayable bounds.
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ProgressIndicator.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ProgressIndicator.kt
index b83db8b..c22169b 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ProgressIndicator.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/ProgressIndicator.kt
@@ -131,7 +131,7 @@
infiniteRepeatable(
animation = keyframes {
durationMillis = LinearAnimationDuration
- 0f at FirstLineHeadDelay with FirstLineHeadEasing
+ 0f at FirstLineHeadDelay using FirstLineHeadEasing
1f at FirstLineHeadDuration + FirstLineHeadDelay
}
)
@@ -142,7 +142,7 @@
infiniteRepeatable(
animation = keyframes {
durationMillis = LinearAnimationDuration
- 0f at FirstLineTailDelay with FirstLineTailEasing
+ 0f at FirstLineTailDelay using FirstLineTailEasing
1f at FirstLineTailDuration + FirstLineTailDelay
}
)
@@ -153,7 +153,7 @@
infiniteRepeatable(
animation = keyframes {
durationMillis = LinearAnimationDuration
- 0f at SecondLineHeadDelay with SecondLineHeadEasing
+ 0f at SecondLineHeadDelay using SecondLineHeadEasing
1f at SecondLineHeadDuration + SecondLineHeadDelay
}
)
@@ -164,7 +164,7 @@
infiniteRepeatable(
animation = keyframes {
durationMillis = LinearAnimationDuration
- 0f at SecondLineTailDelay with SecondLineTailEasing
+ 0f at SecondLineTailDelay using SecondLineTailEasing
1f at SecondLineTailDuration + SecondLineTailDelay
}
)
@@ -431,7 +431,7 @@
infiniteRepeatable(
animation = keyframes {
durationMillis = HeadAndTailAnimationDuration + HeadAndTailDelayDuration
- 0f at 0 with CircularEasing
+ 0f at 0 using CircularEasing
JumpRotationAngle at HeadAndTailAnimationDuration
}
)
@@ -442,7 +442,7 @@
infiniteRepeatable(
animation = keyframes {
durationMillis = HeadAndTailAnimationDuration + HeadAndTailDelayDuration
- 0f at HeadAndTailDelayDuration with CircularEasing
+ 0f at HeadAndTailDelayDuration using CircularEasing
JumpRotationAngle at durationMillis
}
)
diff --git a/compose/ui/ui-lint/src/main/java/androidx/compose/ui/lint/SuspiciousCompositionLocalModifierReadDetector.kt b/compose/ui/ui-lint/src/main/java/androidx/compose/ui/lint/SuspiciousCompositionLocalModifierReadDetector.kt
index 5c7e102..6911407 100644
--- a/compose/ui/ui-lint/src/main/java/androidx/compose/ui/lint/SuspiciousCompositionLocalModifierReadDetector.kt
+++ b/compose/ui/ui-lint/src/main/java/androidx/compose/ui/lint/SuspiciousCompositionLocalModifierReadDetector.kt
@@ -18,7 +18,6 @@
import androidx.compose.lint.Names
import androidx.compose.lint.Package
-import androidx.compose.lint.PackageName
import androidx.compose.lint.isInPackageName
import com.android.tools.lint.detector.api.Category
import com.android.tools.lint.detector.api.Detector
@@ -35,6 +34,7 @@
import org.jetbrains.uast.UElement
import org.jetbrains.uast.UMethod
import org.jetbrains.uast.kotlin.KotlinUFunctionCallExpression
+import org.jetbrains.uast.kotlin.KotlinULambdaExpression
@Suppress("UnstableApiUsage")
class SuspiciousCompositionLocalModifierReadDetector : Detector(), SourceCodeScanner {
@@ -46,10 +46,11 @@
override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
if (!method.isInPackageName(Names.Ui.Node.PackageName)) return
- reportIfAnyParentIsNodeLifecycleCallback(context, node, node)
+ reportIfInNodeLifecycleCallback(context, node, node)
+ reportIfInLazyBlock(context, node, node)
}
- private tailrec fun reportIfAnyParentIsNodeLifecycleCallback(
+ private tailrec fun reportIfInNodeLifecycleCallback(
context: JavaContext,
node: UElement?,
usage: UCallExpression
@@ -75,6 +76,20 @@
}
}
return
+ } else if (node is KotlinULambdaExpression.Body) {
+ return
+ }
+
+ reportIfInNodeLifecycleCallback(context, node.uastParent, usage)
+ }
+
+ private tailrec fun reportIfInLazyBlock(
+ context: JavaContext,
+ node: UElement?,
+ usage: UCallExpression
+ ) {
+ if (node == null) {
+ return
} else if (node is KotlinUFunctionCallExpression && node.isLazyDelegate()) {
report(context, usage) { localBeingRead ->
"Reading $localBeingRead lazily will only access the CompositionLocal's value " +
@@ -84,7 +99,7 @@
return
}
- reportIfAnyParentIsNodeLifecycleCallback(context, node.uastParent, usage)
+ reportIfInLazyBlock(context, node.uastParent, usage)
}
private inline fun report(
diff --git a/compose/ui/ui-lint/src/test/java/androidx/compose/ui/lint/SuspiciousCompositionLocalModifierReadDetectorTest.kt b/compose/ui/ui-lint/src/test/java/androidx/compose/ui/lint/SuspiciousCompositionLocalModifierReadDetectorTest.kt
index 8a8bc0d..6bd0907 100644
--- a/compose/ui/ui-lint/src/test/java/androidx/compose/ui/lint/SuspiciousCompositionLocalModifierReadDetectorTest.kt
+++ b/compose/ui/ui-lint/src/test/java/androidx/compose/ui/lint/SuspiciousCompositionLocalModifierReadDetectorTest.kt
@@ -133,6 +133,46 @@
}
@Test
+ fun testCompositionLocalReadInAttachDetachLambdaNotReported() {
+ lint().files(
+ kotlin(
+ """
+ package test
+
+ import androidx.compose.ui.Modifier
+ import androidx.compose.ui.node.CompositionLocalConsumerModifierNode
+ import androidx.compose.ui.node.currentValueOf
+ import androidx.compose.runtime.CompositionLocal
+ import androidx.compose.runtime.compositionLocalOf
+ import androidx.compose.runtime.staticCompositionLocalOf
+
+ val staticLocalInt = staticCompositionLocalOf { 0 }
+ val localInt = compositionLocalOf { 0 }
+
+ class NodeUnderTest : Modifier.Node(), CompositionLocalConsumerModifierNode {
+ override fun onAttach() {
+ func { val readValue = currentValueOf(localInt) }
+ }
+
+ override fun onDetach() {
+ func { val readValue = currentValueOf(staticLocalInt) }
+ }
+
+ private inline fun func(block: () -> Unit) {
+ block()
+ }
+ }
+ """
+ ),
+ CompositionLocalStub,
+ CompositionLocalConsumerModifierStub,
+ ModifierNodeStub
+ )
+ .run()
+ .expectClean()
+ }
+
+ @Test
fun testCompositionLocalReadInModifierInitializer() {
lint().files(
kotlin(
diff --git a/compose/ui/ui-tooling-preview/api/current.txt b/compose/ui/ui-tooling-preview/api/current.txt
index e43cd20..bee1c29 100644
--- a/compose/ui/ui-tooling-preview/api/current.txt
+++ b/compose/ui/ui-tooling-preview/api/current.txt
@@ -124,7 +124,7 @@
property public kotlin.sequences.Sequence<T> values;
}
- public final class LoremIpsum implements androidx.compose.ui.tooling.preview.PreviewParameterProvider<java.lang.String> {
+ public class LoremIpsum implements androidx.compose.ui.tooling.preview.PreviewParameterProvider<java.lang.String> {
ctor public LoremIpsum();
ctor public LoremIpsum(int words);
method public kotlin.sequences.Sequence<java.lang.String> getValues();
diff --git a/compose/ui/ui-tooling-preview/api/restricted_current.txt b/compose/ui/ui-tooling-preview/api/restricted_current.txt
index e43cd20..bee1c29 100644
--- a/compose/ui/ui-tooling-preview/api/restricted_current.txt
+++ b/compose/ui/ui-tooling-preview/api/restricted_current.txt
@@ -124,7 +124,7 @@
property public kotlin.sequences.Sequence<T> values;
}
- public final class LoremIpsum implements androidx.compose.ui.tooling.preview.PreviewParameterProvider<java.lang.String> {
+ public class LoremIpsum implements androidx.compose.ui.tooling.preview.PreviewParameterProvider<java.lang.String> {
ctor public LoremIpsum();
ctor public LoremIpsum(int words);
method public kotlin.sequences.Sequence<java.lang.String> getValues();
diff --git a/compose/ui/ui-tooling-preview/src/androidMain/kotlin/androidx/compose/ui/tooling/preview/datasource/LoremIpsum.kt b/compose/ui/ui-tooling-preview/src/androidMain/kotlin/androidx/compose/ui/tooling/preview/datasource/LoremIpsum.kt
index a02e165..dec0f53 100644
--- a/compose/ui/ui-tooling-preview/src/androidMain/kotlin/androidx/compose/ui/tooling/preview/datasource/LoremIpsum.kt
+++ b/compose/ui/ui-tooling-preview/src/androidMain/kotlin/androidx/compose/ui/tooling/preview/datasource/LoremIpsum.kt
@@ -43,7 +43,7 @@
*
* @param words Number of words from "Lorem Ipsum" to use.
*/
-class LoremIpsum(private val words: Int) : PreviewParameterProvider<String> {
+open class LoremIpsum(private val words: Int) : PreviewParameterProvider<String> {
// Unfortunately using default parameters seem to fail to be instantiated via reflection.
// We can workaround it by creating the default constructor manually.
constructor() : this(500)
diff --git a/compose/ui/ui-tooling/src/androidInstrumentedTest/kotlin/androidx/compose/ui/tooling/PreviewParameterTest.kt b/compose/ui/ui-tooling/src/androidInstrumentedTest/kotlin/androidx/compose/ui/tooling/PreviewParameterTest.kt
index 46d9006..ee8d3a72 100644
--- a/compose/ui/ui-tooling/src/androidInstrumentedTest/kotlin/androidx/compose/ui/tooling/PreviewParameterTest.kt
+++ b/compose/ui/ui-tooling/src/androidInstrumentedTest/kotlin/androidx/compose/ui/tooling/PreviewParameterTest.kt
@@ -118,4 +118,18 @@
)
}
}
+
+ private class LoremIpsum5WordsProvider : LoremIpsum(5)
+
+ @Test
+ fun checkLoremIpsumProviderSubclass() {
+ activityTestRule.runOnUiThread {
+ composeViewAdapter.init(
+ "androidx.compose.ui.tooling.ParameterProviderComposableKt",
+ "OneStringParameter",
+ parameterProvider = LoremIpsum5WordsProvider::class.java,
+ debugViewInfos = true
+ )
+ }
+ }
}
diff --git a/compose/ui/ui-unit/src/androidInstrumentedTest/kotlin/androidx/compose/ui/unit/fontscaling/FontScaleConverterFactoryTest.kt b/compose/ui/ui-unit/src/androidInstrumentedTest/kotlin/androidx/compose/ui/unit/fontscaling/FontScaleConverterFactoryTest.kt
index 4386510..15f7e03 100644
--- a/compose/ui/ui-unit/src/androidInstrumentedTest/kotlin/androidx/compose/ui/unit/fontscaling/FontScaleConverterFactoryTest.kt
+++ b/compose/ui/ui-unit/src/androidInstrumentedTest/kotlin/androidx/compose/ui/unit/fontscaling/FontScaleConverterFactoryTest.kt
@@ -244,7 +244,7 @@
companion object {
private const val CONVERSION_TOLERANCE = 0.05f
- private const val INTERPOLATED_TOLERANCE = 0.2f
+ private const val INTERPOLATED_TOLERANCE = 0.3f
}
}
diff --git a/compose/ui/ui-util/src/androidUnitTest/kotlin/androidx/compose/ui/util/InlineClassHelperTest.kt b/compose/ui/ui-util/src/androidUnitTest/kotlin/androidx/compose/ui/util/InlineClassHelperTest.kt
index a254057..03ee383 100644
--- a/compose/ui/ui-util/src/androidUnitTest/kotlin/androidx/compose/ui/util/InlineClassHelperTest.kt
+++ b/compose/ui/ui-util/src/androidUnitTest/kotlin/androidx/compose/ui/util/InlineClassHelperTest.kt
@@ -23,7 +23,6 @@
@RunWith(JUnit4::class)
class InlineClassHelperTest {
-
@Test
fun packAndUnpackFloats() {
val first = Float.MAX_VALUE
@@ -95,4 +94,15 @@
assertEquals(first, unpackInt1(packed))
assertEquals(second, unpackInt2(packed))
}
+
+ @Test
+ fun rawBits() {
+ val first = Float.NaN
+ val second = multZero(Float.POSITIVE_INFINITY)
+ val packed = packFloats(first, second)
+ assertEquals(first.toRawBits(), unpackFloat1(packed).toRawBits())
+ assertEquals(second.toRawBits(), unpackFloat2(packed).toRawBits())
+ }
+
+ fun multZero(value: Float) = value * 0f
}
diff --git a/compose/ui/ui-util/src/commonMain/kotlin/androidx/compose/ui/util/InlineClassHelper.kt b/compose/ui/ui-util/src/commonMain/kotlin/androidx/compose/ui/util/InlineClassHelper.kt
index c1548f2..710187b 100644
--- a/compose/ui/ui-util/src/commonMain/kotlin/androidx/compose/ui/util/InlineClassHelper.kt
+++ b/compose/ui/ui-util/src/commonMain/kotlin/androidx/compose/ui/util/InlineClassHelper.kt
@@ -22,8 +22,8 @@
* Packs two Float values into one Long value for use in inline classes.
*/
inline fun packFloats(val1: Float, val2: Float): Long {
- val v1 = val1.toBits().toLong()
- val v2 = val2.toBits().toLong()
+ val v1 = val1.toRawBits().toLong()
+ val v2 = val2.toRawBits().toLong()
return v1.shl(32) or (v2 and 0xFFFFFFFF)
}
diff --git a/compose/ui/ui-viewbinding/samples/src/androidTest/AndroidManifest.xml b/compose/ui/ui-viewbinding/samples/src/androidTest/AndroidManifest.xml
index cc8347b..c201303d 100644
--- a/compose/ui/ui-viewbinding/samples/src/androidTest/AndroidManifest.xml
+++ b/compose/ui/ui-viewbinding/samples/src/androidTest/AndroidManifest.xml
@@ -19,5 +19,6 @@
<activity android:name="androidx.compose.ui.samples.InflatedFragmentActivity"/>
<activity android:name="androidx.compose.ui.samples.ChildInflatedFragmentActivity"/>
<activity android:name="androidx.compose.ui.samples.EmptyFragmentActivity"/>
+ <activity android:name="androidx.compose.ui.samples.ComposeInflatedFragmentActivity"/>
</application>
</manifest>
diff --git a/compose/ui/ui-viewbinding/samples/src/androidTest/java/androidx/compose/ui/samples/FragmentRecreateTest.kt b/compose/ui/ui-viewbinding/samples/src/androidTest/java/androidx/compose/ui/samples/FragmentRecreateTest.kt
index 1439a9b..5de01b8 100644
--- a/compose/ui/ui-viewbinding/samples/src/androidTest/java/androidx/compose/ui/samples/FragmentRecreateTest.kt
+++ b/compose/ui/ui-viewbinding/samples/src/androidTest/java/androidx/compose/ui/samples/FragmentRecreateTest.kt
@@ -22,6 +22,7 @@
import androidx.activity.compose.setContent
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.viewbinding.samples.R
+import androidx.compose.ui.viewbinding.samples.databinding.SampleEditTextLayoutBinding
import androidx.compose.ui.viewbinding.samples.databinding.TestFragmentLayoutBinding
import androidx.compose.ui.viewinterop.AndroidViewBinding
import androidx.fragment.app.Fragment
@@ -51,6 +52,13 @@
supportFragmentManager.findFragmentById(R.id.fragment_container)!!
}
assertThat(fragment.requireView().parent).isNotNull()
+ val binding = SampleEditTextLayoutBinding.bind(fragment.requireView())
+ assertThat(binding.editText.text.toString()).isEqualTo("Default")
+
+ // Update the state to make sure it gets saved and restored properly
+ withActivity {
+ binding.editText.setText("Updated")
+ }
recreate()
@@ -58,6 +66,9 @@
supportFragmentManager.findFragmentById(R.id.fragment_container)!!
}
assertThat(recreatedFragment.requireView().parent).isNotNull()
+ val recreatedBinding = SampleEditTextLayoutBinding.bind(
+ recreatedFragment.requireView())
+ assertThat(recreatedBinding.editText.text.toString()).isEqualTo("Updated")
}
}
@@ -72,6 +83,13 @@
assertWithMessage("Fragment should be added as a child fragment")
.that(fragment).isNotNull()
assertThat(fragment!!.requireView().parent).isNotNull()
+ val binding = SampleEditTextLayoutBinding.bind(fragment.requireView())
+ assertThat(binding.editText.text.toString()).isEqualTo("Default")
+
+ // Update the state to make sure it gets saved and restored properly
+ withActivity {
+ binding.editText.setText("Updated")
+ }
recreate()
@@ -83,6 +101,9 @@
assertWithMessage("Fragment should be added as a child fragment")
.that(recreatedFragment).isNotNull()
assertThat(recreatedFragment!!.requireView().parent).isNotNull()
+ val recreatedBinding = SampleEditTextLayoutBinding.bind(
+ recreatedFragment.requireView())
+ assertThat(recreatedBinding.editText.text.toString()).isEqualTo("Updated")
}
}
}
diff --git a/compose/ui/ui-viewbinding/samples/src/androidTest/java/androidx/compose/ui/samples/FragmentRemoveTest.kt b/compose/ui/ui-viewbinding/samples/src/androidTest/java/androidx/compose/ui/samples/FragmentRemoveTest.kt
index 443d6c3..ec98ec8 100644
--- a/compose/ui/ui-viewbinding/samples/src/androidTest/java/androidx/compose/ui/samples/FragmentRemoveTest.kt
+++ b/compose/ui/ui-viewbinding/samples/src/androidTest/java/androidx/compose/ui/samples/FragmentRemoveTest.kt
@@ -16,16 +16,29 @@
package androidx.compose.ui.samples
+import android.os.Bundle
+import android.view.ViewGroup
+import androidx.activity.compose.setContent
+import androidx.compose.foundation.layout.requiredSize
+import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.test.junit4.createAndroidComposeRule
+import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewbinding.samples.R
+import androidx.compose.ui.viewbinding.samples.databinding.SampleEditTextLayoutBinding
import androidx.compose.ui.viewbinding.samples.databinding.TestFragmentLayoutBinding
import androidx.compose.ui.viewinterop.AndroidViewBinding
import androidx.fragment.app.FragmentActivity
+import androidx.lifecycle.Lifecycle
+import androidx.test.core.app.ActivityScenario
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
+import androidx.testutils.withActivity
+import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
import org.junit.Rule
import org.junit.Test
@@ -64,6 +77,147 @@
.that(fragment)
.isNull()
}
+
+ @Test
+ fun testRemovalRemovesState() {
+ var show by mutableStateOf(true)
+
+ rule.setContent {
+ if (show) {
+ AndroidViewBinding(TestFragmentLayoutBinding::inflate)
+ }
+ }
+
+ var fragment = rule.activity.supportFragmentManager
+ .findFragmentById(R.id.fragment_container)
+ assertWithMessage("Fragment should be present when AndroidViewBinding is in the hierarchy")
+ .that(fragment)
+ .isNotNull()
+
+ var binding = SampleEditTextLayoutBinding.bind(fragment!!.requireView())
+ assertThat(binding.editText.text.toString()).isEqualTo("Default")
+
+ // Update the state to allow verifying the state is destroyed when the
+ // AndroidViewBinding is removed from composition
+ rule.runOnUiThread {
+ binding.editText.setText("Updated")
+ }
+
+ show = false
+
+ rule.waitForIdle()
+
+ fragment = rule.activity.supportFragmentManager
+ .findFragmentById(R.id.fragment_container)
+ assertWithMessage("Fragment should be removed when the AndroidViewBinding is removed")
+ .that(fragment)
+ .isNull()
+
+ show = true
+
+ rule.waitForIdle()
+
+ fragment = rule.activity.supportFragmentManager
+ .findFragmentById(R.id.fragment_container)
+ assertWithMessage("Fragment should be present when AndroidViewBinding is in the hierarchy")
+ .that(fragment)
+ .isNotNull()
+ binding = SampleEditTextLayoutBinding.bind(fragment!!.requireView())
+
+ // State should be reset back to the default
+ assertThat(binding.editText.text.toString()).isEqualTo("Default")
+ }
+
+ @Test
+ fun testRemovalRemovesStateOnBackwardWrite() {
+ var showStateA by mutableStateOf(true)
+
+ rule.setContent {
+ if (showStateA) {
+ AndroidViewBinding(TestFragmentLayoutBinding::inflate)
+ } else {
+ SideEffect {
+ showStateA = true
+ }
+ }
+ }
+
+ var fragment = rule.activity.supportFragmentManager
+ .findFragmentById(R.id.fragment_container)
+ assertWithMessage("Fragment should be present when AndroidViewBinding is in the hierarchy")
+ .that(fragment)
+ .isNotNull()
+
+ var binding = SampleEditTextLayoutBinding.bind(fragment!!.requireView())
+ assertThat(binding.editText.text.toString()).isEqualTo("Default")
+
+ // Update the state to allow verifying the state is destroyed when the
+ // AndroidViewBinding is removed from composition
+ rule.runOnUiThread {
+ binding.editText.setText("Updated")
+ }
+
+ showStateA = false
+
+ rule.waitForIdle()
+
+ fragment = rule.activity.supportFragmentManager
+ .findFragmentById(R.id.fragment_container)
+ assertWithMessage("Fragment should be present when AndroidViewBinding is in the hierarchy")
+ .that(fragment)
+ .isNotNull()
+ binding = SampleEditTextLayoutBinding.bind(fragment!!.requireView())
+
+ // State should be reset back to the default
+ assertThat(binding.editText.text.toString()).isEqualTo("Default")
+ }
+
+ @Test
+ fun testRemovalRemovesStateOnCompositionDisposalAndRecreation() {
+ with(ActivityScenario.launch(ComposeInflatedFragmentActivity::class.java)) {
+ withActivity {
+ val fragment = supportFragmentManager.findFragmentById(R.id.fragment_container)!!
+ assertThat(fragment.lifecycle.currentState).isEqualTo(Lifecycle.State.RESUMED)
+ val binding = SampleEditTextLayoutBinding.bind(fragment.requireView())
+ assertThat(binding.editText.text.toString()).isEqualTo("Default")
+
+ // Update the state to make sure it gets saved and restored properly
+ binding.editText.setText("Updated")
+
+ // detach - attach to dispose composition and compose it again
+ val root = composeView!!.parent as ViewGroup
+ root.removeView(composeView)
+ root.addView(composeView)
+ }
+
+ withActivity {
+ val fragment = supportFragmentManager.findFragmentById(R.id.fragment_container)
+ assertThat(fragment).isNotNull()
+ assertThat(fragment!!.lifecycle.currentState).isEqualTo(Lifecycle.State.RESUMED)
+ val recreatedBinding = SampleEditTextLayoutBinding.bind(
+ fragment.requireView())
+ assertThat(recreatedBinding.editText.text.toString()).isEqualTo("Default")
+ }
+ }
+ }
}
class EmptyFragmentActivity : FragmentActivity()
+
+class ComposeInflatedFragmentActivity : FragmentActivity() {
+ var composeView: ComposeView? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContent {
+ AndroidViewBinding(
+ TestFragmentLayoutBinding::inflate,
+ Modifier.requiredSize(50.dp),
+ )
+ }
+
+ composeView = window.decorView
+ .findViewById<ViewGroup>(android.R.id.content)
+ .getChildAt(0) as? ComposeView
+ }
+}
diff --git a/compose/ui/ui-viewbinding/samples/src/androidTest/java/androidx/compose/ui/samples/InflatedFragment.kt b/compose/ui/ui-viewbinding/samples/src/androidTest/java/androidx/compose/ui/samples/InflatedFragment.kt
index e94e7fc..2b66256 100644
--- a/compose/ui/ui-viewbinding/samples/src/androidTest/java/androidx/compose/ui/samples/InflatedFragment.kt
+++ b/compose/ui/ui-viewbinding/samples/src/androidTest/java/androidx/compose/ui/samples/InflatedFragment.kt
@@ -19,4 +19,4 @@
import androidx.compose.ui.viewbinding.samples.R
import androidx.fragment.app.Fragment
-class InflatedFragment : Fragment(R.layout.sample_layout)
+class InflatedFragment : Fragment(R.layout.sample_edit_text_layout)
diff --git a/compose/ui/ui-viewbinding/samples/src/main/res/layout/sample_edit_text_layout.xml b/compose/ui/ui-viewbinding/samples/src/main/res/layout/sample_edit_text_layout.xml
new file mode 100644
index 0000000..97b87cd
--- /dev/null
+++ b/compose/ui/ui-viewbinding/samples/src/main/res/layout/sample_edit_text_layout.xml
@@ -0,0 +1,26 @@
+<?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.
+ -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/fragment_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <EditText
+ android:id="@+id/edit_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Default" />
+</FrameLayout>
\ No newline at end of file
diff --git a/compose/ui/ui-viewbinding/src/main/java/androidx/compose/ui/viewinterop/AndroidViewBinding.kt b/compose/ui/ui-viewbinding/src/main/java/androidx/compose/ui/viewinterop/AndroidViewBinding.kt
index 1424583..5f2a288 100644
--- a/compose/ui/ui-viewbinding/src/main/java/androidx/compose/ui/viewinterop/AndroidViewBinding.kt
+++ b/compose/ui/ui-viewbinding/src/main/java/androidx/compose/ui/viewinterop/AndroidViewBinding.kt
@@ -31,7 +31,7 @@
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentContainerView
-import androidx.fragment.app.commit
+import androidx.fragment.app.commitNow
import androidx.fragment.app.findFragment
import androidx.viewbinding.ViewBinding
@@ -195,7 +195,7 @@
if (existingFragment != null && !fragmentManager.isStateSaved) {
// If the state isn't saved, that means that some state change
// has removed this Composable from the hierarchy
- fragmentManager.commit {
+ fragmentManager.commitNow {
remove(existingFragment)
}
}
diff --git a/development/build_log_simplifier/message-flakes.ignore b/development/build_log_simplifier/message-flakes.ignore
index 6a1c68d..eb5d676 100644
--- a/development/build_log_simplifier/message-flakes.ignore
+++ b/development/build_log_simplifier/message-flakes.ignore
@@ -49,9 +49,6 @@
\[ant\:jacocoReport\] Note\: Recompile with \-Xlint\:unchecked for details\.
# b/179833331 , https://youtrack.jetbrains.com/issue/KT-35156
# b/181258249 , https://youtrack.jetbrains.com/issue/KT-43881
-# > Task :jetifier-core:compileKotlin
-Could not perform incremental compilation\: Could not connect to Kotlin compile daemon
-Could not connect to kotlin daemon\. Using fallback strategy\.
\.\.\. [0-9]+ more
at java\.rmi\/sun\.rmi\.transport\.StreamRemoteCall\.exceptionReceivedFromServer\(StreamRemoteCall\.java\:[0-9]+\)
at java\.rmi\/sun\.rmi\.transport\.StreamRemoteCall\.executeCall\(StreamRemoteCall\.java\:[0-9]+\)
diff --git a/development/build_log_simplifier/messages.ignore b/development/build_log_simplifier/messages.ignore
index 60beba3..4c2f30d 100644
--- a/development/build_log_simplifier/messages.ignore
+++ b/development/build_log_simplifier/messages.ignore
@@ -11,6 +11,7 @@
Daemon will be stopped at the end of the build
# > Configure project :appsearch:appsearch\-local\-backend
Configuration on demand is an incubating feature\.
+Calculating task graph as configuration cache cannot be reused because the set of Gradle properties has changed\.
# > Task :listTaskOutputs
Deprecated Gradle features were used in this build, making it incompatible with Gradle [0-9]+\.[0-9]+\.
BUILD SUCCESSFUL in .*
diff --git a/gradlew b/gradlew
index 66189ab..e8d45ff 100755
--- a/gradlew
+++ b/gradlew
@@ -27,17 +27,9 @@
mkdir -p "$DIST_DIR"
DIST_DIR="$(cd $DIST_DIR && pwd -P)"
- #Set the initial heap size to match the max heap size,
- #by replacing a string like "-Xmx1g" with one like "-Xms1g -Xmx1g"
- MAX_MEM=32g
- ORG_GRADLE_JVMARGS="$(echo $ORG_GRADLE_JVMARGS | sed "s/-Xmx\([^ ]*\)/-Xms$MAX_MEM -Xmx$MAX_MEM/")"
-
# tell Gradle where to put a heap dump on failure
ORG_GRADLE_JVMARGS="$(echo $ORG_GRADLE_JVMARGS | sed "s|$| -XX:HeapDumpPath=$DIST_DIR|")"
- # Increase the compiler cache size: b/260643754 . Remove when updating to JDK 20 ( https://bugs.openjdk.org/browse/JDK-8295724 )
- ORG_GRADLE_JVMARGS="$(echo $ORG_GRADLE_JVMARGS | sed "s|$| -XX:ReservedCodeCacheSize=576M|")"
-
# We don't set a default DIST_DIR in an else clause here because Studio doesn't use gradlew
# and doesn't set DIST_DIR and we want gradlew and Studio to match
fi
@@ -272,7 +264,8 @@
--stacktrace\
-Pandroidx.summarizeStderr\
-Pandroidx.enableAffectedModuleDetection\
- --no-watch-fs"
+ --no-watch-fs\
+ -Pandroidx.highMemory"
fi
fi
if [ "$compact" == "--strict" ]; then
@@ -316,6 +309,16 @@
fi
done
+if [[ " ${@} " =~ " -Pandroidx.highMemory " ]]; then
+ #Set the initial heap size to match the max heap size,
+ #by replacing a string like "-Xmx1g" with one like "-Xms1g -Xmx1g"
+ MAX_MEM=32g
+ ORG_GRADLE_JVMARGS="$(echo $ORG_GRADLE_JVMARGS | sed "s/-Xmx\([^ ]*\)/-Xms$MAX_MEM -Xmx$MAX_MEM/")"
+
+ # Increase the compiler cache size: b/260643754 . Remove when updating to JDK 20 ( https://bugs.openjdk.org/browse/JDK-8295724 )
+ ORG_GRADLE_JVMARGS="$(echo $ORG_GRADLE_JVMARGS | sed "s|$| -XX:ReservedCodeCacheSize=576M|")"
+fi
+
# check whether the user has requested profiling via yourkit
yourkitArgPrefix="androidx.profile.yourkitAgentPath"
yourkitAgentPath=""
diff --git a/javascriptengine/javascriptengine/api/current.txt b/javascriptengine/javascriptengine/api/current.txt
index 96064bd..f8a7b67 100644
--- a/javascriptengine/javascriptengine/api/current.txt
+++ b/javascriptengine/javascriptengine/api/current.txt
@@ -22,6 +22,7 @@
public class IsolateTerminatedException extends androidx.javascriptengine.JavaScriptException {
ctor public IsolateTerminatedException();
+ ctor public IsolateTerminatedException(String);
}
public interface JavaScriptConsoleCallback {
@@ -49,18 +50,21 @@
ctor public JavaScriptException(String);
}
- public final class JavaScriptIsolate implements java.lang.AutoCloseable {
+ @javax.annotation.concurrent.ThreadSafe public final class JavaScriptIsolate implements java.lang.AutoCloseable {
+ method public void addOnTerminatedCallback(androidx.core.util.Consumer<androidx.javascriptengine.TerminationInfo!>);
+ method public void addOnTerminatedCallback(java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.javascriptengine.TerminationInfo!>);
method @RequiresFeature(name=androidx.javascriptengine.JavaScriptSandbox.JS_FEATURE_CONSOLE_MESSAGING, enforcement="androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported") public void clearConsoleCallback();
method public void close();
method @RequiresFeature(name=androidx.javascriptengine.JavaScriptSandbox.JS_FEATURE_EVALUATE_FROM_FD, enforcement="androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported") public com.google.common.util.concurrent.ListenableFuture<java.lang.String!> evaluateJavaScriptAsync(android.content.res.AssetFileDescriptor);
method @RequiresFeature(name=androidx.javascriptengine.JavaScriptSandbox.JS_FEATURE_EVALUATE_FROM_FD, enforcement="androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported") public com.google.common.util.concurrent.ListenableFuture<java.lang.String!> evaluateJavaScriptAsync(android.os.ParcelFileDescriptor);
method public com.google.common.util.concurrent.ListenableFuture<java.lang.String!> evaluateJavaScriptAsync(String);
method @RequiresFeature(name=androidx.javascriptengine.JavaScriptSandbox.JS_FEATURE_PROVIDE_CONSUME_ARRAY_BUFFER, enforcement="androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported") public void provideNamedData(String, byte[]);
+ method public void removeOnTerminatedCallback(androidx.core.util.Consumer<androidx.javascriptengine.TerminationInfo!>);
method @RequiresFeature(name=androidx.javascriptengine.JavaScriptSandbox.JS_FEATURE_CONSOLE_MESSAGING, enforcement="androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported") public void setConsoleCallback(androidx.javascriptengine.JavaScriptConsoleCallback);
method @RequiresFeature(name=androidx.javascriptengine.JavaScriptSandbox.JS_FEATURE_CONSOLE_MESSAGING, enforcement="androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported") public void setConsoleCallback(java.util.concurrent.Executor, androidx.javascriptengine.JavaScriptConsoleCallback);
}
- public final class JavaScriptSandbox implements java.lang.AutoCloseable {
+ @javax.annotation.concurrent.ThreadSafe public final class JavaScriptSandbox implements java.lang.AutoCloseable {
method public void close();
method public static com.google.common.util.concurrent.ListenableFuture<androidx.javascriptengine.JavaScriptSandbox!> createConnectedInstanceAsync(android.content.Context);
method public androidx.javascriptengine.JavaScriptIsolate createIsolate();
@@ -84,11 +88,21 @@
public final class SandboxDeadException extends androidx.javascriptengine.IsolateTerminatedException {
ctor public SandboxDeadException();
+ ctor public SandboxDeadException(String);
}
public final class SandboxUnsupportedException extends java.lang.RuntimeException {
ctor public SandboxUnsupportedException(String);
}
+ public final class TerminationInfo {
+ method public String getMessage();
+ method public int getStatus();
+ method public String getStatusString();
+ field public static final int STATUS_MEMORY_LIMIT_EXCEEDED = 3; // 0x3
+ field public static final int STATUS_SANDBOX_DEAD = 2; // 0x2
+ field public static final int STATUS_UNKNOWN_ERROR = 1; // 0x1
+ }
+
}
diff --git a/javascriptengine/javascriptengine/api/restricted_current.txt b/javascriptengine/javascriptengine/api/restricted_current.txt
index 96064bd..f8a7b67 100644
--- a/javascriptengine/javascriptengine/api/restricted_current.txt
+++ b/javascriptengine/javascriptengine/api/restricted_current.txt
@@ -22,6 +22,7 @@
public class IsolateTerminatedException extends androidx.javascriptengine.JavaScriptException {
ctor public IsolateTerminatedException();
+ ctor public IsolateTerminatedException(String);
}
public interface JavaScriptConsoleCallback {
@@ -49,18 +50,21 @@
ctor public JavaScriptException(String);
}
- public final class JavaScriptIsolate implements java.lang.AutoCloseable {
+ @javax.annotation.concurrent.ThreadSafe public final class JavaScriptIsolate implements java.lang.AutoCloseable {
+ method public void addOnTerminatedCallback(androidx.core.util.Consumer<androidx.javascriptengine.TerminationInfo!>);
+ method public void addOnTerminatedCallback(java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.javascriptengine.TerminationInfo!>);
method @RequiresFeature(name=androidx.javascriptengine.JavaScriptSandbox.JS_FEATURE_CONSOLE_MESSAGING, enforcement="androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported") public void clearConsoleCallback();
method public void close();
method @RequiresFeature(name=androidx.javascriptengine.JavaScriptSandbox.JS_FEATURE_EVALUATE_FROM_FD, enforcement="androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported") public com.google.common.util.concurrent.ListenableFuture<java.lang.String!> evaluateJavaScriptAsync(android.content.res.AssetFileDescriptor);
method @RequiresFeature(name=androidx.javascriptengine.JavaScriptSandbox.JS_FEATURE_EVALUATE_FROM_FD, enforcement="androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported") public com.google.common.util.concurrent.ListenableFuture<java.lang.String!> evaluateJavaScriptAsync(android.os.ParcelFileDescriptor);
method public com.google.common.util.concurrent.ListenableFuture<java.lang.String!> evaluateJavaScriptAsync(String);
method @RequiresFeature(name=androidx.javascriptengine.JavaScriptSandbox.JS_FEATURE_PROVIDE_CONSUME_ARRAY_BUFFER, enforcement="androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported") public void provideNamedData(String, byte[]);
+ method public void removeOnTerminatedCallback(androidx.core.util.Consumer<androidx.javascriptengine.TerminationInfo!>);
method @RequiresFeature(name=androidx.javascriptengine.JavaScriptSandbox.JS_FEATURE_CONSOLE_MESSAGING, enforcement="androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported") public void setConsoleCallback(androidx.javascriptengine.JavaScriptConsoleCallback);
method @RequiresFeature(name=androidx.javascriptengine.JavaScriptSandbox.JS_FEATURE_CONSOLE_MESSAGING, enforcement="androidx.javascriptengine.JavaScriptSandbox#isFeatureSupported") public void setConsoleCallback(java.util.concurrent.Executor, androidx.javascriptengine.JavaScriptConsoleCallback);
}
- public final class JavaScriptSandbox implements java.lang.AutoCloseable {
+ @javax.annotation.concurrent.ThreadSafe public final class JavaScriptSandbox implements java.lang.AutoCloseable {
method public void close();
method public static com.google.common.util.concurrent.ListenableFuture<androidx.javascriptengine.JavaScriptSandbox!> createConnectedInstanceAsync(android.content.Context);
method public androidx.javascriptengine.JavaScriptIsolate createIsolate();
@@ -84,11 +88,21 @@
public final class SandboxDeadException extends androidx.javascriptengine.IsolateTerminatedException {
ctor public SandboxDeadException();
+ ctor public SandboxDeadException(String);
}
public final class SandboxUnsupportedException extends java.lang.RuntimeException {
ctor public SandboxUnsupportedException(String);
}
+ public final class TerminationInfo {
+ method public String getMessage();
+ method public int getStatus();
+ method public String getStatusString();
+ field public static final int STATUS_MEMORY_LIMIT_EXCEEDED = 3; // 0x3
+ field public static final int STATUS_SANDBOX_DEAD = 2; // 0x2
+ field public static final int STATUS_UNKNOWN_ERROR = 1; // 0x1
+ }
+
}
diff --git a/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/IsolateTerminatedException.java b/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/IsolateTerminatedException.java
index 8548c19..84ea3ab 100644
--- a/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/IsolateTerminatedException.java
+++ b/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/IsolateTerminatedException.java
@@ -17,7 +17,6 @@
package androidx.javascriptengine;
import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
import androidx.core.util.Consumer;
import java.util.concurrent.Executor;
@@ -50,7 +49,7 @@
public IsolateTerminatedException() {
super();
}
- @RestrictTo(RestrictTo.Scope.LIBRARY)
+
public IsolateTerminatedException(@NonNull String message) {
super(message);
}
diff --git a/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/JavaScriptIsolate.java b/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/JavaScriptIsolate.java
index dde4b27..eea5139 100644
--- a/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/JavaScriptIsolate.java
+++ b/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/JavaScriptIsolate.java
@@ -16,6 +16,7 @@
package androidx.javascriptengine;
+import android.annotation.SuppressLint;
import android.content.res.AssetFileDescriptor;
import android.os.Binder;
import android.os.ParcelFileDescriptor;
@@ -25,7 +26,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresFeature;
-import androidx.annotation.RestrictTo;
import androidx.core.util.Consumer;
import com.google.common.util.concurrent.ListenableFuture;
@@ -37,6 +37,7 @@
import java.util.concurrent.Executor;
import javax.annotation.concurrent.GuardedBy;
+import javax.annotation.concurrent.ThreadSafe;
/**
* Environment within a {@link JavaScriptSandbox} where JavaScript is executed.
@@ -54,6 +55,7 @@
* <p>
* This class is thread-safe.
*/
+@ThreadSafe
public final class JavaScriptIsolate implements AutoCloseable {
private static final String TAG = "JavaScriptIsolate";
private final Object mLock = new Object();
@@ -475,7 +477,7 @@
* @param callback Consumer to be called with TerminationInfo when a crash occurs.
* @throws IllegalStateException if the callback is already registered (using any executor).
*/
- @RestrictTo(RestrictTo.Scope.LIBRARY)
+ @SuppressLint("RegistrationName")
public void addOnTerminatedCallback(@NonNull Executor executor,
@NonNull Consumer<TerminationInfo> callback) {
Objects.requireNonNull(executor);
@@ -494,7 +496,7 @@
* @param callback Consumer to be called with TerminationInfo when a crash occurs.
* @throws IllegalStateException if the callback is already registered (using any executor).
*/
- @RestrictTo(RestrictTo.Scope.LIBRARY)
+ @SuppressLint("RegistrationName")
public void addOnTerminatedCallback(@NonNull Consumer<TerminationInfo> callback) {
addOnTerminatedCallback(mJsSandbox.getMainExecutor(), callback);
}
@@ -504,7 +506,7 @@
*
* @param callback The callback to unregister, if currently registered.
*/
- @RestrictTo(RestrictTo.Scope.LIBRARY)
+ @SuppressLint("RegistrationName")
public void removeOnTerminatedCallback(@NonNull Consumer<TerminationInfo> callback) {
Objects.requireNonNull(callback);
synchronized (mLock) {
diff --git a/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/JavaScriptSandbox.java b/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/JavaScriptSandbox.java
index b3f65eb..b35dbd2 100644
--- a/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/JavaScriptSandbox.java
+++ b/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/JavaScriptSandbox.java
@@ -61,6 +61,7 @@
import java.util.concurrent.atomic.AtomicReference;
import javax.annotation.concurrent.GuardedBy;
+import javax.annotation.concurrent.ThreadSafe;
/**
* Sandbox that provides APIs for JavaScript evaluation in a restricted environment.
@@ -83,6 +84,7 @@
* can create their own {@link JavaScriptIsolate} objects from it but the
* {@link JavaScriptIsolate} object cannot be shared.
*/
+@ThreadSafe
public final class JavaScriptSandbox implements AutoCloseable {
private static final String TAG = "JavaScriptSandbox";
// TODO(crbug.com/1297672): Add capability to this class to support spawning
diff --git a/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/SandboxDeadException.java b/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/SandboxDeadException.java
index f3439b7..5cc86a3 100644
--- a/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/SandboxDeadException.java
+++ b/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/SandboxDeadException.java
@@ -17,7 +17,6 @@
package androidx.javascriptengine;
import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
/**
* Exception thrown when evaluation is terminated due the {@link JavaScriptSandbox} being dead.
@@ -28,7 +27,7 @@
public SandboxDeadException() {
super();
}
- @RestrictTo(RestrictTo.Scope.LIBRARY)
+
public SandboxDeadException(@NonNull String message) {
super(message);
}
diff --git a/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/TerminationInfo.java b/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/TerminationInfo.java
index fc828de..db18013 100644
--- a/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/TerminationInfo.java
+++ b/javascriptengine/javascriptengine/src/main/java/androidx/javascriptengine/TerminationInfo.java
@@ -28,8 +28,7 @@
/**
* Information about how and why an isolate has terminated.
*/
-@RestrictTo(RestrictTo.Scope.LIBRARY)
-public class TerminationInfo {
+public final class TerminationInfo {
/**
* Termination status code for an isolate.
*/
diff --git a/libraryversions.toml b/libraryversions.toml
index 89da799..09d7dc2 100644
--- a/libraryversions.toml
+++ b/libraryversions.toml
@@ -102,7 +102,7 @@
PRINT = "1.1.0-beta01"
PRIVACYSANDBOX_ADS = "1.1.0-beta02"
PRIVACYSANDBOX_PLUGINS = "1.0.0-alpha03"
-PRIVACYSANDBOX_SDKRUNTIME = "1.0.0-alpha09"
+PRIVACYSANDBOX_SDKRUNTIME = "1.0.0-alpha10"
PRIVACYSANDBOX_TOOLS = "1.0.0-alpha06"
PRIVACYSANDBOX_UI = "1.0.0-alpha06"
PROFILEINSTALLER = "1.4.0-alpha01"
@@ -163,7 +163,7 @@
WINDOW_EXTENSIONS_CORE = "1.1.0-alpha01"
WINDOW_SIDECAR = "1.0.0-rc01"
# Do not remove comment
-WORK = "2.9.0-rc01"
+WORK = "2.10.0-alpha01"
[groups]
ACTIVITY = { group = "androidx.activity", atomicGroupVersion = "versions.ACTIVITY" }
diff --git a/media/version-compat-tests/build.gradle b/media/version-compat-tests/build.gradle
new file mode 100644
index 0000000..92fc391
--- /dev/null
+++ b/media/version-compat-tests/build.gradle
@@ -0,0 +1,5 @@
+// This project creates some version compat test artifacts
+// See TestSuiteConfiguration.kt
+plugins {
+ id("AndroidXPlugin")
+}
diff --git a/media/version-compat-tests/current/service/src/androidTest/java/android/support/mediacompat/service/MediaSessionCompatCallbackTest.java b/media/version-compat-tests/current/service/src/androidTest/java/android/support/mediacompat/service/MediaSessionCompatCallbackTest.java
index 9768f00..1065a82 100644
--- a/media/version-compat-tests/current/service/src/androidTest/java/android/support/mediacompat/service/MediaSessionCompatCallbackTest.java
+++ b/media/version-compat-tests/current/service/src/androidTest/java/android/support/mediacompat/service/MediaSessionCompatCallbackTest.java
@@ -112,6 +112,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -329,6 +330,7 @@
@Test
@SmallTest
+ @Ignore("flaky: b/297535302")
public void testGetPlaybackStateWithPositionUpdate() throws InterruptedException {
final long stateSetTime = SystemClock.elapsedRealtime();
PlaybackStateCompat stateIn = new PlaybackStateCompat.Builder()
diff --git a/media/version-compat-tests/previous/service/src/androidTest/java/android/support/mediacompat/service/MediaSessionCompatCallbackTest.java b/media/version-compat-tests/previous/service/src/androidTest/java/android/support/mediacompat/service/MediaSessionCompatCallbackTest.java
index 9b180e8..615ca1b 100644
--- a/media/version-compat-tests/previous/service/src/androidTest/java/android/support/mediacompat/service/MediaSessionCompatCallbackTest.java
+++ b/media/version-compat-tests/previous/service/src/androidTest/java/android/support/mediacompat/service/MediaSessionCompatCallbackTest.java
@@ -112,6 +112,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -329,6 +330,7 @@
@Test
@SmallTest
+ @Ignore("flaky: b/297535302")
public void testGetPlaybackStateWithPositionUpdate() throws InterruptedException {
final long stateSetTime = SystemClock.elapsedRealtime();
PlaybackStateCompat stateIn = new PlaybackStateCompat.Builder()
diff --git a/media2/media2-session/version-compat-tests/build.gradle b/media2/media2-session/version-compat-tests/build.gradle
new file mode 100644
index 0000000..92fc391
--- /dev/null
+++ b/media2/media2-session/version-compat-tests/build.gradle
@@ -0,0 +1,5 @@
+// This project creates some version compat test artifacts
+// See TestSuiteConfiguration.kt
+plugins {
+ id("AndroidXPlugin")
+}
diff --git a/test/screenshot/screenshot-proto/build.gradle b/test/screenshot/screenshot-proto/build.gradle
index 6940586..879e232 100644
--- a/test/screenshot/screenshot-proto/build.gradle
+++ b/test/screenshot/screenshot-proto/build.gradle
@@ -19,6 +19,7 @@
plugins {
id("java-library")
id("com.google.protobuf")
+ id("AndroidXPlugin")
}
dependencies {
@@ -44,3 +45,12 @@
}
}
}
+
+afterEvaluate {
+ lint {
+ lintOptions {
+ // protobuf generates unannotated methods
+ disable("UnknownNullness")
+ }
+ }
+}
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 c05d7b5..42cefbb 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
@@ -181,7 +181,7 @@
infiniteRepeatable(
animation = keyframes {
durationMillis = HeadAndTailAnimationDuration + HeadAndTailDelayDuration
- 0f at 0 with CircularEasing
+ 0f at 0 using CircularEasing
JumpRotationAngle at HeadAndTailAnimationDuration
}
)
@@ -193,7 +193,7 @@
infiniteRepeatable(
animation = keyframes {
durationMillis = HeadAndTailAnimationDuration + HeadAndTailDelayDuration
- 0f at HeadAndTailDelayDuration with CircularEasing
+ 0f at HeadAndTailDelayDuration using CircularEasing
JumpRotationAngle at durationMillis
}
)
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
index 1a1b0e7..7396864 100644
--- 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
@@ -59,7 +59,15 @@
import kotlin.math.abs
/**
- * [SwipeToReveal] Material composable for Chips. This provides the default style for consistency.
+ * [SwipeToReveal] Material composable for [Chip]s. This adds the option to configure up to two
+ * additional actions on the [Chip]: a mandatory [primaryAction] and an optional
+ * [secondaryAction]. These actions are initially hidden and revealed only when the [content] is
+ * swiped. These additional actions can be triggered by clicking on them after they are revealed.
+ * [primaryAction] can also be triggered by performing a full swipe of the [content].
+ *
+ * For actions like "Delete", consider adding [undoPrimaryAction] (displayed when the
+ * [primaryAction] is activated) and/or [undoSecondaryAction] (displayed when the [secondaryAction]
+ * is activated). Adding undo composables allow users to undo the action that they just performed.
*
* Example of [SwipeToRevealChip] with primary and secondary actions
* @sample androidx.wear.compose.material.samples.SwipeToRevealChipSample
@@ -112,7 +120,15 @@
}
/**
- * [SwipeToReveal] Material composable for Cards. This provides the default style for consistency.
+ * [SwipeToReveal] Material composable for [Card]s. This adds the option to configure up to two
+ * additional actions on the [Card]: a mandatory [primaryAction] and an optional
+ * [secondaryAction]. These actions are initially hidden and revealed only when the [content] is
+ * swiped. These additional actions can be triggered by clicking on them after they are revealed.
+ * [primaryAction] can also be triggered by performing a full swipe of the [content].
+ *
+ * For actions like "Delete", consider adding [undoPrimaryAction] (displayed when the
+ * [primaryAction] is activated) and/or [undoSecondaryAction] (displayed when the [secondaryAction]
+ * is activated). Adding undo composables allow users to undo the action that they just performed.
*
* Example of [SwipeToRevealCard] with primary and secondary actions
* @sample androidx.wear.compose.material.samples.SwipeToRevealCardSample
@@ -176,17 +192,19 @@
public val CardActionShape = RoundedCornerShape(40.dp)
/**
- * Colors to be used with different actions in [SwipeToReveal].
+ * The recommended colors used to display the contents of the
+ * primary, secondary and undo actions in [SwipeToReveal].
*
- * @param primaryActionBackgroundColor The background color (color of the shape) of the primary
- * action
- * @param primaryActionContentColor The content color (text and icon) of the primary action
+ * @param primaryActionBackgroundColor The background color (color of the shape) of the
+ * [primaryAction]
+ * @param primaryActionContentColor The content color (text and icon) of the [primaryAction]
* @param secondaryActionBackgroundColor The background color (color of the shape) of the
- * secondary action
- * @param secondaryActionContentColor The content color (text and icon) of the secondary
- * action
- * @param undoActionBackgroundColor The background color (color of the shape) of the undo action
- * @param undoActionContentColor The content color (text) of the undo action
+ * [secondaryAction]
+ * @param secondaryActionContentColor The content color (text and icon) of the
+ * [secondaryAction]
+ * @param undoActionBackgroundColor The background color (color of the shape) of the
+ * [undoAction]
+ * @param undoActionContentColor The content color (text) of the [undoAction]
*/
@Composable
public fun actionColors(
@@ -316,6 +334,7 @@
/**
* A class representing the colors applied in [SwipeToReveal] actions.
+ * See [SwipeToRevealDefaults.actionColors].
*
* @param primaryActionBackgroundColor Color of the shape (background) of primary action
* @param primaryActionContentColor Color of icon or text used in the primary action
diff --git a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/dialog/Dialog.android.kt b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/dialog/Dialog.android.kt
index 966ee28..253f341 100644
--- a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/dialog/Dialog.android.kt
+++ b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/dialog/Dialog.android.kt
@@ -262,7 +262,7 @@
// Outro
durationMillis = QUICK + RAPID
1f at 0
- 0.9f at RAPID with STANDARD_IN
+ 0.9f at RAPID using STANDARD_IN
0.0f at RAPID + QUICK
}
}
@@ -285,7 +285,7 @@
// Intro
durationMillis = QUICK + RAPID
0.0f at 0
- 0.1f at RAPID with STANDARD_IN
+ 0.1f at RAPID using STANDARD_IN
1f at RAPID + QUICK
}
diff --git a/webkit/webkit/api/current.txt b/webkit/webkit/api/current.txt
index ebb7e57..aa2bfe1 100644
--- a/webkit/webkit/api/current.txt
+++ b/webkit/webkit/api/current.txt
@@ -27,6 +27,23 @@
method @RequiresFeature(name=androidx.webkit.WebViewFeature.STARTUP_FEATURE_SET_DIRECTORY_BASE_PATHS, enforcement="androidx.webkit.WebViewFeature#isConfigFeatureSupported(String, Context)") public androidx.webkit.ProcessGlobalConfig setDirectoryBasePaths(android.content.Context, java.io.File, java.io.File);
}
+ public interface Profile {
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public android.webkit.CookieManager getCookieManager();
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public android.webkit.GeolocationPermissions getGeolocationPermissions();
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public String getName();
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public android.webkit.ServiceWorkerController getServiceWorkerController();
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public android.webkit.WebStorage getWebStorage();
+ field public static final String DEFAULT_PROFILE_NAME = "Default";
+ }
+
+ public interface ProfileStore {
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public boolean deleteProfile(String);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public java.util.List<java.lang.String!> getAllProfileNames();
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.ProfileStore getInstance();
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public androidx.webkit.Profile getOrCreateProfile(String);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public androidx.webkit.Profile? getProfile(String);
+ }
+
public final class ProxyConfig {
method public java.util.List<java.lang.String!> getBypassRules();
method public java.util.List<androidx.webkit.ProxyConfig.ProxyRule!> getProxyRules();
@@ -197,6 +214,7 @@
}
public class WebSettingsCompat {
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.ATTRIBUTION_REGISTRATION_BEHAVIOR, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static int getAttributionRegistrationBehavior(android.webkit.WebSettings);
method @RequiresFeature(name=androidx.webkit.WebViewFeature.DISABLED_ACTION_MODE_MENU_ITEMS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static int getDisabledActionModeMenuItems(android.webkit.WebSettings);
method @RequiresFeature(name=androidx.webkit.WebViewFeature.ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean getEnterpriseAuthenticationAppLinkPolicyEnabled(android.webkit.WebSettings);
method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.FORCE_DARK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static int getForceDark(android.webkit.WebSettings);
@@ -207,6 +225,7 @@
method @RequiresFeature(name=androidx.webkit.WebViewFeature.USER_AGENT_METADATA, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.UserAgentMetadata getUserAgentMetadata(android.webkit.WebSettings);
method @RequiresFeature(name=androidx.webkit.WebViewFeature.ALGORITHMIC_DARKENING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean isAlgorithmicDarkeningAllowed(android.webkit.WebSettings);
method @RequiresFeature(name=androidx.webkit.WebViewFeature.ALGORITHMIC_DARKENING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setAlgorithmicDarkeningAllowed(android.webkit.WebSettings, boolean);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.ATTRIBUTION_REGISTRATION_BEHAVIOR, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setAttributionRegistrationBehavior(android.webkit.WebSettings, int);
method @RequiresFeature(name=androidx.webkit.WebViewFeature.DISABLED_ACTION_MODE_MENU_ITEMS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setDisabledActionModeMenuItems(android.webkit.WebSettings, int);
method @RequiresFeature(name=androidx.webkit.WebViewFeature.ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setEnterpriseAuthenticationAppLinkPolicyEnabled(android.webkit.WebSettings, boolean);
method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.FORCE_DARK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setForceDark(android.webkit.WebSettings, int);
@@ -215,6 +234,10 @@
method @RequiresFeature(name="REQUESTED_WITH_HEADER_ALLOW_LIST", enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setRequestedWithHeaderOriginAllowList(android.webkit.WebSettings, java.util.Set<java.lang.String!>);
method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_ENABLE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setSafeBrowsingEnabled(android.webkit.WebSettings, boolean);
method @RequiresFeature(name=androidx.webkit.WebViewFeature.USER_AGENT_METADATA, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setUserAgentMetadata(android.webkit.WebSettings, androidx.webkit.UserAgentMetadata);
+ field public static final int ATTRIBUTION_BEHAVIOR_APP_SOURCE_AND_APP_TRIGGER = 3; // 0x3
+ field public static final int ATTRIBUTION_BEHAVIOR_APP_SOURCE_AND_WEB_TRIGGER = 1; // 0x1
+ field public static final int ATTRIBUTION_BEHAVIOR_DISABLED = 0; // 0x0
+ field public static final int ATTRIBUTION_BEHAVIOR_WEB_SOURCE_AND_WEB_TRIGGER = 2; // 0x2
field @Deprecated public static final int DARK_STRATEGY_PREFER_WEB_THEME_OVER_USER_AGENT_DARKENING = 2; // 0x2
field @Deprecated public static final int DARK_STRATEGY_USER_AGENT_DARKENING_ONLY = 0; // 0x0
field @Deprecated public static final int DARK_STRATEGY_WEB_THEME_DARKENING_ONLY = 1; // 0x1
@@ -268,6 +291,7 @@
method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_LISTENER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void addWebMessageListener(android.webkit.WebView, String, java.util.Set<java.lang.String!>, androidx.webkit.WebViewCompat.WebMessageListener);
method @RequiresFeature(name=androidx.webkit.WebViewFeature.CREATE_WEB_MESSAGE_CHANNEL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.WebMessagePortCompat![] createWebMessageChannel(android.webkit.WebView);
method public static android.content.pm.PackageInfo? getCurrentWebViewPackage(android.content.Context);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.Profile getProfile(android.webkit.WebView);
method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_PRIVACY_POLICY_URL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static android.net.Uri getSafeBrowsingPrivacyPolicyUrl();
method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_VARIATIONS_HEADER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static String getVariationsHeader();
method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_WEB_CHROME_CLIENT, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static android.webkit.WebChromeClient? getWebChromeClient(android.webkit.WebView);
@@ -278,6 +302,7 @@
method @RequiresFeature(name=androidx.webkit.WebViewFeature.VISUAL_STATE_CALLBACK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void postVisualStateCallback(android.webkit.WebView, long, androidx.webkit.WebViewCompat.VisualStateCallback);
method @RequiresFeature(name=androidx.webkit.WebViewFeature.POST_WEB_MESSAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void postWebMessage(android.webkit.WebView, androidx.webkit.WebMessageCompat, android.net.Uri);
method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_LISTENER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void removeWebMessageListener(android.webkit.WebView, String);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setProfile(android.webkit.WebView, String);
method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_ALLOWLIST, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setSafeBrowsingAllowlist(java.util.Set<java.lang.String!>, android.webkit.ValueCallback<java.lang.Boolean!>?);
method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_WHITELIST, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setSafeBrowsingWhitelist(java.util.List<java.lang.String!>, android.webkit.ValueCallback<java.lang.Boolean!>?);
method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setWebViewRenderProcessClient(android.webkit.WebView, androidx.webkit.WebViewRenderProcessClient?);
@@ -297,6 +322,7 @@
method public static boolean isFeatureSupported(String);
method public static boolean isStartupFeatureSupported(android.content.Context, String);
field public static final String ALGORITHMIC_DARKENING = "ALGORITHMIC_DARKENING";
+ field public static final String ATTRIBUTION_REGISTRATION_BEHAVIOR = "ATTRIBUTION_REGISTRATION_BEHAVIOR";
field public static final String CREATE_WEB_MESSAGE_CHANNEL = "CREATE_WEB_MESSAGE_CHANNEL";
field public static final String DISABLED_ACTION_MODE_MENU_ITEMS = "DISABLED_ACTION_MODE_MENU_ITEMS";
field public static final String DOCUMENT_START_SCRIPT = "DOCUMENT_START_SCRIPT";
@@ -309,6 +335,7 @@
field public static final String GET_WEB_VIEW_CLIENT = "GET_WEB_VIEW_CLIENT";
field public static final String GET_WEB_VIEW_RENDERER = "GET_WEB_VIEW_RENDERER";
field public static final String MULTI_PROCESS = "MULTI_PROCESS";
+ field public static final String MULTI_PROFILE = "MULTI_PROFILE";
field public static final String OFF_SCREEN_PRERASTER = "OFF_SCREEN_PRERASTER";
field public static final String POST_WEB_MESSAGE = "POST_WEB_MESSAGE";
field public static final String PROXY_OVERRIDE = "PROXY_OVERRIDE";
diff --git a/webkit/webkit/api/restricted_current.txt b/webkit/webkit/api/restricted_current.txt
index ebb7e57..aa2bfe1 100644
--- a/webkit/webkit/api/restricted_current.txt
+++ b/webkit/webkit/api/restricted_current.txt
@@ -27,6 +27,23 @@
method @RequiresFeature(name=androidx.webkit.WebViewFeature.STARTUP_FEATURE_SET_DIRECTORY_BASE_PATHS, enforcement="androidx.webkit.WebViewFeature#isConfigFeatureSupported(String, Context)") public androidx.webkit.ProcessGlobalConfig setDirectoryBasePaths(android.content.Context, java.io.File, java.io.File);
}
+ public interface Profile {
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public android.webkit.CookieManager getCookieManager();
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public android.webkit.GeolocationPermissions getGeolocationPermissions();
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public String getName();
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public android.webkit.ServiceWorkerController getServiceWorkerController();
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public android.webkit.WebStorage getWebStorage();
+ field public static final String DEFAULT_PROFILE_NAME = "Default";
+ }
+
+ public interface ProfileStore {
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public boolean deleteProfile(String);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public java.util.List<java.lang.String!> getAllProfileNames();
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.ProfileStore getInstance();
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public androidx.webkit.Profile getOrCreateProfile(String);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public androidx.webkit.Profile? getProfile(String);
+ }
+
public final class ProxyConfig {
method public java.util.List<java.lang.String!> getBypassRules();
method public java.util.List<androidx.webkit.ProxyConfig.ProxyRule!> getProxyRules();
@@ -197,6 +214,7 @@
}
public class WebSettingsCompat {
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.ATTRIBUTION_REGISTRATION_BEHAVIOR, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static int getAttributionRegistrationBehavior(android.webkit.WebSettings);
method @RequiresFeature(name=androidx.webkit.WebViewFeature.DISABLED_ACTION_MODE_MENU_ITEMS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static int getDisabledActionModeMenuItems(android.webkit.WebSettings);
method @RequiresFeature(name=androidx.webkit.WebViewFeature.ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean getEnterpriseAuthenticationAppLinkPolicyEnabled(android.webkit.WebSettings);
method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.FORCE_DARK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static int getForceDark(android.webkit.WebSettings);
@@ -207,6 +225,7 @@
method @RequiresFeature(name=androidx.webkit.WebViewFeature.USER_AGENT_METADATA, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.UserAgentMetadata getUserAgentMetadata(android.webkit.WebSettings);
method @RequiresFeature(name=androidx.webkit.WebViewFeature.ALGORITHMIC_DARKENING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean isAlgorithmicDarkeningAllowed(android.webkit.WebSettings);
method @RequiresFeature(name=androidx.webkit.WebViewFeature.ALGORITHMIC_DARKENING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setAlgorithmicDarkeningAllowed(android.webkit.WebSettings, boolean);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.ATTRIBUTION_REGISTRATION_BEHAVIOR, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setAttributionRegistrationBehavior(android.webkit.WebSettings, int);
method @RequiresFeature(name=androidx.webkit.WebViewFeature.DISABLED_ACTION_MODE_MENU_ITEMS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setDisabledActionModeMenuItems(android.webkit.WebSettings, int);
method @RequiresFeature(name=androidx.webkit.WebViewFeature.ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setEnterpriseAuthenticationAppLinkPolicyEnabled(android.webkit.WebSettings, boolean);
method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.FORCE_DARK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setForceDark(android.webkit.WebSettings, int);
@@ -215,6 +234,10 @@
method @RequiresFeature(name="REQUESTED_WITH_HEADER_ALLOW_LIST", enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setRequestedWithHeaderOriginAllowList(android.webkit.WebSettings, java.util.Set<java.lang.String!>);
method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_ENABLE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setSafeBrowsingEnabled(android.webkit.WebSettings, boolean);
method @RequiresFeature(name=androidx.webkit.WebViewFeature.USER_AGENT_METADATA, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setUserAgentMetadata(android.webkit.WebSettings, androidx.webkit.UserAgentMetadata);
+ field public static final int ATTRIBUTION_BEHAVIOR_APP_SOURCE_AND_APP_TRIGGER = 3; // 0x3
+ field public static final int ATTRIBUTION_BEHAVIOR_APP_SOURCE_AND_WEB_TRIGGER = 1; // 0x1
+ field public static final int ATTRIBUTION_BEHAVIOR_DISABLED = 0; // 0x0
+ field public static final int ATTRIBUTION_BEHAVIOR_WEB_SOURCE_AND_WEB_TRIGGER = 2; // 0x2
field @Deprecated public static final int DARK_STRATEGY_PREFER_WEB_THEME_OVER_USER_AGENT_DARKENING = 2; // 0x2
field @Deprecated public static final int DARK_STRATEGY_USER_AGENT_DARKENING_ONLY = 0; // 0x0
field @Deprecated public static final int DARK_STRATEGY_WEB_THEME_DARKENING_ONLY = 1; // 0x1
@@ -268,6 +291,7 @@
method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_LISTENER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void addWebMessageListener(android.webkit.WebView, String, java.util.Set<java.lang.String!>, androidx.webkit.WebViewCompat.WebMessageListener);
method @RequiresFeature(name=androidx.webkit.WebViewFeature.CREATE_WEB_MESSAGE_CHANNEL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.WebMessagePortCompat![] createWebMessageChannel(android.webkit.WebView);
method public static android.content.pm.PackageInfo? getCurrentWebViewPackage(android.content.Context);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.Profile getProfile(android.webkit.WebView);
method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_PRIVACY_POLICY_URL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static android.net.Uri getSafeBrowsingPrivacyPolicyUrl();
method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_VARIATIONS_HEADER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static String getVariationsHeader();
method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_WEB_CHROME_CLIENT, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static android.webkit.WebChromeClient? getWebChromeClient(android.webkit.WebView);
@@ -278,6 +302,7 @@
method @RequiresFeature(name=androidx.webkit.WebViewFeature.VISUAL_STATE_CALLBACK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void postVisualStateCallback(android.webkit.WebView, long, androidx.webkit.WebViewCompat.VisualStateCallback);
method @RequiresFeature(name=androidx.webkit.WebViewFeature.POST_WEB_MESSAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void postWebMessage(android.webkit.WebView, androidx.webkit.WebMessageCompat, android.net.Uri);
method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_LISTENER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void removeWebMessageListener(android.webkit.WebView, String);
+ method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setProfile(android.webkit.WebView, String);
method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_ALLOWLIST, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setSafeBrowsingAllowlist(java.util.Set<java.lang.String!>, android.webkit.ValueCallback<java.lang.Boolean!>?);
method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_WHITELIST, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setSafeBrowsingWhitelist(java.util.List<java.lang.String!>, android.webkit.ValueCallback<java.lang.Boolean!>?);
method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setWebViewRenderProcessClient(android.webkit.WebView, androidx.webkit.WebViewRenderProcessClient?);
@@ -297,6 +322,7 @@
method public static boolean isFeatureSupported(String);
method public static boolean isStartupFeatureSupported(android.content.Context, String);
field public static final String ALGORITHMIC_DARKENING = "ALGORITHMIC_DARKENING";
+ field public static final String ATTRIBUTION_REGISTRATION_BEHAVIOR = "ATTRIBUTION_REGISTRATION_BEHAVIOR";
field public static final String CREATE_WEB_MESSAGE_CHANNEL = "CREATE_WEB_MESSAGE_CHANNEL";
field public static final String DISABLED_ACTION_MODE_MENU_ITEMS = "DISABLED_ACTION_MODE_MENU_ITEMS";
field public static final String DOCUMENT_START_SCRIPT = "DOCUMENT_START_SCRIPT";
@@ -309,6 +335,7 @@
field public static final String GET_WEB_VIEW_CLIENT = "GET_WEB_VIEW_CLIENT";
field public static final String GET_WEB_VIEW_RENDERER = "GET_WEB_VIEW_RENDERER";
field public static final String MULTI_PROCESS = "MULTI_PROCESS";
+ field public static final String MULTI_PROFILE = "MULTI_PROFILE";
field public static final String OFF_SCREEN_PRERASTER = "OFF_SCREEN_PRERASTER";
field public static final String POST_WEB_MESSAGE = "POST_WEB_MESSAGE";
field public static final String PROXY_OVERRIDE = "PROXY_OVERRIDE";
diff --git a/webkit/webkit/src/main/java/androidx/webkit/Profile.java b/webkit/webkit/src/main/java/androidx/webkit/Profile.java
index ccf3da4..14d9e0f 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/Profile.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/Profile.java
@@ -23,17 +23,13 @@
import androidx.annotation.NonNull;
import androidx.annotation.RequiresFeature;
-import androidx.annotation.RestrictTo;
/**
* A Profile represents one browsing session for WebView.
* <p> You can have multiple profiles and each profile holds its own set of data. The creation
* and deletion of the Profile is being managed by {@link ProfileStore}.
*
- * @hide
- *
*/
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public interface Profile {
/**
@@ -61,7 +57,7 @@
@NonNull
@RequiresFeature(name = WebViewFeature.MULTI_PROFILE,
enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
- CookieManager getCookieManager() throws IllegalStateException;
+ CookieManager getCookieManager();
/**
* Returns the profile's web storage.
@@ -74,7 +70,7 @@
@NonNull
@RequiresFeature(name = WebViewFeature.MULTI_PROFILE,
enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
- WebStorage getWebStorage() throws IllegalStateException;
+ WebStorage getWebStorage();
/**
* Returns the geolocation permissions of the profile.
@@ -87,7 +83,7 @@
@NonNull
@RequiresFeature(name = WebViewFeature.MULTI_PROFILE,
enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
- GeolocationPermissions getGeolocationPermissions() throws IllegalStateException;
+ GeolocationPermissions getGeolocationPermissions();
/**
* Returns the service worker controller of the profile.
@@ -100,6 +96,6 @@
@NonNull
@RequiresFeature(name = WebViewFeature.MULTI_PROFILE,
enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
- ServiceWorkerController getServiceWorkerController() throws IllegalStateException;
+ ServiceWorkerController getServiceWorkerController();
}
diff --git a/webkit/webkit/src/main/java/androidx/webkit/ProfileStore.java b/webkit/webkit/src/main/java/androidx/webkit/ProfileStore.java
index f7a2af9..4ebb193 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/ProfileStore.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/ProfileStore.java
@@ -19,7 +19,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresFeature;
-import androidx.annotation.RestrictTo;
import androidx.webkit.internal.ApiFeature;
import androidx.webkit.internal.ProfileStoreImpl;
import androidx.webkit.internal.WebViewFeatureInternal;
@@ -41,10 +40,7 @@
* profileStore.deleteProfile("profile_test");
*
* </pre>
- *
- * @hide
*/
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public interface ProfileStore {
/**
@@ -128,6 +124,5 @@
*/
@RequiresFeature(name = WebViewFeature.MULTI_PROFILE,
enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
- boolean deleteProfile(@NonNull String name) throws IllegalStateException,
- IllegalArgumentException;
+ boolean deleteProfile(@NonNull String name);
}
diff --git a/webkit/webkit/src/main/java/androidx/webkit/WebSettingsCompat.java b/webkit/webkit/src/main/java/androidx/webkit/WebSettingsCompat.java
index 4a544da..08ed011 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/WebSettingsCompat.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/WebSettingsCompat.java
@@ -783,7 +783,6 @@
* happen depending on the installed version of WebView, but any response is discarded and
* nothing will be stored on the device.
*/
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static final int ATTRIBUTION_BEHAVIOR_DISABLED =
WebSettingsBoundaryInterface.AttributionBehavior.DISABLED;
/**
@@ -793,7 +792,6 @@
* <p>
* This is the default behavior.
*/
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static final int ATTRIBUTION_BEHAVIOR_APP_SOURCE_AND_WEB_TRIGGER =
WebSettingsBoundaryInterface.AttributionBehavior.APP_SOURCE_AND_WEB_TRIGGER;
/**
@@ -804,14 +802,12 @@
* <a href="https://developer.android.com/design-for-safety/privacy-sandbox/attribution-app-to-web#register-attribution">
* use web sources</a>.
*/
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static final int ATTRIBUTION_BEHAVIOR_WEB_SOURCE_AND_WEB_TRIGGER =
WebSettingsBoundaryInterface.AttributionBehavior.WEB_SOURCE_AND_WEB_TRIGGER;
/**
* AttributionRegistrationBehavior that allows apps to register app sources and app triggers
* from WebView.
*/
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static final int ATTRIBUTION_BEHAVIOR_APP_SOURCE_AND_APP_TRIGGER =
WebSettingsBoundaryInterface.AttributionBehavior.APP_SOURCE_AND_APP_TRIGGER;
@@ -842,7 +838,6 @@
* @param settings Settings retrieved from {@link WebView#getSettings()}.
* @param behavior New behavior to use.
*/
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@RequiresFeature(name = WebViewFeature.ATTRIBUTION_REGISTRATION_BEHAVIOR,
enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
public static void setAttributionRegistrationBehavior(@NonNull WebSettings settings,
@@ -866,7 +861,6 @@
* @see #ATTRIBUTION_BEHAVIOR_APP_SOURCE_AND_APP_TRIGGER
* @param settings Settings retrieved from {@link WebView#getSettings()}.
*/
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@RequiresFeature(name = WebViewFeature.ATTRIBUTION_REGISTRATION_BEHAVIOR,
enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
@AttributionRegistrationBehavior
diff --git a/webkit/webkit/src/main/java/androidx/webkit/WebViewCompat.java b/webkit/webkit/src/main/java/androidx/webkit/WebViewCompat.java
index 8deb404..6432c54 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/WebViewCompat.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/WebViewCompat.java
@@ -1092,12 +1092,11 @@
* called on the WebView before this method.
* @throws IllegalStateException if the WebView has previously navigated to a web page.
*/
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@RequiresFeature(
name = WebViewFeature.MULTI_PROFILE,
enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
public static void setProfile(@NonNull WebView webView,
- @NonNull String profileName) throws IllegalStateException {
+ @NonNull String profileName) {
final ApiFeature.NoFramework feature = WebViewFeatureInternal.MULTI_PROFILE;
if (feature.isSupportedByWebView()) {
getProvider(webView).setProfileWithName(profileName);
@@ -1119,11 +1118,10 @@
* @throws IllegalStateException if the WebView has been destroyed.
*/
@NonNull
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@RequiresFeature(
name = WebViewFeature.MULTI_PROFILE,
enforcement = "androidx.webkit.WebViewFeature#isFeatureSupported")
- public static Profile getProfile(@NonNull WebView webView) throws IllegalStateException {
+ public static Profile getProfile(@NonNull WebView webView) {
final ApiFeature.NoFramework feature = WebViewFeatureInternal.MULTI_PROFILE;
if (feature.isSupportedByWebView()) {
return getProvider(webView).getProfile();
diff --git a/webkit/webkit/src/main/java/androidx/webkit/WebViewFeature.java b/webkit/webkit/src/main/java/androidx/webkit/WebViewFeature.java
index 11936ee..fa09c22 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/WebViewFeature.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/WebViewFeature.java
@@ -557,7 +557,6 @@
* {@link ProfileStore#deleteProfile(String)}.
* {@link ProfileStore#getInstance()}.
*/
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static final String MULTI_PROFILE = "MULTI_PROFILE";
/**
@@ -566,7 +565,6 @@
* {@link androidx.webkit.WebSettingsCompat#setAttributionRegistrationBehavior(WebSettings, int)}
* {@link androidx.webkit.WebSettingsCompat#getAttributionRegistrationBehavior(WebSettings)}
*/
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static final String ATTRIBUTION_REGISTRATION_BEHAVIOR =
"ATTRIBUTION_REGISTRATION_BEHAVIOR";
diff --git a/webkit/webkit/src/main/java/androidx/webkit/internal/WebViewFeatureInternal.java b/webkit/webkit/src/main/java/androidx/webkit/internal/WebViewFeatureInternal.java
index 4922c47..4790c17 100644
--- a/webkit/webkit/src/main/java/androidx/webkit/internal/WebViewFeatureInternal.java
+++ b/webkit/webkit/src/main/java/androidx/webkit/internal/WebViewFeatureInternal.java
@@ -557,7 +557,6 @@
* {@link ProfileStore#deleteProfile(String)}.
* {@link ProfileStore#getInstance()}.
*/
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static final ApiFeature.NoFramework MULTI_PROFILE =
new ApiFeature.NoFramework(WebViewFeature.MULTI_PROFILE, Features.MULTI_PROFILE) {
@Override
diff --git a/work/work-runtime/src/main/java/androidx/work/impl/WorkLauncher.kt b/work/work-runtime/src/main/java/androidx/work/impl/WorkLauncher.kt
index f5fdeca..3dafa1c 100644
--- a/work/work-runtime/src/main/java/androidx/work/impl/WorkLauncher.kt
+++ b/work/work-runtime/src/main/java/androidx/work/impl/WorkLauncher.kt
@@ -21,7 +21,6 @@
import androidx.work.WorkerParameters
import androidx.work.WorkerParameters.RuntimeExtras
import androidx.work.impl.model.WorkSpec
-import androidx.work.impl.utils.StartWorkRunnable
import androidx.work.impl.utils.StopWorkRunnable
import androidx.work.impl.utils.taskexecutor.TaskExecutor
@@ -55,8 +54,7 @@
val workTaskExecutor: TaskExecutor,
) : WorkLauncher {
override fun startWork(workSpecId: StartStopToken, runtimeExtras: RuntimeExtras?) {
- val startWork = StartWorkRunnable(processor, workSpecId, runtimeExtras)
- workTaskExecutor.executeOnTaskThread(startWork)
+ workTaskExecutor.executeOnTaskThread { processor.startWork(workSpecId, runtimeExtras) }
}
override fun stopWork(workSpecId: StartStopToken, @StopReason reason: Int) {
diff --git a/work/work-runtime/src/main/java/androidx/work/impl/utils/StartWorkRunnable.kt b/work/work-runtime/src/main/java/androidx/work/impl/utils/StartWorkRunnable.kt
deleted file mode 100644
index 58e8038..0000000
--- a/work/work-runtime/src/main/java/androidx/work/impl/utils/StartWorkRunnable.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2018 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.work.impl.utils
-
-import androidx.annotation.RestrictTo
-import androidx.work.WorkerParameters
-import androidx.work.impl.Processor
-import androidx.work.impl.StartStopToken
-
-/**
- * A [Runnable] that can start work on the
- * [androidx.work.impl.Processor].
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-class StartWorkRunnable(
- private val processor: Processor,
- private val startStopToken: StartStopToken,
- private val runtimeExtras: WorkerParameters.RuntimeExtras?
-) : Runnable {
- override fun run() {
- processor.startWork(startStopToken, runtimeExtras)
- }
-}