Merge "Remove unused proto" into androidx-master-dev
diff --git a/.github/workflows/presubmit.yml b/.github/workflows/presubmit.yml
index d44d46c..3fd78ec 100644
--- a/.github/workflows/presubmit.yml
+++ b/.github/workflows/presubmit.yml
@@ -49,30 +49,28 @@
with:
fetch-depth: 1
- - name: "Cache ~/.gradle/caches"
- uses: actions/cache@v2.1.2
+ - name: "Setup JDK 11"
+ id: setup-java
+ uses: actions/setup-java@v1
with:
- path: "~/.gradle/caches"
- key: gradle-cache-${{ runner.os }}-${{ hashFiles('**/*.gradle') }}-${{ hashFiles('**/gradle.properties') }}
- restore-keys: |
- gradle-cache-${{ runner.os }}-
+ java-version: "11"
- - name: "Cache ~/.gradlew/wrapper"
- uses: actions/cache@v2.1.2
- with:
- path: ~/.gradle/wrapper
- key: gradle-wrapper-${{ runner.os }}-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties') }}
+ - name: "Set environment variables"
+ shell: bash
+ run: |
+ set -x
+ echo "ANDROID_SDK_ROOT=$HOME/Library/Android/sdk" >> $GITHUB_ENV
+ echo "DIST_DIR=$HOME/dist" >> $GITHUB_ENV
- - name: "Cache ~/.konan"
- uses: actions/cache@v2.1.2
+ - name: "./gradlew buildOnServer"
+ uses: eskatos/gradle-command-action@v1
+ env:
+ JAVA_HOME: ${{ steps.setup-java.outputs.path }}
with:
- path: ~/.konan
- key: konan-${{ runner.os }}
-
- - name: "Build"
- uses: androidx/build-on-server-action@main
- with:
- path: ${{ env.group-id }}
+ arguments: buildOnServer
+ build-root-directory: ${{ env.group-id }}
+ gradle-executable: ${{ env.group-id }}/gradlew
+ wrapper-directory: ${{ env.group-id }}/gradle/wrapper
- name: "Upload build artifacts"
continue-on-error: true
@@ -104,30 +102,28 @@
with:
fetch-depth: 1
- - name: "Cache ~/.gradle/caches"
- uses: actions/cache@v2.1.2
+ - name: "Setup JDK 11"
+ id: setup-java
+ uses: actions/setup-java@v1
with:
- path: "~/.gradle/caches"
- key: gradle-cache-${{ runner.os }}-${{ hashFiles('**/*.gradle') }}-${{ hashFiles('**/gradle.properties') }}
- restore-keys: |
- gradle-cache-${{ runner.os }}-
+ java-version: "11"
- - name: "Cache ~/.gradlew/wrapper"
- uses: actions/cache@v2.1.2
- with:
- path: ~/.gradle/wrapper
- key: gradle-wrapper-${{ runner.os }}-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties') }}
+ - name: "Set environment variables"
+ shell: bash
+ run: |
+ set -x
+ echo "ANDROID_SDK_ROOT=$HOME/Library/Android/sdk" >> $GITHUB_ENV
+ echo "DIST_DIR=$HOME/dist" >> $GITHUB_ENV
- - name: "Cache ~/.konan"
- uses: actions/cache@v2.1.2
+ - name: "./gradlew buildOnServer"
+ uses: eskatos/gradle-command-action@v1
+ env:
+ JAVA_HOME: ${{ steps.setup-java.outputs.path }}
with:
- path: ~/.konan
- key: konan-${{ runner.os }}
-
- - name: "Build"
- uses: androidx/build-on-server-action@main
- with:
- path: ${{ env.group-id }}
+ arguments: buildOnServer
+ build-root-directory: ${{ env.group-id }}
+ gradle-executable: ${{ env.group-id }}/gradlew
+ wrapper-directory: ${{ env.group-id }}/gradle/wrapper
- name: "Upload build artifacts"
continue-on-error: true
@@ -159,30 +155,28 @@
with:
fetch-depth: 1
- - name: "Cache ~/.gradle/caches"
- uses: actions/cache@v2.1.2
+ - name: "Setup JDK 11"
+ id: setup-java
+ uses: actions/setup-java@v1
with:
- path: "~/.gradle/caches"
- key: gradle-cache-${{ runner.os }}-${{ hashFiles('**/*.gradle') }}-${{ hashFiles('**/gradle.properties') }}
- restore-keys: |
- gradle-cache-${{ runner.os }}-
+ java-version: "11"
- - name: "Cache ~/.gradlew/wrapper"
- uses: actions/cache@v2.1.2
- with:
- path: ~/.gradle/wrapper
- key: gradle-wrapper-${{ runner.os }}-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties') }}
+ - name: "Set environment variables"
+ shell: bash
+ run: |
+ set -x
+ echo "ANDROID_SDK_ROOT=$HOME/Library/Android/sdk" >> $GITHUB_ENV
+ echo "DIST_DIR=$HOME/dist" >> $GITHUB_ENV
- - name: "Cache ~/.konan"
- uses: actions/cache@v2.1.2
+ - name: "./gradlew buildOnServer"
+ uses: eskatos/gradle-command-action@v1
+ env:
+ JAVA_HOME: ${{ steps.setup-java.outputs.path }}
with:
- path: ~/.konan
- key: konan-${{ runner.os }}
-
- - name: "Build"
- uses: androidx/build-on-server-action@main
- with:
- path: ${{ env.group-id }}
+ arguments: buildOnServer
+ build-root-directory: ${{ env.group-id }}
+ gradle-executable: ${{ env.group-id }}/gradlew
+ wrapper-directory: ${{ env.group-id }}/gradle/wrapper
- name: "Upload build artifacts"
continue-on-error: true
@@ -214,30 +208,28 @@
with:
fetch-depth: 1
- - name: "Cache ~/.gradle/caches"
- uses: actions/cache@v2.1.2
+ - name: "Setup JDK 11"
+ id: setup-java
+ uses: actions/setup-java@v1
with:
- path: "~/.gradle/caches"
- key: gradle-cache-${{ runner.os }}-${{ hashFiles('**/*.gradle') }}-${{ hashFiles('**/gradle.properties') }}
- restore-keys: |
- gradle-cache-${{ runner.os }}-
+ java-version: "11"
- - name: "Cache ~/.gradlew/wrapper"
- uses: actions/cache@v2.1.2
- with:
- path: ~/.gradle/wrapper
- key: gradle-wrapper-${{ runner.os }}-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties') }}
+ - name: "Set environment variables"
+ shell: bash
+ run: |
+ set -x
+ echo "ANDROID_SDK_ROOT=$HOME/Library/Android/sdk" >> $GITHUB_ENV
+ echo "DIST_DIR=$HOME/dist" >> $GITHUB_ENV
- - name: "Cache ~/.konan"
- uses: actions/cache@v2.1.2
+ - name: "./gradlew buildOnServer"
+ uses: eskatos/gradle-command-action@v1
+ env:
+ JAVA_HOME: ${{ steps.setup-java.outputs.path }}
with:
- path: ~/.konan
- key: konan-${{ runner.os }}
-
- - name: "Build"
- uses: androidx/build-on-server-action@main
- with:
- path: ${{ env.group-id }}
+ arguments: buildOnServer
+ build-root-directory: ${{ env.group-id }}
+ gradle-executable: ${{ env.group-id }}/gradlew
+ wrapper-directory: ${{ env.group-id }}/gradle/wrapper
- name: "Upload build artifacts"
continue-on-error: true
@@ -269,32 +261,31 @@
with:
fetch-depth: 1
- - name: "Cache ~/.gradle/caches"
- uses: actions/cache@v2.1.2
+ - name: "Setup JDK 11"
+ id: setup-java
+ uses: actions/setup-java@v1
with:
- path: "~/.gradle/caches"
- key: gradle-cache-${{ runner.os }}-${{ hashFiles('**/*.gradle') }}-${{ hashFiles('**/gradle.properties') }}
- restore-keys: |
- gradle-cache-${{ runner.os }}-
+ java-version: "11"
- - name: "Cache ~/.gradlew/wrapper"
- uses: actions/cache@v2.1.2
+ - name: "Set environment variables"
+ shell: bash
+ run: |
+ set -x
+ echo "ANDROID_SDK_ROOT=$HOME/Library/Android/sdk" >> $GITHUB_ENV
+ echo "DIST_DIR=$HOME/dist" >> $GITHUB_ENV
+
+ - name: "./gradlew buildOnServer"
+ uses: eskatos/gradle-command-action@v1
+ env:
+ JAVA_HOME: ${{ steps.setup-java.outputs.path }}
with:
- path: ~/.gradle/wrapper
- key: gradle-wrapper-${{ runner.os }}-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties') }}
+ arguments: buildOnServer
+ build-root-directory: ${{ env.group-id }}
+ gradle-executable: ${{ env.group-id }}/gradlew
+ wrapper-directory: ${{ env.group-id }}/gradle/wrapper
- - name: "Cache ~/.konan"
- uses: actions/cache@v2.1.2
- with:
- path: ~/.konan
- key: konan-${{ runner.os }}
- - name: "Build"
- uses: androidx/build-on-server-action@main
- with:
- path: ${{ env.group-id }}
-
- - name: "Upload build artifacts"
+ - name: "upload build artifacts"
continue-on-error: true
if: always()
uses: actions/upload-artifact@v2
@@ -324,30 +315,28 @@
with:
fetch-depth: 1
- - name: "Cache ~/.gradle/caches"
- uses: actions/cache@v2.1.2
+ - name: "Setup JDK 11"
+ id: setup-java
+ uses: actions/setup-java@v1
with:
- path: "~/.gradle/caches"
- key: gradle-cache-${{ runner.os }}-${{ hashFiles('**/*.gradle') }}-${{ hashFiles('**/gradle.properties') }}
- restore-keys: |
- gradle-cache-${{ runner.os }}-
+ java-version: "11"
- - name: "Cache ~/.gradlew/wrapper"
- uses: actions/cache@v2.1.2
- with:
- path: ~/.gradle/wrapper
- key: gradle-wrapper-${{ runner.os }}-${{ hashFiles('**/gradle/wrapper/gradle-wrapper.properties') }}
+ - name: "Set environment variables"
+ shell: bash
+ run: |
+ set -x
+ echo "ANDROID_SDK_ROOT=$HOME/Library/Android/sdk" >> $GITHUB_ENV
+ echo "DIST_DIR=$HOME/dist" >> $GITHUB_ENV
- - name: "Cache ~/.konan"
- uses: actions/cache@v2.1.2
+ - name: "./gradlew buildOnServer"
+ uses: eskatos/gradle-command-action@v1
+ env:
+ JAVA_HOME: ${{ steps.setup-java.outputs.path }}
with:
- path: ~/.konan
- key: konan-${{ runner.os }}
-
- - name: "Build"
- uses: androidx/build-on-server-action@main
- with:
- path: ${{ env.group-id }}
+ arguments: buildOnServer
+ build-root-directory: ${{ env.group-id }}
+ gradle-executable: ${{ env.group-id }}/gradlew
+ wrapper-directory: ${{ env.group-id }}/gradle/wrapper
- name: "Upload build artifacts"
continue-on-error: true
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AnnotationProcessorTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AnnotationProcessorTest.java
index 080cf76..09ac4df 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AnnotationProcessorTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AnnotationProcessorTest.java
@@ -16,6 +16,8 @@
package androidx.appsearch.app;
+import static androidx.appsearch.app.AppSearchSchema.PropertyConfig.INDEXING_TYPE_PREFIXES;
+import static androidx.appsearch.app.AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN;
import static androidx.appsearch.app.AppSearchTestUtils.checkIsBatchResultSuccess;
import static androidx.appsearch.app.AppSearchTestUtils.checkIsResultSuccess;
import static androidx.appsearch.app.AppSearchTestUtils.convertSearchResultsToDocuments;
@@ -51,6 +53,27 @@
}
@AppSearchDocument
+ static class Card {
+ @AppSearchDocument.Uri String mUri;
+ @AppSearchDocument.Property
+ (indexingType = INDEXING_TYPE_PREFIXES, tokenizerType = TOKENIZER_TYPE_PLAIN)
+ String mString; // 3a
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof Card)) {
+ return false;
+ }
+ Card otherCard = (Card) other;
+ assertThat(otherCard.mUri).isEqualTo(this.mUri);
+ return true;
+ }
+ }
+
+ @AppSearchDocument
static class Gift {
@AppSearchDocument.Uri String mUri;
@@ -62,7 +85,7 @@
@AppSearchDocument.Property Collection<Boolean> mCollectBoolean; // 1a
@AppSearchDocument.Property Collection<byte[]> mCollectByteArr; // 1a
@AppSearchDocument.Property Collection<String> mCollectString; // 1b
- @AppSearchDocument.Property Collection<Gift> mCollectGift; // 1c
+ @AppSearchDocument.Property Collection<Card> mCollectCard; // 1c
// Arrays
@AppSearchDocument.Property Long[] mArrBoxLong; // 2a
@@ -78,7 +101,7 @@
@AppSearchDocument.Property byte[][] mArrUnboxByteArr; // 2b
@AppSearchDocument.Property Byte[] mBoxByteArr; // 2a
@AppSearchDocument.Property String[] mArrString; // 2b
- @AppSearchDocument.Property Gift[] mArrGift; // 2c
+ @AppSearchDocument.Property Card[] mArrCard; // 2c
// Single values
@AppSearchDocument.Property String mString; // 3a
@@ -93,7 +116,7 @@
@AppSearchDocument.Property Boolean mBoxBoolean; // 3a
@AppSearchDocument.Property boolean mUnboxBoolean; // 3b
@AppSearchDocument.Property byte[] mUnboxByteArr; // 3a
- @AppSearchDocument.Property Gift mGift; // 3c
+ @AppSearchDocument.Property Card mCard; // 3c
@Override
public boolean equals(Object other) {
@@ -118,7 +141,7 @@
assertThat(otherGift.mArrUnboxFloat).isEqualTo(this.mArrUnboxFloat);
assertThat(otherGift.mArrUnboxLong).isEqualTo(this.mArrUnboxLong);
assertThat(otherGift.mArrUnboxInt).isEqualTo(this.mArrUnboxInt);
- assertThat(otherGift.mArrGift).isEqualTo(this.mArrGift);
+ assertThat(otherGift.mArrCard).isEqualTo(this.mArrCard);
assertThat(otherGift.mCollectLong).isEqualTo(this.mCollectLong);
assertThat(otherGift.mCollectInteger).isEqualTo(this.mCollectInteger);
@@ -126,7 +149,7 @@
assertThat(otherGift.mCollectString).isEqualTo(this.mCollectString);
assertThat(otherGift.mCollectDouble).isEqualTo(this.mCollectDouble);
assertThat(otherGift.mCollectFloat).isEqualTo(this.mCollectFloat);
- assertThat(otherGift.mCollectGift).isEqualTo(this.mCollectGift);
+ assertThat(otherGift.mCollectCard).isEqualTo(this.mCollectCard);
checkCollectByteArr(otherGift.mCollectByteArr, this.mCollectByteArr);
assertThat(otherGift.mString).isEqualTo(this.mString);
@@ -141,7 +164,7 @@
assertThat(otherGift.mBoxBoolean).isEqualTo(this.mBoxBoolean);
assertThat(otherGift.mUnboxBoolean).isEqualTo(this.mUnboxBoolean);
assertThat(otherGift.mUnboxByteArr).isEqualTo(this.mUnboxByteArr);
- assertThat(otherGift.mGift).isEqualTo(this.mGift);
+ assertThat(otherGift.mCard).isEqualTo(this.mCard);
return true;
}
@@ -160,7 +183,7 @@
//TODO(b/156296904) add test for int, float, GenericDocument, and class with
// @AppSearchDocument annotation
checkIsResultSuccess(mSession.setSchema(
- new SetSchemaRequest.Builder().addDataClass(Gift.class).build()));
+ new SetSchemaRequest.Builder().addDataClass(Card.class, Gift.class).build()));
// Create a Gift object and assign values.
Gift inputDataClass = new Gift();
@@ -180,11 +203,11 @@
inputDataClass.mArrUnboxInt = new int[]{5, 4};
inputDataClass.mArrUnboxLong = new long[]{7, 6};
- Gift innerGift1 = new Gift();
- innerGift1.mUri = "innerGift.uri1";
- Gift innerGift2 = new Gift();
- innerGift2.mUri = "innerGift.uri2";
- inputDataClass.mArrGift = new Gift[]{innerGift1, innerGift2};
+ Card card1 = new Card();
+ card1.mUri = "card.uri1";
+ Card card2 = new Card();
+ card2.mUri = "card.uri2";
+ inputDataClass.mArrCard = new Card[]{card2, card2};
inputDataClass.mCollectLong = Arrays.asList(inputDataClass.mArrBoxLong);
inputDataClass.mCollectInteger = Arrays.asList(inputDataClass.mArrBoxInteger);
@@ -193,7 +216,7 @@
inputDataClass.mCollectDouble = Arrays.asList(inputDataClass.mArrBoxDouble);
inputDataClass.mCollectFloat = Arrays.asList(inputDataClass.mArrBoxFloat);
inputDataClass.mCollectByteArr = Arrays.asList(inputDataClass.mArrUnboxByteArr);
- inputDataClass.mCollectGift = Arrays.asList(innerGift1, innerGift2);
+ inputDataClass.mCollectCard = Arrays.asList(card2, card2);
inputDataClass.mString = "String";
inputDataClass.mBoxLong = 1L;
@@ -207,7 +230,7 @@
inputDataClass.mBoxBoolean = true;
inputDataClass.mUnboxBoolean = false;
inputDataClass.mUnboxByteArr = new byte[]{1, 2, 3};
- inputDataClass.mGift = innerGift1;
+ inputDataClass.mCard = card1;
// Index the Gift document and query it.
checkIsBatchResultSuccess(mSession.putDocuments(
@@ -231,7 +254,7 @@
public void testAnnotationProcessor_QueryByType() throws Exception {
checkIsResultSuccess(mSession.setSchema(
new SetSchemaRequest.Builder()
- .addDataClass(Gift.class)
+ .addDataClass(Card.class, Gift.class)
.addSchema(AppSearchEmail.SCHEMA).build()));
// Create documents and index them
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchSessionTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchSessionTest.java
index 14f4e33..48129c8 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchSessionTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchSessionTest.java
@@ -152,8 +152,8 @@
.addProperty(new AppSearchSchema.PropertyConfig.Builder("price")
.setDataType(PropertyConfig.DATA_TYPE_INT64)
.setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
- .setIndexingType(PropertyConfig.INDEXING_TYPE_EXACT_TERMS)
- .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setIndexingType(PropertyConfig.INDEXING_TYPE_NONE)
+ .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_NONE)
.build())
.build();
checkIsResultSuccess(
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/annotation/AppSearchDocument.java b/appsearch/appsearch/src/main/java/androidx/appsearch/annotation/AppSearchDocument.java
index ecb2b9e..1f1b399 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/annotation/AppSearchDocument.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/annotation/AppSearchDocument.java
@@ -191,6 +191,7 @@
* <p>If not specified, defaults to {@link
* AppSearchSchema.PropertyConfig#INDEXING_TYPE_NONE} (the field will not be indexed and
* cannot be queried).
+ * TODO(b/171857731) renamed to TermMatchType when using String-specific indexing config.
*/
@AppSearchSchema.PropertyConfig.IndexingType int indexingType()
default AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE;
diff --git a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/SchemaCodeGenerator.java b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/SchemaCodeGenerator.java
index 0af8dc1..eca9c5a 100644
--- a/appsearch/compiler/src/main/java/androidx/appsearch/compiler/SchemaCodeGenerator.java
+++ b/appsearch/compiler/src/main/java/androidx/appsearch/compiler/SchemaCodeGenerator.java
@@ -188,6 +188,10 @@
// Find tokenizer type
int tokenizerType = Integer.parseInt(params.get("tokenizerType").toString());
+ if (Integer.parseInt(params.get("indexingType").toString()) == 0) {
+ //TODO(b/171857731) remove this hack after apply to Icing lib's change.
+ tokenizerType = 0;
+ }
ClassName tokenizerEnum;
if (tokenizerType == 0 || isPropertyDocument) { // TOKENIZER_TYPE_NONE
//It is only valid for tokenizer_type to be 'NONE' if the data type is
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 86f579d..94a5317 100644
--- a/appsearch/compiler/src/test/java/androidx/appsearch/compiler/AppSearchCompilerTest.java
+++ b/appsearch/compiler/src/test/java/androidx/appsearch/compiler/AppSearchCompilerTest.java
@@ -454,7 +454,8 @@
+ "@AppSearchDocument\n"
+ "public class Gift {\n"
+ " @AppSearchDocument.Uri String uri;\n"
- + " @AppSearchDocument.Property(tokenizerType=100) String str;\n"
+ + " @AppSearchDocument.Property(indexingType=1, tokenizerType=100)\n"
+ + " String str;\n"
+ "}\n");
CompilationSubject.assertThat(compilation).hadErrorContaining("Unknown tokenizer type 100");
}
@@ -485,7 +486,8 @@
+ "@AppSearchDocument\n"
+ "public class Gift {\n"
+ " @AppSearchDocument.Uri String uri;\n"
- + " @AppSearchDocument.Property(indexingType=100) String str;\n"
+ + " @AppSearchDocument.Property(indexingType=100, tokenizerType=1)\n"
+ + " String str;\n"
+ "}\n");
CompilationSubject.assertThat(compilation).hadErrorContaining("Unknown indexing type 100");
}
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAllSingleTypes.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAllSingleTypes.JAVA
index 4dfb30e..b256fa5 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAllSingleTypes.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAllSingleTypes.JAVA
@@ -26,43 +26,43 @@
.addProperty(new AppSearchSchema.PropertyConfig.Builder("stringProp")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("integerProp")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("longProp")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("floatProp")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_DOUBLE)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("doubleProp")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_DOUBLE)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("booleanProp")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BOOLEAN)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("bytesProp")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BYTES)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.build();
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAllSpecialFields_Field.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAllSpecialFields_Field.JAVA
index f9bea13..e315345 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAllSpecialFields_Field.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAllSpecialFields_Field.JAVA
@@ -21,7 +21,7 @@
.addProperty(new AppSearchSchema.PropertyConfig.Builder("price")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.build();
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAllSpecialFields_Getter.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAllSpecialFields_Getter.JAVA
index c30fa56..1cf9253 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAllSpecialFields_Getter.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testAllSpecialFields_Getter.JAVA
@@ -21,7 +21,7 @@
.addProperty(new AppSearchSchema.PropertyConfig.Builder("price")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.build();
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testCardinality.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testCardinality.JAVA
index 50f7553..13f4f18 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testCardinality.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testCardinality.JAVA
@@ -24,25 +24,25 @@
.addProperty(new AppSearchSchema.PropertyConfig.Builder("repeatReq")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("repeatNoReq")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("req")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_DOUBLE)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("noReq")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_DOUBLE)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.build();
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testIndexingType.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testIndexingType.JAVA
index 7855d50..87283d4 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testIndexingType.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testIndexingType.JAVA
@@ -21,7 +21,7 @@
.addProperty(new AppSearchSchema.PropertyConfig.Builder("indexNone")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("indexExact")
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testInnerClass.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testInnerClass.JAVA
index 8a91767..8900092 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testInnerClass.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testInnerClass.JAVA
@@ -21,7 +21,7 @@
.addProperty(new AppSearchSchema.PropertyConfig.Builder("arrString")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.build();
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testPropertyName.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testPropertyName.JAVA
index 45459ae..d843153 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testPropertyName.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testPropertyName.JAVA
@@ -21,7 +21,7 @@
.addProperty(new AppSearchSchema.PropertyConfig.Builder("newName")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.build();
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testRead_MultipleGetters.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testRead_MultipleGetters.JAVA
index 8a6e8f6b..890d43d 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testRead_MultipleGetters.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testRead_MultipleGetters.JAVA
@@ -21,7 +21,7 @@
.addProperty(new AppSearchSchema.PropertyConfig.Builder("price")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.build();
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testRepeatedFields.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testRepeatedFields.JAVA
index 9f0d759..be28a52 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testRepeatedFields.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testRepeatedFields.JAVA
@@ -26,25 +26,25 @@
.addProperty(new AppSearchSchema.PropertyConfig.Builder("listOfString")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("setOfInt")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("repeatedByteArray")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BYTES)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("byteArray")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BYTES)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.build();
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testSuccessSimple.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testSuccessSimple.JAVA
index e4d80c9..8499df6 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testSuccessSimple.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testSuccessSimple.JAVA
@@ -21,19 +21,19 @@
.addProperty(new AppSearchSchema.PropertyConfig.Builder("price")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("cat")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BOOLEAN)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("dog")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BOOLEAN)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.build();
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testToGenericDocument_AllSupportedTypes.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testToGenericDocument_AllSupportedTypes.JAVA
index 9692936..d3ee29b 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testToGenericDocument_AllSupportedTypes.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testToGenericDocument_AllSupportedTypes.JAVA
@@ -32,43 +32,43 @@
.addProperty(new AppSearchSchema.PropertyConfig.Builder("collectLong")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("collectInteger")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("collectDouble")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_DOUBLE)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("collectFloat")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_DOUBLE)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("collectBoolean")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BOOLEAN)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("collectByteArr")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BYTES)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("collectString")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("collectGift")
@@ -81,79 +81,79 @@
.addProperty(new AppSearchSchema.PropertyConfig.Builder("arrBoxLong")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("arrUnboxLong")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("arrBoxInteger")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("arrUnboxInt")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("arrBoxDouble")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_DOUBLE)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("arrUnboxDouble")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_DOUBLE)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("arrBoxFloat")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_DOUBLE)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("arrUnboxFloat")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_DOUBLE)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("arrBoxBoolean")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BOOLEAN)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("arrUnboxBoolean")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BOOLEAN)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("arrUnboxByteArr")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BYTES)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("boxByteArr")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BYTES)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("arrString")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("arrGift")
@@ -166,73 +166,73 @@
.addProperty(new AppSearchSchema.PropertyConfig.Builder("string")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("boxLong")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("unboxLong")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("boxInteger")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("unboxInt")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("boxDouble")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_DOUBLE)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("unboxDouble")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_DOUBLE)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("boxFloat")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_DOUBLE)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("unboxFloat")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_DOUBLE)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("boxBoolean")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BOOLEAN)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("unboxBoolean")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BOOLEAN)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("unboxByteArr")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BYTES)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.addProperty(new AppSearchSchema.PropertyConfig.Builder("gift")
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testTokenizerType.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testTokenizerType.JAVA
index 71ec562..9be6cdb 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testTokenizerType.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testTokenizerType.JAVA
@@ -27,7 +27,7 @@
.addProperty(new AppSearchSchema.PropertyConfig.Builder("tokPlain")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.build();
diff --git a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testWrite_MultipleSetters.JAVA b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testWrite_MultipleSetters.JAVA
index 8a6e8f6b..890d43d 100644
--- a/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testWrite_MultipleSetters.JAVA
+++ b/appsearch/compiler/src/test/resources/androidx/appsearch/compiler/goldens/testWrite_MultipleSetters.JAVA
@@ -21,7 +21,7 @@
.addProperty(new AppSearchSchema.PropertyConfig.Builder("price")
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
- .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
.setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
.build())
.build();
diff --git a/benchmark/benchmark/build.gradle b/benchmark/benchmark/build.gradle
index b0d1aa1..98a9387 100644
--- a/benchmark/benchmark/build.gradle
+++ b/benchmark/benchmark/build.gradle
@@ -22,8 +22,17 @@
id("androidx.benchmark")
}
+android {
+ defaultConfig {
+ // 18 needed for UI automator dependency, via benchmark-perfetto
+ minSdkVersion 18
+ }
+}
+
dependencies {
androidTestImplementation(project(":benchmark:benchmark-junit4"))
+ androidTestImplementation(project(":benchmark:benchmark-perfetto"))
+ androidTestImplementation(project(":tracing:tracing-ktx"))
androidTestImplementation(ANDROIDX_TEST_RUNNER)
androidTestImplementation(ANDROIDX_TEST_RULES)
androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
diff --git a/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/PerfettoOverheadBenchmark.kt b/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/PerfettoOverheadBenchmark.kt
new file mode 100644
index 0000000..f3f224c
--- /dev/null
+++ b/benchmark/benchmark/src/androidTest/java/androidx/benchmark/benchmark/PerfettoOverheadBenchmark.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.benchmark.benchmark
+
+import android.os.Trace
+import androidx.benchmark.junit4.BenchmarkRule
+import androidx.benchmark.junit4.measureRepeated
+import androidx.benchmark.perfetto.PerfettoRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import androidx.tracing.trace
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+class PerfettoOverheadBenchmark {
+ @get:Rule
+ val benchmarkRule = BenchmarkRule()
+
+ @get:Rule
+ val perfettoRule = PerfettoRule()
+
+ /**
+ * Empty baseline, no tracing. Expect similar results to [TrivialJavaBenchmark.nothing].
+ */
+ @Test
+ fun empty() = benchmarkRule.measureRepeated {}
+
+ /**
+ * The trace section within runWithTimingDisabled, even though not measured, can impact the
+ * results of a small benchmark significantly.
+ */
+ @Test
+ fun runWithTimingDisabled() = benchmarkRule.measureRepeated {
+ runWithTimingDisabled { /* nothing*/ }
+ }
+
+ /**
+ * Trace section adds ~5us (depending on many factors) in this ideal case, but will be
+ * significantly worse in a real benchmark, as there's more computation to interfere with.
+ */
+ @Test
+ fun traceBeginEnd() = benchmarkRule.measureRepeated {
+ Trace.beginSection("foo")
+ Trace.endSection()
+ }
+
+ /**
+ * Dupe of [traceBeginEnd], just using [trace].
+ */
+ @Test
+ fun traceBlock() = benchmarkRule.measureRepeated {
+ trace("foo") { /* nothing */ }
+ }
+}
diff --git a/benchmark/perfetto/src/main/java/androidx/benchmark/perfetto/PerfettoRule.kt b/benchmark/perfetto/src/main/java/androidx/benchmark/perfetto/PerfettoRule.kt
index eb69b8c..e562cb8 100644
--- a/benchmark/perfetto/src/main/java/androidx/benchmark/perfetto/PerfettoRule.kt
+++ b/benchmark/perfetto/src/main/java/androidx/benchmark/perfetto/PerfettoRule.kt
@@ -91,6 +91,7 @@
block()
val dst = destinationPath(traceName)
stop(dst.absolutePath)
+ Log.d(PerfettoRule.TAG, "Finished recording to ${dst.absolutePath}")
reportAdditionalFileToCopy("perfetto_trace", dst.absolutePath)
} finally {
cancel()
diff --git a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ImageTest.kt b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ImageTest.kt
index 833bb8e..06ce451 100644
--- a/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ImageTest.kt
+++ b/compose/foundation/foundation/src/androidAndroidTest/kotlin/androidx/compose/foundation/ImageTest.kt
@@ -35,6 +35,7 @@
import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.asAndroidBitmap
+import androidx.compose.ui.graphics.drawscope.CanvasDrawScope
import androidx.compose.ui.graphics.painter.ImagePainter
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.layout.ContentScale
@@ -47,7 +48,9 @@
import androidx.compose.ui.test.onRoot
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
+import androidx.compose.testutils.assertPixels
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import androidx.test.filters.SdkSuppress
@@ -293,6 +296,46 @@
}
@Test
+ fun testImageScalesNonuniformly() {
+ val imageComposableWidth = imageWidth * 3
+ val imageComposableHeight = imageHeight * 7
+
+ rule.setContent {
+ val density = DensityAmbient.current
+ val size = (containerSize * 2 / density.density).dp
+ val imageAsset = ImageAsset(imageWidth, imageHeight)
+ CanvasDrawScope().draw(
+ density,
+ LayoutDirection.Ltr,
+ Canvas(imageAsset),
+ Size(imageWidth.toFloat(), imageHeight.toFloat())
+ ) {
+ drawRect(color = Color.Blue)
+ }
+ Box(
+ Modifier.preferredSize(size)
+ .background(color = Color.White)
+ .wrapContentSize(Alignment.Center)
+ ) {
+ Image(
+ asset = imageAsset,
+ modifier = Modifier
+ .testTag(contentTag)
+ .preferredSize(
+ (imageComposableWidth / density.density).dp,
+ (imageComposableHeight / density.density).dp
+ ),
+ // Scale the image non-uniformly within the bounds of the composable
+ contentScale = ContentScale.FillBounds,
+ alignment = Alignment.BottomEnd
+ )
+ }
+ }
+
+ rule.onNodeWithTag(contentTag).captureToImage().assertPixels { Color.Blue }
+ }
+
+ @Test
fun testImageFixedSizeAlignedBottomEnd() {
val imageComposableWidth = imageWidth * 2
val imageComposableHeight = imageHeight * 2
diff --git a/compose/integration-tests/benchmark/build.gradle b/compose/integration-tests/benchmark/build.gradle
index f46802f..f9efbbfe2 100644
--- a/compose/integration-tests/benchmark/build.gradle
+++ b/compose/integration-tests/benchmark/build.gradle
@@ -33,6 +33,7 @@
kotlinPlugin project(":compose:compiler:compiler")
implementation project(":benchmark:benchmark-junit4")
+ implementation project(":benchmark:benchmark-perfetto")
implementation project(":compose:foundation:foundation-layout")
implementation project(":compose:integration-tests")
implementation project(":compose:runtime:runtime")
diff --git a/compose/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/VectorBenchmark.kt b/compose/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/VectorBenchmark.kt
index 64f3096..bfe8f59 100644
--- a/compose/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/VectorBenchmark.kt
+++ b/compose/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/VectorBenchmark.kt
@@ -35,7 +35,7 @@
*/
@LargeTest
@RunWith(AndroidJUnit4::class)
-class VectorBenchmark {
+open class VectorBenchmark {
@get:Rule
val benchmarkRule = ComposeBenchmarkRule()
diff --git a/compose/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/VectorBenchmarkWithTracing.kt b/compose/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/VectorBenchmarkWithTracing.kt
new file mode 100644
index 0000000..d4e1688
--- /dev/null
+++ b/compose/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/VectorBenchmarkWithTracing.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.benchmark.test
+
+import androidx.benchmark.perfetto.PerfettoRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import org.junit.Rule
+import org.junit.runner.RunWith
+
+/**
+ * Duplicate of [VectorBenchmark], but which adds tracing.
+ *
+ * Note: Per PerfettoRule, these benchmarks will be ignored < API 29
+ */
+@Suppress("ClassName")
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+class VectorBenchmarkWithTracing : VectorBenchmark() {
+ @get:Rule
+ val perfettoRule = PerfettoRule()
+}
diff --git a/compose/ui/ui/api/current.txt b/compose/ui/ui/api/current.txt
index 62005ed..63fd054 100644
--- a/compose/ui/ui/api/current.txt
+++ b/compose/ui/ui/api/current.txt
@@ -1591,18 +1591,21 @@
}
@androidx.compose.runtime.Stable public interface ContentScale {
- method public float scale-AhF4CD4(long srcSize, long dstSize);
+ method public long computeScaleFactor-AhF4CD4(long srcSize, long dstSize);
+ method @Deprecated public default float scale-AhF4CD4(long srcSize, long dstSize);
field public static final androidx.compose.ui.layout.ContentScale.Companion Companion;
}
public static final class ContentScale.Companion {
method public androidx.compose.ui.layout.ContentScale getCrop();
+ method public androidx.compose.ui.layout.ContentScale getFillBounds();
method public androidx.compose.ui.layout.ContentScale getFillHeight();
method public androidx.compose.ui.layout.ContentScale getFillWidth();
method public androidx.compose.ui.layout.ContentScale getFit();
method public androidx.compose.ui.layout.ContentScale getInside();
method public androidx.compose.ui.layout.FixedScale getNone();
property public final androidx.compose.ui.layout.ContentScale Crop;
+ property public final androidx.compose.ui.layout.ContentScale FillBounds;
property public final androidx.compose.ui.layout.ContentScale FillHeight;
property public final androidx.compose.ui.layout.ContentScale FillWidth;
property public final androidx.compose.ui.layout.ContentScale Fit;
@@ -1616,9 +1619,9 @@
@androidx.compose.runtime.Immutable public final class FixedScale implements androidx.compose.ui.layout.ContentScale {
ctor public FixedScale(float value);
method public float component1();
+ method public long computeScaleFactor-AhF4CD4(long srcSize, long dstSize);
method @androidx.compose.runtime.Immutable public androidx.compose.ui.layout.FixedScale copy(float value);
method public float getValue();
- method public float scale-AhF4CD4(long srcSize, long dstSize);
property public final float value;
}
@@ -1800,6 +1803,36 @@
method public void onRemeasurementAvailable(androidx.compose.ui.layout.Remeasurement remeasurement);
}
+ @androidx.compose.runtime.Immutable public final inline class ScaleFactor {
+ ctor public ScaleFactor();
+ method @androidx.compose.runtime.Stable public static inline operator float component1-impl(long $this);
+ method @androidx.compose.runtime.Stable public static inline operator float component2-impl(long $this);
+ method public static long constructor-impl(internal long packedValue);
+ method public static long copy-_hLwfpc(long $this, optional float scaleX, optional float scaleY);
+ method @androidx.compose.runtime.Stable public static operator long div-_hLwfpc(long $this, float operand);
+ method @androidx.compose.runtime.Immutable public static inline boolean equals-impl(long p, Object? p1);
+ method public static boolean equals-impl0(long p1, long p2);
+ method public static float getScaleX-impl(long $this);
+ method public static float getScaleY-impl(long $this);
+ method @androidx.compose.runtime.Immutable public static inline int hashCode-impl(long p);
+ method @androidx.compose.runtime.Stable public static operator long times-_hLwfpc(long $this, float operand);
+ method public static String toString-impl(long $this);
+ field public static final androidx.compose.ui.layout.ScaleFactor.Companion Companion;
+ }
+
+ public static final class ScaleFactor.Companion {
+ method public long getUnspecified-_hLwfpc();
+ property public final long Unspecified;
+ }
+
+ public final class ScaleFactorKt {
+ method @androidx.compose.runtime.Stable public static long ScaleFactor(float scaleX, float scaleY);
+ method @androidx.compose.runtime.Stable public static operator long div-ngKnWWw(long, long scaleFactor);
+ method @androidx.compose.runtime.Stable public static long lerp-bKVCie4(long start, long stop, float fraction);
+ method @androidx.compose.runtime.Stable public static operator long times-Sp6zcS4(long, long size);
+ method @androidx.compose.runtime.Stable public static operator long times-ngKnWWw(long, long scaleFactor);
+ }
+
public final class SubcomposeLayoutKt {
method @androidx.compose.runtime.Composable public static <T> void SubcomposeLayout(optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function2<? super androidx.compose.ui.layout.SubcomposeMeasureScope<T>,? super androidx.compose.ui.unit.Constraints,? extends androidx.compose.ui.layout.MeasureResult> measureBlock);
}
diff --git a/compose/ui/ui/api/public_plus_experimental_current.txt b/compose/ui/ui/api/public_plus_experimental_current.txt
index 62005ed..63fd054 100644
--- a/compose/ui/ui/api/public_plus_experimental_current.txt
+++ b/compose/ui/ui/api/public_plus_experimental_current.txt
@@ -1591,18 +1591,21 @@
}
@androidx.compose.runtime.Stable public interface ContentScale {
- method public float scale-AhF4CD4(long srcSize, long dstSize);
+ method public long computeScaleFactor-AhF4CD4(long srcSize, long dstSize);
+ method @Deprecated public default float scale-AhF4CD4(long srcSize, long dstSize);
field public static final androidx.compose.ui.layout.ContentScale.Companion Companion;
}
public static final class ContentScale.Companion {
method public androidx.compose.ui.layout.ContentScale getCrop();
+ method public androidx.compose.ui.layout.ContentScale getFillBounds();
method public androidx.compose.ui.layout.ContentScale getFillHeight();
method public androidx.compose.ui.layout.ContentScale getFillWidth();
method public androidx.compose.ui.layout.ContentScale getFit();
method public androidx.compose.ui.layout.ContentScale getInside();
method public androidx.compose.ui.layout.FixedScale getNone();
property public final androidx.compose.ui.layout.ContentScale Crop;
+ property public final androidx.compose.ui.layout.ContentScale FillBounds;
property public final androidx.compose.ui.layout.ContentScale FillHeight;
property public final androidx.compose.ui.layout.ContentScale FillWidth;
property public final androidx.compose.ui.layout.ContentScale Fit;
@@ -1616,9 +1619,9 @@
@androidx.compose.runtime.Immutable public final class FixedScale implements androidx.compose.ui.layout.ContentScale {
ctor public FixedScale(float value);
method public float component1();
+ method public long computeScaleFactor-AhF4CD4(long srcSize, long dstSize);
method @androidx.compose.runtime.Immutable public androidx.compose.ui.layout.FixedScale copy(float value);
method public float getValue();
- method public float scale-AhF4CD4(long srcSize, long dstSize);
property public final float value;
}
@@ -1800,6 +1803,36 @@
method public void onRemeasurementAvailable(androidx.compose.ui.layout.Remeasurement remeasurement);
}
+ @androidx.compose.runtime.Immutable public final inline class ScaleFactor {
+ ctor public ScaleFactor();
+ method @androidx.compose.runtime.Stable public static inline operator float component1-impl(long $this);
+ method @androidx.compose.runtime.Stable public static inline operator float component2-impl(long $this);
+ method public static long constructor-impl(internal long packedValue);
+ method public static long copy-_hLwfpc(long $this, optional float scaleX, optional float scaleY);
+ method @androidx.compose.runtime.Stable public static operator long div-_hLwfpc(long $this, float operand);
+ method @androidx.compose.runtime.Immutable public static inline boolean equals-impl(long p, Object? p1);
+ method public static boolean equals-impl0(long p1, long p2);
+ method public static float getScaleX-impl(long $this);
+ method public static float getScaleY-impl(long $this);
+ method @androidx.compose.runtime.Immutable public static inline int hashCode-impl(long p);
+ method @androidx.compose.runtime.Stable public static operator long times-_hLwfpc(long $this, float operand);
+ method public static String toString-impl(long $this);
+ field public static final androidx.compose.ui.layout.ScaleFactor.Companion Companion;
+ }
+
+ public static final class ScaleFactor.Companion {
+ method public long getUnspecified-_hLwfpc();
+ property public final long Unspecified;
+ }
+
+ public final class ScaleFactorKt {
+ method @androidx.compose.runtime.Stable public static long ScaleFactor(float scaleX, float scaleY);
+ method @androidx.compose.runtime.Stable public static operator long div-ngKnWWw(long, long scaleFactor);
+ method @androidx.compose.runtime.Stable public static long lerp-bKVCie4(long start, long stop, float fraction);
+ method @androidx.compose.runtime.Stable public static operator long times-Sp6zcS4(long, long size);
+ method @androidx.compose.runtime.Stable public static operator long times-ngKnWWw(long, long scaleFactor);
+ }
+
public final class SubcomposeLayoutKt {
method @androidx.compose.runtime.Composable public static <T> void SubcomposeLayout(optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function2<? super androidx.compose.ui.layout.SubcomposeMeasureScope<T>,? super androidx.compose.ui.unit.Constraints,? extends androidx.compose.ui.layout.MeasureResult> measureBlock);
}
diff --git a/compose/ui/ui/api/restricted_current.txt b/compose/ui/ui/api/restricted_current.txt
index eee4efa..a7df2e5 100644
--- a/compose/ui/ui/api/restricted_current.txt
+++ b/compose/ui/ui/api/restricted_current.txt
@@ -1591,18 +1591,21 @@
}
@androidx.compose.runtime.Stable public interface ContentScale {
- method public float scale-AhF4CD4(long srcSize, long dstSize);
+ method public long computeScaleFactor-AhF4CD4(long srcSize, long dstSize);
+ method @Deprecated public default float scale-AhF4CD4(long srcSize, long dstSize);
field public static final androidx.compose.ui.layout.ContentScale.Companion Companion;
}
public static final class ContentScale.Companion {
method public androidx.compose.ui.layout.ContentScale getCrop();
+ method public androidx.compose.ui.layout.ContentScale getFillBounds();
method public androidx.compose.ui.layout.ContentScale getFillHeight();
method public androidx.compose.ui.layout.ContentScale getFillWidth();
method public androidx.compose.ui.layout.ContentScale getFit();
method public androidx.compose.ui.layout.ContentScale getInside();
method public androidx.compose.ui.layout.FixedScale getNone();
property public final androidx.compose.ui.layout.ContentScale Crop;
+ property public final androidx.compose.ui.layout.ContentScale FillBounds;
property public final androidx.compose.ui.layout.ContentScale FillHeight;
property public final androidx.compose.ui.layout.ContentScale FillWidth;
property public final androidx.compose.ui.layout.ContentScale Fit;
@@ -1633,9 +1636,9 @@
@androidx.compose.runtime.Immutable public final class FixedScale implements androidx.compose.ui.layout.ContentScale {
ctor public FixedScale(float value);
method public float component1();
+ method public long computeScaleFactor-AhF4CD4(long srcSize, long dstSize);
method @androidx.compose.runtime.Immutable public androidx.compose.ui.layout.FixedScale copy(float value);
method public float getValue();
- method public float scale-AhF4CD4(long srcSize, long dstSize);
property public final float value;
}
@@ -1847,6 +1850,36 @@
method public void onRemeasurementAvailable(androidx.compose.ui.layout.Remeasurement remeasurement);
}
+ @androidx.compose.runtime.Immutable public final inline class ScaleFactor {
+ ctor public ScaleFactor();
+ method @androidx.compose.runtime.Stable public static inline operator float component1-impl(long $this);
+ method @androidx.compose.runtime.Stable public static inline operator float component2-impl(long $this);
+ method public static long constructor-impl(internal long packedValue);
+ method public static long copy-_hLwfpc(long $this, optional float scaleX, optional float scaleY);
+ method @androidx.compose.runtime.Stable public static operator long div-_hLwfpc(long $this, float operand);
+ method @androidx.compose.runtime.Immutable public static inline boolean equals-impl(long p, Object? p1);
+ method public static boolean equals-impl0(long p1, long p2);
+ method public static float getScaleX-impl(long $this);
+ method public static float getScaleY-impl(long $this);
+ method @androidx.compose.runtime.Immutable public static inline int hashCode-impl(long p);
+ method @androidx.compose.runtime.Stable public static operator long times-_hLwfpc(long $this, float operand);
+ method public static String toString-impl(long $this);
+ field public static final androidx.compose.ui.layout.ScaleFactor.Companion Companion;
+ }
+
+ public static final class ScaleFactor.Companion {
+ method public long getUnspecified-_hLwfpc();
+ property public final long Unspecified;
+ }
+
+ public final class ScaleFactorKt {
+ method @androidx.compose.runtime.Stable public static long ScaleFactor(float scaleX, float scaleY);
+ method @androidx.compose.runtime.Stable public static operator long div-ngKnWWw(long, long scaleFactor);
+ method @androidx.compose.runtime.Stable public static long lerp-bKVCie4(long start, long stop, float fraction);
+ method @androidx.compose.runtime.Stable public static operator long times-Sp6zcS4(long, long size);
+ method @androidx.compose.runtime.Stable public static operator long times-ngKnWWw(long, long scaleFactor);
+ }
+
public final class SubcomposeLayoutKt {
method @androidx.compose.runtime.Composable public static <T> void SubcomposeLayout(optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function2<? super androidx.compose.ui.layout.SubcomposeMeasureScope<T>,? super androidx.compose.ui.unit.Constraints,? extends androidx.compose.ui.layout.MeasureResult> measureBlock);
}
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/PainterModifierTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/PainterModifierTest.kt
index 6d4a922c..b5ef674 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/PainterModifierTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/draw/PainterModifierTest.kt
@@ -26,6 +26,7 @@
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Providers
+import androidx.compose.testutils.assertPixels
import androidx.compose.ui.AlignTopLeft
import androidx.compose.ui.Alignment
import androidx.compose.ui.AtLeastSize
@@ -44,6 +45,7 @@
import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.graphics.asAndroidBitmap
+import androidx.compose.ui.graphics.asImageAsset
import androidx.compose.ui.graphics.compositeOver
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.graphics.painter.ImagePainter
@@ -565,6 +567,35 @@
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
@Test
+ fun testImagePainterScalesNonUniformly() {
+ // The composable dimensions are larger than the ImageAsset. By not passing in
+ // a ContentScale parameter to the painter, the ImageAsset should be stretched
+ // non-uniformly to fully occupy the bounds of the composable
+ val boxWidth = 60
+ val boxHeight = 40
+ val srcImage = ImageAsset(10, 20)
+ val canvas = Canvas(srcImage)
+ val paint = Paint().apply { this.color = Color.Red }
+ canvas.drawRect(0f, 0f, 40f, 20f, paint)
+
+ val testTag = "testTag"
+
+ rule.setContent {
+ Box(
+ modifier = Modifier
+ .testTag(testTag)
+ .background(color = Color.Gray)
+ .width((boxWidth / DensityAmbient.current.density).dp)
+ .height((boxHeight / DensityAmbient.current.density).dp)
+ .paint(ImagePainter(srcImage), contentScale = ContentScale.FillBounds)
+ )
+ }
+
+ rule.obtainScreenshotBitmap(boxWidth, boxHeight).asImageAsset().assertPixels { Color.Red }
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+ @Test
fun testVectorPainterScalesContent() {
// VectorPainter should handle scaling its content vector up to fill the
// corresponding content bounds. Because the composable is twice the
diff --git a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/vector/VectorTest.kt b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/vector/VectorTest.kt
index 742f475..4d47e25 100644
--- a/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/vector/VectorTest.kt
+++ b/compose/ui/ui/src/androidAndroidTest/kotlin/androidx/compose/ui/graphics/vector/VectorTest.kt
@@ -27,6 +27,7 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
+import androidx.compose.testutils.assertPixels
import androidx.compose.ui.Alignment
import androidx.compose.ui.AtLeastSize
import androidx.compose.ui.Modifier
@@ -255,6 +256,40 @@
}
}
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+ @Test
+ fun testVectorScaleNonUniformly() {
+ val defaultWidth = 24.dp
+ val defaultHeight = 24.dp
+ val testTag = "testTag"
+ rule.setContent {
+ val vectorPainter = rememberVectorPainter(
+ defaultWidth = defaultWidth,
+ defaultHeight = defaultHeight
+ ) { viewportWidth, viewportHeight ->
+ Path(
+ fill = SolidColor(Color.Blue),
+ pathData = PathData {
+ lineTo(viewportWidth, 0f)
+ lineTo(viewportWidth, viewportHeight)
+ lineTo(0f, viewportHeight)
+ close()
+ }
+ )
+ }
+ Image(
+ painter = vectorPainter,
+ modifier = Modifier
+ .testTag(testTag)
+ .preferredSize(defaultWidth * 7, defaultHeight * 3)
+ .background(Color.Red),
+ contentScale = ContentScale.FillBounds
+ )
+ }
+
+ rule.onNodeWithTag(testTag).captureToImage().assertPixels { Color.Blue }
+ }
+
@Composable
private fun VectorTint(
size: Int = 200,
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/draw/PainterModifier.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/draw/PainterModifier.kt
index b609dd9..2993b7f 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/draw/PainterModifier.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/draw/PainterModifier.kt
@@ -27,6 +27,7 @@
import androidx.compose.ui.graphics.drawscope.translate
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.layout.times
import androidx.compose.ui.layout.IntrinsicMeasurable
import androidx.compose.ui.layout.IntrinsicMeasureScope
import androidx.compose.ui.layout.LayoutModifier
@@ -183,7 +184,7 @@
}
val srcSize = Size(srcWidth, srcHeight)
- srcSize * contentScale.scale(srcSize, dstSize)
+ srcSize * contentScale.computeScaleFactor(srcSize, dstSize)
}
}
@@ -246,7 +247,7 @@
}
val srcSize = Size(srcWidth, srcHeight)
- val scale = contentScale.scale(srcSize, size)
+ val scale = contentScale.computeScaleFactor(srcSize, size)
// Compute the offset to translate the content based on the given alignment
// and size to draw based on the ContentScale parameter
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/ContentScale.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/ContentScale.kt
index cbf29a5..7ecf32c3 100644
--- a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/ContentScale.kt
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/ContentScale.kt
@@ -22,8 +22,6 @@
import kotlin.math.max
import kotlin.math.min
-private const val OriginalScale = 1.0f
-
/**
* Represents a rule to apply to scale a source rectangle to be inscribed into a destination
*/
@@ -31,10 +29,26 @@
interface ContentScale {
/**
+ * Computes the scale factor to apply to the horizontal and vertical axes independently
+ * of one another to fit the source appropriately with the given destination
+ */
+ fun computeScaleFactor(srcSize: Size, dstSize: Size): ScaleFactor
+
+ /**
* Computes the scale factor to apply to both dimensions in order to fit the source
* appropriately with the given destination size
*/
- fun scale(srcSize: Size, dstSize: Size): Float
+ @Deprecated(
+ "use computeScaleFactor instead",
+ ReplaceWith(
+ "computeScaleFactor(srcSize, dstSize)",
+ "androidx.compose.ui.layout"
+ )
+ )
+ fun scale(srcSize: Size, dstSize: Size): Float =
+ // Returning scaleX here as previous implementations of ContentScale all provided
+ // uniform scale values which were identical for both scaleX and scaleY
+ computeScaleFactor(srcSize, dstSize).scaleX
/**
* Companion object containing commonly used [ContentScale] implementations
@@ -51,8 +65,10 @@
*/
@Stable
val Crop = object : ContentScale {
- override fun scale(srcSize: Size, dstSize: Size): Float =
- computeFillMaxDimension(srcSize, dstSize)
+ override fun computeScaleFactor(srcSize: Size, dstSize: Size): ScaleFactor =
+ computeFillMaxDimension(srcSize, dstSize).let {
+ ScaleFactor(it, it)
+ }
}
/**
@@ -65,8 +81,10 @@
*/
@Stable
val Fit = object : ContentScale {
- override fun scale(srcSize: Size, dstSize: Size): Float =
- computeFillMinDimension(srcSize, dstSize)
+ override fun computeScaleFactor(srcSize: Size, dstSize: Size): ScaleFactor =
+ computeFillMinDimension(srcSize, dstSize).let {
+ ScaleFactor(it, it)
+ }
}
/**
@@ -76,8 +94,10 @@
*/
@Stable
val FillHeight = object : ContentScale {
- override fun scale(srcSize: Size, dstSize: Size): Float =
- computeFillHeight(srcSize, dstSize)
+ override fun computeScaleFactor(srcSize: Size, dstSize: Size): ScaleFactor =
+ computeFillHeight(srcSize, dstSize).let {
+ ScaleFactor(it, it)
+ }
}
/**
@@ -87,8 +107,10 @@
*/
@Stable
val FillWidth = object : ContentScale {
- override fun scale(srcSize: Size, dstSize: Size): Float =
- computeFillWidth(srcSize, dstSize)
+ override fun computeScaleFactor(srcSize: Size, dstSize: Size): ScaleFactor =
+ computeFillWidth(srcSize, dstSize).let {
+ ScaleFactor(it, it)
+ }
}
/**
@@ -102,19 +124,37 @@
*/
@Stable
val Inside = object : ContentScale {
- override fun scale(srcSize: Size, dstSize: Size): Float =
- if (srcSize.width <= dstSize.width && srcSize.height <= dstSize.height) {
- OriginalScale
+
+ override fun computeScaleFactor(srcSize: Size, dstSize: Size): ScaleFactor {
+ return if (srcSize.width <= dstSize.width &&
+ srcSize.height <= dstSize.height
+ ) {
+ ScaleFactor(1.0f, 1.0f)
} else {
- computeFillMinDimension(srcSize, dstSize)
+ computeFillMinDimension(srcSize, dstSize).let {
+ ScaleFactor(it, it)
+ }
}
+ }
}
/**
* Do not apply any scaling to the source
*/
@Stable
- val None = FixedScale(OriginalScale)
+ val None = FixedScale(1.0f)
+
+ /**
+ * Scale horizontal and vertically non-uniformly to fill the destination bounds.
+ */
+ @Stable
+ val FillBounds = object : ContentScale {
+ override fun computeScaleFactor(srcSize: Size, dstSize: Size): ScaleFactor =
+ ScaleFactor(
+ computeFillWidth(srcSize, dstSize),
+ computeFillHeight(srcSize, dstSize)
+ )
+ }
}
}
@@ -124,7 +164,8 @@
*/
@Immutable
data class FixedScale(val value: Float) : ContentScale {
- override fun scale(srcSize: Size, dstSize: Size): Float = value
+ override fun computeScaleFactor(srcSize: Size, dstSize: Size): ScaleFactor =
+ ScaleFactor(value, value)
}
private fun computeFillMaxDimension(srcSize: Size, dstSize: Size): Float {
diff --git a/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/ScaleFactor.kt b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/ScaleFactor.kt
new file mode 100644
index 0000000..256ac27
--- /dev/null
+++ b/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/layout/ScaleFactor.kt
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.layout
+
+import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.Stable
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.util.packFloats
+import androidx.compose.ui.util.toStringAsFixed
+import androidx.compose.ui.util.unpackFloat1
+import androidx.compose.ui.util.unpackFloat2
+
+/**
+ * Constructs a [ScaleFactor] from the given x and y scale values
+ */
+@Stable
+fun ScaleFactor(scaleX: Float, scaleY: Float) = ScaleFactor(packFloats(scaleX, scaleY))
+
+/**
+ * Holds 2 dimensional scaling factors for horizontal and vertical axes
+ */
+@Suppress("EXPERIMENTAL_FEATURE_WARNING")
+@Immutable
+inline class ScaleFactor(@PublishedApi internal val packedValue: Long) {
+
+ /**
+ * Returns the scale factor to apply along the horizontal axis
+ */
+ @Stable
+ val scaleX: Float
+ get() {
+ // Explicitly compare against packed values to avoid
+ // auto-boxing of ScaleFactor.Unspecified
+ check(this.packedValue != ScaleFactor.Unspecified.packedValue) {
+ "ScaleFactor is unspecified"
+ }
+ return unpackFloat1(packedValue)
+ }
+
+ /**
+ * Returns the scale factor to apply along the vertical axis
+ */
+ @Stable
+ val scaleY: Float
+ get() {
+ // Explicitly compare against packed values to avoid
+ // auto-boxing of Size.Unspecified
+ check(this.packedValue != ScaleFactor.Unspecified.packedValue) {
+ "ScaleFactor is unspecified"
+ }
+ return unpackFloat2(packedValue)
+ }
+
+ @Suppress("NOTHING_TO_INLINE")
+ @Stable
+ inline operator fun component1(): Float = scaleX
+
+ @Suppress("NOTHING_TO_INLINE")
+ @Stable
+ inline operator fun component2(): Float = scaleY
+
+ /**
+ * Returns a copy of this ScaleFactor instance optionally overriding the
+ * scaleX or scaleY parameters
+ */
+ fun copy(scaleX: Float = this.scaleX, scaleY: Float = this.scaleY) = ScaleFactor(scaleX, scaleY)
+
+ /**
+ * Multiplication operator.
+ *
+ * Returns a [ScaleFactor] with scale x and y values multiplied by the operand
+ */
+ @Stable
+ operator fun times(operand: Float) = ScaleFactor(scaleX * operand, scaleY * operand)
+
+ /**
+ * Division operator.
+ *
+ * Returns a [ScaleFactor] with scale x and y values divided by the operand
+ */
+ @Stable
+ operator fun div(operand: Float) = ScaleFactor(scaleX / operand, scaleY / operand)
+
+ override fun toString() = "ScaleFactor(${scaleX.toStringAsFixed(1)}, " +
+ "${scaleY.toStringAsFixed(1)})"
+
+ companion object {
+
+ /**
+ * A ScaleFactor whose [scaleX] and [scaleY] parameters are unspecified. This is a sentinel
+ * value used to initialize a non-null parameter.
+ * Access to scaleX or scaleY on an unspecified size is not allowed
+ */
+ @Stable
+ val Unspecified = ScaleFactor(Float.NaN, Float.NaN)
+ }
+}
+
+/**
+ * Multiplication operator with [Size].
+ *
+ * Return a new [Size] with the width and height multiplied by the [ScaleFactor.scaleX] and
+ * [ScaleFactor.scaleY] respectively
+ */
+@Stable
+operator fun Size.times(scaleFactor: ScaleFactor): Size =
+ Size(this.width * scaleFactor.scaleX, this.height * scaleFactor.scaleY)
+
+/**
+ * Multiplication operator with [Size] with reverse parameter types to maintain
+ * commutative properties of multiplication
+ *
+ * Return a new [Size] with the width and height multiplied by the [ScaleFactor.scaleX] and
+ * [ScaleFactor.scaleY] respectively
+ */
+@Stable
+operator fun ScaleFactor.times(size: Size): Size = size * this
+
+/**
+ * Division operator with [Size]
+ *
+ * Return a new [Size] with the width and height divided by [ScaleFactor.scaleX] and
+ * [ScaleFactor.scaleY] respectively
+ */
+@Stable
+operator fun Size.div(scaleFactor: ScaleFactor): Size =
+ Size(width / scaleFactor.scaleX, height / scaleFactor.scaleY)
+
+/**
+ * Linearly interpolate between two [ScaleFactor] parameters
+ *
+ * The [fraction] argument represents position on the timeline, with 0.0 meaning
+ * that the interpolation has not started, returning [start] (or something
+ * equivalent to [start]), 1.0 meaning that the interpolation has finished,
+ * returning [stop] (or something equivalent to [stop]), and values in between
+ * meaning that the interpolation is at the relevant point on the timeline
+ * between [start] and [stop]. The interpolation can be extrapolated beyond 0.0 and
+ * 1.0, so negative values and values greater than 1.0 are valid (and can
+ * easily be generated by curves).
+ *
+ * Values for [fraction] are usually obtained from an [Animation<Float>], such as
+ * an `AnimationController`.
+ */
+@Stable
+fun lerp(start: ScaleFactor, stop: ScaleFactor, fraction: Float): ScaleFactor {
+ return ScaleFactor(
+ androidx.compose.ui.util.lerp(start.scaleX, stop.scaleX, fraction),
+ androidx.compose.ui.util.lerp(start.scaleY, stop.scaleY, fraction)
+ )
+}
\ No newline at end of file
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/layout/ContentScaleTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/layout/ContentScaleTest.kt
index 8d646fc..7134996 100644
--- a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/layout/ContentScaleTest.kt
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/layout/ContentScaleTest.kt
@@ -27,92 +27,112 @@
@Test
fun testScaleNone() {
- val scale = ContentScale.None.scale(
+ val scale = ContentScale.None.computeScaleFactor(
srcSize = Size(100.0f, 100.0f),
dstSize = Size(200.0f, 200.0f)
)
- assertEquals(1.0f, scale)
+ assertEquals(1.0f, scale.scaleX)
+ assertEquals(1.0f, scale.scaleY)
}
@Test
fun testContentScaleFit() {
- val scale = ContentScale.Fit.scale(
+ val scale = ContentScale.Fit.computeScaleFactor(
srcSize = Size(200.0f, 100.0f),
dstSize = Size(100.0f, 200.0f)
)
- assertEquals(.5f, scale)
+ assertEquals(.5f, scale.scaleX)
+ assertEquals(.5f, scale.scaleY)
}
@Test
fun testContentScaleFillWidth() {
- val scale = ContentScale.FillWidth.scale(
+ val scale = ContentScale.FillWidth.computeScaleFactor(
srcSize = Size(400.0f, 100.0f),
dstSize = Size(100.0f, 200.0f)
)
- assertEquals(0.25f, scale)
+ assertEquals(0.25f, scale.scaleX)
+ assertEquals(0.25f, scale.scaleY)
}
@Test
fun testScaleFillHeight() {
- val scale = ContentScale.FillHeight.scale(
+ val scale = ContentScale.FillHeight.computeScaleFactor(
srcSize = Size(400.0f, 100.0f),
dstSize = Size(100.0f, 200.0f)
)
- assertEquals(2.0f, scale)
+ assertEquals(2.0f, scale.scaleX)
+ assertEquals(2.0f, scale.scaleY)
}
@Test
fun testContentScaleCrop() {
- val scale = ContentScale.Crop.scale(
+ val scale = ContentScale.Crop.computeScaleFactor(
srcSize = Size(400.0f, 100.0f),
dstSize = Size(100.0f, 200.0f)
)
- assertEquals(2.0f, scale)
+ assertEquals(2.0f, scale.scaleX)
+ assertEquals(2.0f, scale.scaleY)
}
@Test
fun testContentScaleInside() {
- val scale = ContentScale.Inside.scale(
+ val scale = ContentScale.Inside.computeScaleFactor(
srcSize = Size(400.0f, 100.0f),
dstSize = Size(100.0f, 200.0f)
)
- assertEquals(0.25f, scale)
+ assertEquals(0.25f, scale.scaleX)
+ assertEquals(0.25f, scale.scaleY)
}
@Test
fun testContentScaleInsideLargeDst() {
// If the src is smaller than the destination, ensure no scaling is done
- val scale = ContentScale.Inside.scale(
+ val scale = ContentScale.Inside.computeScaleFactor(
srcSize = Size(400.0f, 100.0f),
dstSize = Size(900.0f, 200.0f)
)
- assertEquals(1.0f, scale)
+ assertEquals(1.0f, scale.scaleX)
+ assertEquals(1.0f, scale.scaleY)
}
@Test
fun testContentFitInsideLargeDst() {
- val scale = ContentScale.Fit.scale(
+ val scale = ContentScale.Fit.computeScaleFactor(
srcSize = Size(400.0f, 100.0f),
dstSize = Size(900.0f, 200.0f)
)
- assertEquals(2.0f, scale)
+ assertEquals(2.0f, scale.scaleX)
+ assertEquals(2.0f, scale.scaleY)
}
@Test
fun testContentScaleCropWidth() {
- val scale = ContentScale.Crop.scale(
+ val scale = ContentScale.Crop.computeScaleFactor(
srcSize = Size(100.0f, 400.0f),
dstSize = Size(200.0f, 200.0f)
)
- assertEquals(2.00f, scale)
+ assertEquals(2.00f, scale.scaleX)
+ assertEquals(2.00f, scale.scaleY)
}
@Test
fun testContentScaleCropHeight() {
- val scale = ContentScale.Crop.scale(
+ val scale = ContentScale.Crop.computeScaleFactor(
srcSize = Size(300.0f, 100.0f),
dstSize = Size(200.0f, 200.0f)
)
- assertEquals(2.00f, scale)
+ assertEquals(2.00f, scale.scaleX)
+ assertEquals(2.00f, scale.scaleY)
+ }
+
+ @Test
+ fun testContentScaleFillBoundsUp() {
+ val scale = ContentScale.FillBounds.computeScaleFactor(
+ srcSize = Size(100f, 100f),
+ dstSize = Size(300f, 700f)
+ )
+ assertEquals(3.0f, scale.scaleX)
+ assertEquals(7.0f, scale.scaleY)
}
}
\ No newline at end of file
diff --git a/compose/ui/ui/src/test/kotlin/androidx/compose/ui/layout/ScaleFactorTest.kt b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/layout/ScaleFactorTest.kt
new file mode 100644
index 0000000..f2b8c87
--- /dev/null
+++ b/compose/ui/ui/src/test/kotlin/androidx/compose/ui/layout/ScaleFactorTest.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.compose.ui.layout
+
+import androidx.compose.ui.geometry.Size
+import org.junit.Assert.assertEquals
+import org.junit.Assert.fail
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class ScaleFactorTest {
+
+ @Test
+ fun testScaleFactorConstructor() {
+ val scaleFactor = ScaleFactor(2f, 3f)
+ assertEquals(2f, scaleFactor.scaleX)
+ assertEquals(3f, scaleFactor.scaleY)
+ }
+
+ @Test
+ fun testDestructuring() {
+ val (scaleX, scaleY) = ScaleFactor(7f, 12f)
+ assertEquals(7f, scaleX)
+ assertEquals(12f, scaleY)
+ }
+
+ @Test
+ fun testCopy() {
+ val scaleFactor = ScaleFactor(11f, 4f)
+ assertEquals(scaleFactor, scaleFactor.copy())
+ }
+
+ @Test
+ fun testCopyOverwriteScaleX() {
+ val scaleFactor = ScaleFactor(7f, 2f)
+ assertEquals(ScaleFactor(3f, 2f), scaleFactor.copy(scaleX = 3f))
+ }
+
+ @Test
+ fun testCopyOverwriteScaleY() {
+ val scaleFactor = ScaleFactor(2f, 9f)
+ assertEquals(ScaleFactor(2f, 27f), scaleFactor.copy(scaleY = 27f))
+ }
+
+ @Test
+ fun testScaleFactorMultiplication() {
+ assertEquals(ScaleFactor(2f, 8f), ScaleFactor(1f, 4f) * 2f)
+ }
+
+ @Test
+ fun testScaleFactorDivision() {
+ assertEquals(ScaleFactor(1f, 4f), ScaleFactor(2f, 8f) / 2f)
+ }
+
+ @Test
+ fun testUnspecifiedScaleXQueryThrows() {
+ try {
+ ScaleFactor.Unspecified.scaleX
+ fail("Attempt to access ScaleFactor.Unspecified.scaleX is not allowed")
+ } catch (e: Throwable) {
+ // no-op
+ }
+ }
+
+ @Test
+ fun testUnspecifiedScaleYQueryThrows() {
+ try {
+ ScaleFactor.Unspecified.scaleY
+ fail("Attempt to access ScaleFactor.Unspecified.scaleY is not allowed")
+ } catch (e: Throwable) {
+ // no-op
+ }
+ }
+
+ @Test
+ fun testSizeMultiplication() {
+ val scaleFactor = ScaleFactor(2f, 3f)
+ val size = Size(100f, 200f)
+ val expected = Size(200f, 600f)
+ // verify commutative property of multiplication
+ assertEquals(expected, size * scaleFactor)
+ assertEquals(expected, scaleFactor * size)
+ }
+
+ @Test
+ fun testScaleFactorLerp() {
+ val scaleFactor1 = ScaleFactor(1f, 10f)
+ val scaleFactor2 = ScaleFactor(3f, 20f)
+ assertEquals(ScaleFactor(2f, 15f), lerp(scaleFactor1, scaleFactor2, 0.5f))
+ }
+
+ @Test
+ fun testSizeDivision() {
+ assertEquals(Size(1f, 2f), Size(100f, 300f) / ScaleFactor(100f, 150f))
+ }
+}
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
index 5fae244..fae9aa0 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -148,9 +148,9 @@
includeProject(":benchmark:benchmark-gradle-plugin", "benchmark/gradle-plugin", [BuildType.MAIN])
includeProject(":benchmark:benchmark-junit4", "benchmark/junit4")
includeProject(":benchmark:benchmark-macro", "benchmark/macro", [BuildType.MAIN])
-includeProject(":benchmark:benchmark-macro-runtime", "benchmark/benchmark-macro-runtime", [BuildType.MAIN])
+includeProject(":benchmark:benchmark-macro-runtime", "benchmark/benchmark-macro-runtime", [BuildType.MAIN, BuildType.COMPOSE])
+includeProject(":benchmark:benchmark-perfetto", "benchmark/perfetto", [BuildType.MAIN, BuildType.COMPOSE])
includeProject(":benchmark:benchmark-simple-macro-benchmark", "benchmark/benchmark-simple-macro-benchmark", [BuildType.MAIN])
-includeProject(":benchmark:benchmark-perfetto", "benchmark/perfetto", [BuildType.MAIN])
includeProject(":benchmark:integration-tests:dry-run-benchmark", "benchmark/integration-tests/dry-run-benchmark", [BuildType.MAIN])
includeProject(":benchmark:integration-tests:startup-benchmark", "benchmark/integration-tests/startup-benchmark", [BuildType.MAIN])
includeProject(":benchmark:integration-tests:benchmark-simple-macro-benchmark-target", "benchmark/integration-tests/benchmark-simple-macro-benchmark-target", [BuildType.MAIN])
diff --git a/window/window/api/current.txt b/window/window/api/current.txt
index 76987d6..2741333 100644
--- a/window/window/api/current.txt
+++ b/window/window/api/current.txt
@@ -50,11 +50,18 @@
public final class WindowManager {
ctor public WindowManager(android.content.Context);
ctor public WindowManager(android.content.Context, androidx.window.WindowBackend);
+ method public androidx.window.WindowMetrics getCurrentWindowMetrics();
+ method public androidx.window.WindowMetrics getMaximumWindowMetrics();
method public void registerDeviceStateChangeCallback(java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.window.DeviceState!>);
method public void registerLayoutChangeCallback(java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.window.WindowLayoutInfo!>);
method public void unregisterDeviceStateChangeCallback(androidx.core.util.Consumer<androidx.window.DeviceState!>);
method public void unregisterLayoutChangeCallback(androidx.core.util.Consumer<androidx.window.WindowLayoutInfo!>);
}
+ public final class WindowMetrics {
+ ctor public WindowMetrics(android.graphics.Rect);
+ method public android.graphics.Rect getBounds();
+ }
+
}
diff --git a/window/window/api/public_plus_experimental_current.txt b/window/window/api/public_plus_experimental_current.txt
index 76987d6..2741333 100644
--- a/window/window/api/public_plus_experimental_current.txt
+++ b/window/window/api/public_plus_experimental_current.txt
@@ -50,11 +50,18 @@
public final class WindowManager {
ctor public WindowManager(android.content.Context);
ctor public WindowManager(android.content.Context, androidx.window.WindowBackend);
+ method public androidx.window.WindowMetrics getCurrentWindowMetrics();
+ method public androidx.window.WindowMetrics getMaximumWindowMetrics();
method public void registerDeviceStateChangeCallback(java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.window.DeviceState!>);
method public void registerLayoutChangeCallback(java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.window.WindowLayoutInfo!>);
method public void unregisterDeviceStateChangeCallback(androidx.core.util.Consumer<androidx.window.DeviceState!>);
method public void unregisterLayoutChangeCallback(androidx.core.util.Consumer<androidx.window.WindowLayoutInfo!>);
}
+ public final class WindowMetrics {
+ ctor public WindowMetrics(android.graphics.Rect);
+ method public android.graphics.Rect getBounds();
+ }
+
}
diff --git a/window/window/api/restricted_current.txt b/window/window/api/restricted_current.txt
index 76987d6..2741333 100644
--- a/window/window/api/restricted_current.txt
+++ b/window/window/api/restricted_current.txt
@@ -50,11 +50,18 @@
public final class WindowManager {
ctor public WindowManager(android.content.Context);
ctor public WindowManager(android.content.Context, androidx.window.WindowBackend);
+ method public androidx.window.WindowMetrics getCurrentWindowMetrics();
+ method public androidx.window.WindowMetrics getMaximumWindowMetrics();
method public void registerDeviceStateChangeCallback(java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.window.DeviceState!>);
method public void registerLayoutChangeCallback(java.util.concurrent.Executor, androidx.core.util.Consumer<androidx.window.WindowLayoutInfo!>);
method public void unregisterDeviceStateChangeCallback(androidx.core.util.Consumer<androidx.window.DeviceState!>);
method public void unregisterLayoutChangeCallback(androidx.core.util.Consumer<androidx.window.WindowLayoutInfo!>);
}
+ public final class WindowMetrics {
+ ctor public WindowMetrics(android.graphics.Rect);
+ method public android.graphics.Rect getBounds();
+ }
+
}
diff --git a/window/window/src/androidTest/java/androidx/window/TestWindowBoundsHelper.java b/window/window/src/androidTest/java/androidx/window/TestWindowBoundsHelper.java
index 90d3156..15a069e 100644
--- a/window/window/src/androidTest/java/androidx/window/TestWindowBoundsHelper.java
+++ b/window/window/src/androidTest/java/androidx/window/TestWindowBoundsHelper.java
@@ -33,6 +33,7 @@
class TestWindowBoundsHelper extends WindowBoundsHelper {
private Rect mGlobalOverriddenBounds;
private final HashMap<Activity, Rect> mOverriddenBounds = new HashMap<>();
+ private final HashMap<Activity, Rect> mOverriddenMaximumBounds = new HashMap<>();
/**
* Overrides the bounds returned from this helper for the given context. Passing null {@code
@@ -46,6 +47,14 @@
}
/**
+ * Overrides the max bounds returned from this helper for the given context. Passing {@code
+ * null} {@code bounds} has the effect of clearing the bounds override.
+ */
+ void setMaximumBoundsForActivity(@NonNull Activity activity, @Nullable Rect bounds) {
+ mOverriddenMaximumBounds.put(activity, bounds);
+ }
+
+ /**
* Overrides the bounds returned from this helper for all supplied contexts. Passing null
* {@code bounds} has the effect of clearing the global override.
*/
@@ -68,6 +77,17 @@
return super.computeCurrentWindowBounds(activity);
}
+ @NonNull
+ @Override
+ Rect computeMaximumWindowBounds(Activity activity) {
+ Rect bounds = mOverriddenMaximumBounds.get(activity);
+ if (bounds != null) {
+ return bounds;
+ }
+
+ return super.computeMaximumWindowBounds(activity);
+ }
+
/**
* Clears any overrides set with {@link #setCurrentBounds(Rect)} or
* {@link #setCurrentBoundsForActivity(Activity, Rect)}.
@@ -75,5 +95,6 @@
void reset() {
mGlobalOverriddenBounds = null;
mOverriddenBounds.clear();
+ mOverriddenMaximumBounds.clear();
}
}
diff --git a/window/window/src/androidTest/java/androidx/window/WindowBoundsHelperTest.java b/window/window/src/androidTest/java/androidx/window/WindowBoundsHelperTest.java
index 93cfa19..cf6eab3 100644
--- a/window/window/src/androidTest/java/androidx/window/WindowBoundsHelperTest.java
+++ b/window/window/src/androidTest/java/androidx/window/WindowBoundsHelperTest.java
@@ -18,6 +18,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
import android.app.Activity;
import android.graphics.Point;
@@ -45,7 +46,9 @@
new ActivityScenarioRule<>(TestActivity.class);
@Test
- public void testGetCurrentWindowBounds_matchParentWindowSize_avoidCutouts() {
+ public void testGetCurrentWindowBounds_matchParentWindowSize_avoidCutouts_preR() {
+ assumePlatformBeforeR();
+
testGetCurrentWindowBoundsMatchesRealDisplaySize(activity -> {
assumeFalse(isInMultiWindowMode(activity));
@@ -61,7 +64,9 @@
}
@Test
- public void testGetCurrentWindowBounds_fixedWindowSize_avoidCutouts() {
+ public void testGetCurrentWindowBounds_fixedWindowSize_avoidCutouts_preR() {
+ assumePlatformBeforeR();
+
testGetCurrentWindowBoundsMatchesRealDisplaySize(activity -> {
assumeFalse(isInMultiWindowMode(activity));
@@ -77,7 +82,9 @@
}
@Test
- public void testGetCurrentWindowBounds_matchParentWindowSize_layoutBehindCutouts() {
+ public void testGetCurrentWindowBounds_matchParentWindowSize_layoutBehindCutouts_preR() {
+ assumePlatformBeforeR();
+
testGetCurrentWindowBoundsMatchesRealDisplaySize(activity -> {
assumeFalse(isInMultiWindowMode(activity));
@@ -93,7 +100,9 @@
}
@Test
- public void testGetCurrentWindowBounds_fixedWindowSize_layoutBehindCutouts() {
+ public void testGetCurrentWindowBounds_fixedWindowSize_layoutBehindCutouts_preR() {
+ assumePlatformBeforeR();
+
testGetCurrentWindowBoundsMatchesRealDisplaySize(activity -> {
assumeFalse(isInMultiWindowMode(activity));
@@ -108,22 +117,137 @@
});
}
+ @Test
+ public void testGetCurrentWindowBounds_postR() {
+ assumePlatformROrAbove();
+
+ runActionsAcrossActivityLifecycle(activity -> { }, activity -> {
+ Rect bounds = WindowBoundsHelper.getInstance().computeCurrentWindowBounds(activity);
+ Rect windowMetricsBounds =
+ activity.getWindowManager().getCurrentWindowMetrics().getBounds();
+ assertEquals(windowMetricsBounds, bounds);
+ });
+ }
+
+ @Test
+ public void testGetMaximumWindowBounds_matchParentWindowSize_avoidCutouts_preR() {
+ assumePlatformBeforeR();
+
+ testGetMaximumWindowBoundsMatchesRealDisplaySize(activity -> {
+ assumeFalse(isInMultiWindowMode(activity));
+
+ WindowManager.LayoutParams lp = activity.getWindow().getAttributes();
+ lp.width = WindowManager.LayoutParams.MATCH_PARENT;
+ lp.height = WindowManager.LayoutParams.MATCH_PARENT;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ lp.layoutInDisplayCutoutMode =
+ WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
+ }
+ activity.getWindow().setAttributes(lp);
+ });
+ }
+
+ @Test
+ public void testGetMaximumWindowBounds_fixedWindowSize_avoidCutouts_preR() {
+ assumePlatformBeforeR();
+
+ testGetMaximumWindowBoundsMatchesRealDisplaySize(activity -> {
+ assumeFalse(isInMultiWindowMode(activity));
+
+ WindowManager.LayoutParams lp = activity.getWindow().getAttributes();
+ lp.width = 100;
+ lp.height = 100;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ lp.layoutInDisplayCutoutMode =
+ WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
+ }
+ activity.getWindow().setAttributes(lp);
+ });
+ }
+
+ @Test
+ public void testGetMaximumWindowBounds_matchParentWindowSize_layoutBehindCutouts_preR() {
+ assumePlatformBeforeR();
+
+ testGetMaximumWindowBoundsMatchesRealDisplaySize(activity -> {
+ assumeFalse(isInMultiWindowMode(activity));
+
+ WindowManager.LayoutParams lp = activity.getWindow().getAttributes();
+ lp.width = WindowManager.LayoutParams.MATCH_PARENT;
+ lp.height = WindowManager.LayoutParams.MATCH_PARENT;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ lp.layoutInDisplayCutoutMode =
+ WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ }
+ activity.getWindow().setAttributes(lp);
+ });
+ }
+
+ @Test
+ public void testGetMaximumWindowBounds_fixedWindowSize_layoutBehindCutouts_preR() {
+ assumePlatformBeforeR();
+
+ testGetMaximumWindowBoundsMatchesRealDisplaySize(activity -> {
+ assumeFalse(isInMultiWindowMode(activity));
+
+ WindowManager.LayoutParams lp = activity.getWindow().getAttributes();
+ lp.width = 100;
+ lp.height = 100;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ lp.layoutInDisplayCutoutMode =
+ WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ }
+ activity.getWindow().setAttributes(lp);
+ });
+ }
+
+ @Test
+ public void testGetMaximumWindowBounds_postR() {
+ assumePlatformROrAbove();
+
+ runActionsAcrossActivityLifecycle(activity -> { }, activity -> {
+ Rect bounds = WindowBoundsHelper.getInstance().computeMaximumWindowBounds(activity);
+ Rect windowMetricsBounds =
+ activity.getWindowManager().getMaximumWindowMetrics().getBounds();
+ assertEquals(windowMetricsBounds, bounds);
+ });
+ }
+
private void testGetCurrentWindowBoundsMatchesRealDisplaySize(
ActivityScenario.ActivityAction<TestActivity> initialAction) {
+ ActivityScenario.ActivityAction<TestActivity> assertWindowBoundsMatchesDisplayAction =
+ new AssertCurrentWindowBoundsEqualsRealDisplaySizeAction();
+ runActionsAcrossActivityLifecycle(initialAction, assertWindowBoundsMatchesDisplayAction);
+ }
+
+ private void testGetMaximumWindowBoundsMatchesRealDisplaySize(
+ ActivityScenario.ActivityAction<TestActivity> initialAction) {
+ ActivityScenario.ActivityAction<TestActivity> assertWindowBoundsMatchesDisplayAction =
+ new AssertMaximumWindowBoundsEqualsRealDisplaySizeAction();
+ runActionsAcrossActivityLifecycle(initialAction, assertWindowBoundsMatchesDisplayAction);
+ }
+
+ /**
+ * Creates and launches an activity performing the supplied actions at various points in the
+ * activity lifecycle.
+ *
+ * @param initialAction the action that will run once before the activity is created.
+ * @param verifyAction the action to run once after each change in activity lifecycle state.
+ */
+ private void runActionsAcrossActivityLifecycle(
+ ActivityScenario.ActivityAction<TestActivity> initialAction,
+ ActivityScenario.ActivityAction<TestActivity> verifyAction) {
ActivityScenario<TestActivity> scenario = mActivityScenarioRule.getScenario();
scenario.onActivity(initialAction);
- ActivityScenario.ActivityAction<TestActivity> assertWindowBoundsAction =
- new AssertWindowBoundsEqualsRealDisplaySizeAction();
-
scenario.moveToState(Lifecycle.State.CREATED);
- scenario.onActivity(assertWindowBoundsAction);
+ scenario.onActivity(verifyAction);
scenario.moveToState(Lifecycle.State.STARTED);
- scenario.onActivity(assertWindowBoundsAction);
+ scenario.onActivity(verifyAction);
scenario.moveToState(Lifecycle.State.RESUMED);
- scenario.onActivity(assertWindowBoundsAction);
+ scenario.onActivity(verifyAction);
}
private static boolean isInMultiWindowMode(Activity activity) {
@@ -133,7 +257,15 @@
return false;
}
- private static final class AssertWindowBoundsEqualsRealDisplaySizeAction implements
+ private static void assumePlatformBeforeR() {
+ assumeTrue(Build.VERSION.SDK_INT < Build.VERSION_CODES.R);
+ }
+
+ private static void assumePlatformROrAbove() {
+ assumeTrue(Build.VERSION.SDK_INT >= Build.VERSION_CODES.R);
+ }
+
+ private static final class AssertCurrentWindowBoundsEqualsRealDisplaySizeAction implements
ActivityScenario.ActivityAction<TestActivity> {
@Override
public void perform(TestActivity activity) {
@@ -153,4 +285,25 @@
realDisplaySize.y, bounds.height());
}
}
+
+ private static final class AssertMaximumWindowBoundsEqualsRealDisplaySizeAction implements
+ ActivityScenario.ActivityAction<TestActivity> {
+ @Override
+ public void perform(TestActivity activity) {
+ Display display;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ display = activity.getDisplay();
+ } else {
+ display = activity.getWindowManager().getDefaultDisplay();
+ }
+
+ Point realDisplaySize = WindowBoundsHelper.getRealSizeForDisplay(display);
+
+ Rect bounds = WindowBoundsHelper.getInstance().computeMaximumWindowBounds(activity);
+ assertEquals("Window bounds width does not match real display width",
+ realDisplaySize.x, bounds.width());
+ assertEquals("Window bounds height does not match real display height",
+ realDisplaySize.y, bounds.height());
+ }
+ }
}
diff --git a/window/window/src/androidTest/java/androidx/window/WindowManagerTest.java b/window/window/src/androidTest/java/androidx/window/WindowManagerTest.java
index 957e9cb..dddc9ef 100644
--- a/window/window/src/androidTest/java/androidx/window/WindowManagerTest.java
+++ b/window/window/src/androidTest/java/androidx/window/WindowManagerTest.java
@@ -16,12 +16,15 @@
package androidx.window;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import android.app.Activity;
import android.content.ContextWrapper;
+import android.graphics.Rect;
import androidx.core.util.Consumer;
import androidx.test.core.app.ApplicationProvider;
@@ -30,6 +33,7 @@
import com.google.common.util.concurrent.MoreExecutors;
+import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -40,6 +44,11 @@
@RunWith(AndroidJUnit4.class)
public final class WindowManagerTest extends WindowTestBase {
+ @After
+ public void tearDown() {
+ WindowBoundsHelper.setForTesting(null);
+ }
+
@Test
public void testConstructor_activity() {
new WindowManager(mock(Activity.class), mock(WindowBackend.class));
@@ -89,4 +98,36 @@
wm.unregisterDeviceStateChangeCallback(consumer);
verify(backend).unregisterDeviceStateChangeCallback(eq(consumer));
}
+
+ @Test
+ public void testGetCurrentWindowMetrics() {
+ WindowBackend backend = mock(WindowBackend.class);
+ Activity activity = mock(Activity.class);
+ WindowManager wm = new WindowManager(activity, backend);
+
+ Rect bounds = new Rect(1, 2, 3, 4);
+ TestWindowBoundsHelper mWindowBoundsHelper = new TestWindowBoundsHelper();
+ mWindowBoundsHelper.setCurrentBoundsForActivity(activity, bounds);
+ WindowBoundsHelper.setForTesting(mWindowBoundsHelper);
+
+ WindowMetrics windowMetrics = wm.getCurrentWindowMetrics();
+ assertNotNull(windowMetrics);
+ assertEquals(bounds, windowMetrics.getBounds());
+ }
+
+ @Test
+ public void testGetMaximumWindowMetrics() {
+ WindowBackend backend = mock(WindowBackend.class);
+ Activity activity = mock(Activity.class);
+ WindowManager wm = new WindowManager(activity, backend);
+
+ Rect bounds = new Rect(0, 2, 4, 5);
+ TestWindowBoundsHelper mWindowBoundsHelper = new TestWindowBoundsHelper();
+ mWindowBoundsHelper.setMaximumBoundsForActivity(activity, bounds);
+ WindowBoundsHelper.setForTesting(mWindowBoundsHelper);
+
+ WindowMetrics windowMetrics = wm.getMaximumWindowMetrics();
+ assertNotNull(windowMetrics);
+ assertEquals(bounds, windowMetrics.getBounds());
+ }
}
diff --git a/window/window/src/androidTest/java/androidx/window/WindowMetricsTest.java b/window/window/src/androidTest/java/androidx/window/WindowMetricsTest.java
new file mode 100644
index 0000000..126e31e
--- /dev/null
+++ b/window/window/src/androidTest/java/androidx/window/WindowMetricsTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.graphics.Rect;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Tests for {@link WindowMetrics} class. */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class WindowMetricsTest {
+
+ @Test
+ public void testGetBounds() {
+ Rect bounds = new Rect(1, 2, 3, 4);
+ WindowMetrics windowMetrics = new WindowMetrics(bounds);
+ assertEquals(bounds, windowMetrics.getBounds());
+ }
+
+ @Test
+ public void testEquals_sameBounds() {
+ Rect bounds = new Rect(1, 2, 3, 4);
+ WindowMetrics windowMetrics0 = new WindowMetrics(bounds);
+ WindowMetrics windowMetrics1 = new WindowMetrics(bounds);
+
+ assertEquals(windowMetrics0, windowMetrics1);
+ }
+
+ @Test
+ public void testEquals_differentBounds() {
+ Rect bounds0 = new Rect(1, 2, 3, 4);
+ WindowMetrics windowMetrics0 = new WindowMetrics(bounds0);
+
+ Rect bounds1 = new Rect(6, 7, 8, 9);
+ WindowMetrics windowMetrics1 = new WindowMetrics(bounds1);
+
+ assertNotEquals(windowMetrics0, windowMetrics1);
+ }
+
+ @Test
+ public void testHashCode_matchesIfEqual() {
+ Rect bounds = new Rect(1, 2, 3, 4);
+ WindowMetrics windowMetrics0 = new WindowMetrics(bounds);
+ WindowMetrics windowMetrics1 = new WindowMetrics(bounds);
+
+ assertEquals(windowMetrics0.hashCode(), windowMetrics1.hashCode());
+ }
+}
diff --git a/window/window/src/main/java/androidx/window/WindowBoundsHelper.java b/window/window/src/main/java/androidx/window/WindowBoundsHelper.java
index 882f30c..d2db428 100644
--- a/window/window/src/main/java/androidx/window/WindowBoundsHelper.java
+++ b/window/window/src/main/java/androidx/window/WindowBoundsHelper.java
@@ -104,14 +104,13 @@
* <p>
* Note: The value of this is based on the last windowing state reported to the client.
*
+ * @see android.view.WindowManager#getCurrentWindowMetrics()
* @see android.view.WindowMetrics#getBounds()
*/
@NonNull
Rect computeCurrentWindowBounds(Activity activity) {
if (Build.VERSION.SDK_INT >= R) {
- android.view.WindowManager platformWindowManager =
- activity.getSystemService(android.view.WindowManager.class);
- return platformWindowManager.getCurrentWindowMetrics().getBounds();
+ return activity.getWindowManager().getCurrentWindowMetrics().getBounds();
} else if (Build.VERSION.SDK_INT >= Q) {
return computeWindowBoundsQ(activity);
} else if (Build.VERSION.SDK_INT >= P) {
@@ -123,6 +122,27 @@
}
}
+ /**
+ * Computes the maximum size and position of the area the window can expect with
+ * {@link android.view.WindowManager.LayoutParams#MATCH_PARENT MATCH_PARENT} width and height
+ * and any combination of flags that would allow the window to extend behind display cutouts.
+ * <p>
+ * The value returned from this method will always match {@link Display#getRealSize(Point)} on
+ * {@link Build.VERSION_CODES#Q Android 10} and below.
+ *
+ * @see android.view.WindowManager#getMaximumWindowMetrics()
+ */
+ @NonNull
+ Rect computeMaximumWindowBounds(Activity activity) {
+ if (Build.VERSION.SDK_INT >= R) {
+ return activity.getWindowManager().getMaximumWindowMetrics().getBounds();
+ } else {
+ Display display = activity.getWindowManager().getDefaultDisplay();
+ Point displaySize = getRealSizeForDisplay(display);
+ return new Rect(0, 0, displaySize.x, displaySize.y);
+ }
+ }
+
/** Computes the window bounds for {@link Build.VERSION_CODES#Q}. */
@NonNull
@RequiresApi(Q)
diff --git a/window/window/src/main/java/androidx/window/WindowManager.java b/window/window/src/main/java/androidx/window/WindowManager.java
index 9304d49..76c3633 100644
--- a/window/window/src/main/java/androidx/window/WindowManager.java
+++ b/window/window/src/main/java/androidx/window/WindowManager.java
@@ -19,6 +19,7 @@
import android.app.Activity;
import android.content.Context;
import android.content.ContextWrapper;
+import android.graphics.Rect;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -107,6 +108,57 @@
}
/**
+ * Returns the {@link WindowMetrics} according to the current system state.
+ * <p>
+ * The metrics describe the size of the area the window would occupy with
+ * {@link android.view.WindowManager.LayoutParams#MATCH_PARENT MATCH_PARENT} width and height
+ * and any combination of flags that would allow the window to extend behind display cutouts.
+ * <p>
+ * The value of this is based on the <b>current</b> windowing state of the system. For
+ * example, for activities in multi-window mode, the metrics returned are based on the
+ * current bounds that the user has selected for the {@link android.app.Activity Activity}'s
+ * window.
+ *
+ * @see #getMaximumWindowMetrics()
+ * @see android.view.WindowManager#getCurrentWindowMetrics()
+ */
+ @NonNull
+ public WindowMetrics getCurrentWindowMetrics() {
+ Activity activity = getActivityFromContext(mContext);
+ Rect currentBounds = WindowBoundsHelper.getInstance().computeCurrentWindowBounds(activity);
+ return new WindowMetrics(currentBounds);
+ }
+
+ /**
+ * Returns the largest {@link WindowMetrics} an app may expect in the current system state.
+ * <p>
+ * The metrics describe the size of the largest potential area the window might occupy with
+ * {@link android.view.WindowManager.LayoutParams#MATCH_PARENT MATCH_PARENT} width and height
+ * and any combination of flags that would allow the window to extend behind display cutouts.
+ * <p>
+ * The value of this is based on the largest <b>potential</b> windowing state of the system.
+ * For example, for activities in multi-window mode the metrics returned are based on what the
+ * bounds would be if the user expanded the window to cover the entire screen.
+ * <p>
+ * Note that this might still be smaller than the size of the physical display if certain
+ * areas of the display are not available to windows created for the associated {@link Context}.
+ * For example, devices with foldable displays that wrap around the enclosure may split the
+ * physical display into different regions, one for the front and one for the back, each acting
+ * as different logical displays. In this case {@link #getMaximumWindowMetrics()} would return
+ * the region describing the side of the device the associated {@link Context context's}
+ * window is placed.
+ *
+ * @see #getCurrentWindowMetrics()
+ * @see android.view.WindowManager#getMaximumWindowMetrics()
+ */
+ @NonNull
+ public WindowMetrics getMaximumWindowMetrics() {
+ Activity activity = getActivityFromContext(mContext);
+ Rect maxBounds = WindowBoundsHelper.getInstance().computeMaximumWindowBounds(activity);
+ return new WindowMetrics(maxBounds);
+ }
+
+ /**
* Unwraps the hierarchy of {@link ContextWrapper}-s until {@link Activity} is reached.
* @return Base {@link Activity} context or {@code null} if not available.
*/
diff --git a/window/window/src/main/java/androidx/window/WindowMetrics.java b/window/window/src/main/java/androidx/window/WindowMetrics.java
new file mode 100644
index 0000000..9faa5f6
--- /dev/null
+++ b/window/window/src/main/java/androidx/window/WindowMetrics.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.view.Display;
+import android.view.WindowManager;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Metrics about a {@link android.view.Window}, consisting of its bounds.
+ * <p>
+ * This is usually obtained from {@link WindowManager#getCurrentWindowMetrics()} or
+ * {@link WindowManager#getMaximumWindowMetrics()}.
+ *
+ * @see WindowManager#getCurrentWindowMetrics()
+ * @see WindowManager#getMaximumWindowMetrics()
+ */
+public final class WindowMetrics {
+ @NonNull
+ private final Rect mBounds;
+
+ /**
+ * Constructs a new {@link WindowMetrics} instance.
+ *
+ * @param bounds rect describing the bounds of the window, see {@link #getBounds}.
+ */
+ public WindowMetrics(@NonNull Rect bounds) {
+ mBounds = new Rect(bounds);
+ }
+
+ /**
+ * Returns a new {@link Rect} describing the bounds of the area the window occupies.
+ * <p>
+ * <b>Note that the size of the reported bounds can have different size than
+ * {@link Display#getSize(Point)}.</b> This method reports the window size including all system
+ * decorations, while {@link Display#getSize(Point)} reports the area excluding navigation bars
+ * and display cutout areas.
+ *
+ * @return window bounds in pixels.
+ */
+ @NonNull
+ public Rect getBounds() {
+ return new Rect(mBounds);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ WindowMetrics that = (WindowMetrics) o;
+ return mBounds.equals(that.mBounds);
+ }
+
+ @Override
+ public int hashCode() {
+ return mBounds.hashCode();
+ }
+}