Merge "Fix flaky tests due to time unit quantization errors." into androidx-master-dev
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/BaseDao.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/BaseDao.kt
index 5224667..1653a0e 100644
--- a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/BaseDao.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/BaseDao.kt
@@ -30,6 +30,12 @@
fun insertAll(t: List<T>)
@Insert
+ fun insertAllSet(t: Set<T>): List<Long>
+
+ @Insert
+ fun insertAllCollection(t: Collection<T>): Array<Long>
+
+ @Insert
fun insertAllArg(vararg t: T)
@Update
diff --git a/room/runtime/api/restricted_2.2.0-alpha02.txt b/room/runtime/api/restricted_2.2.0-alpha02.txt
index f3a1d00..8111b31 100644
--- a/room/runtime/api/restricted_2.2.0-alpha02.txt
+++ b/room/runtime/api/restricted_2.2.0-alpha02.txt
@@ -38,12 +38,12 @@
method public final void insert(T![]!);
method public final void insert(Iterable<? extends T>!);
method public final long insertAndReturnId(T!);
- method public final long[]! insertAndReturnIdsArray(java.util.Collection<T!>!);
+ method public final long[]! insertAndReturnIdsArray(java.util.Collection<? extends T>!);
method public final long[]! insertAndReturnIdsArray(T![]!);
- method public final Long![]! insertAndReturnIdsArrayBox(java.util.Collection<T!>!);
+ method public final Long![]! insertAndReturnIdsArrayBox(java.util.Collection<? extends T>!);
method public final Long![]! insertAndReturnIdsArrayBox(T![]!);
method public final java.util.List<java.lang.Long!>! insertAndReturnIdsList(T![]!);
- method public final java.util.List<java.lang.Long!>! insertAndReturnIdsList(java.util.Collection<T!>!);
+ method public final java.util.List<java.lang.Long!>! insertAndReturnIdsList(java.util.Collection<? extends T>!);
}
public class InvalidationTracker {
diff --git a/room/runtime/api/restricted_current.txt b/room/runtime/api/restricted_current.txt
index f3a1d00..8111b31 100644
--- a/room/runtime/api/restricted_current.txt
+++ b/room/runtime/api/restricted_current.txt
@@ -38,12 +38,12 @@
method public final void insert(T![]!);
method public final void insert(Iterable<? extends T>!);
method public final long insertAndReturnId(T!);
- method public final long[]! insertAndReturnIdsArray(java.util.Collection<T!>!);
+ method public final long[]! insertAndReturnIdsArray(java.util.Collection<? extends T>!);
method public final long[]! insertAndReturnIdsArray(T![]!);
- method public final Long![]! insertAndReturnIdsArrayBox(java.util.Collection<T!>!);
+ method public final Long![]! insertAndReturnIdsArrayBox(java.util.Collection<? extends T>!);
method public final Long![]! insertAndReturnIdsArrayBox(T![]!);
method public final java.util.List<java.lang.Long!>! insertAndReturnIdsList(T![]!);
- method public final java.util.List<java.lang.Long!>! insertAndReturnIdsList(java.util.Collection<T!>!);
+ method public final java.util.List<java.lang.Long!>! insertAndReturnIdsList(java.util.Collection<? extends T>!);
}
public class InvalidationTracker {
diff --git a/room/runtime/src/main/java/androidx/room/EntityInsertionAdapter.java b/room/runtime/src/main/java/androidx/room/EntityInsertionAdapter.java
index 8572f9e..3046d6c 100644
--- a/room/runtime/src/main/java/androidx/room/EntityInsertionAdapter.java
+++ b/room/runtime/src/main/java/androidx/room/EntityInsertionAdapter.java
@@ -123,7 +123,7 @@
* @param entities Entities to insert
* @return The SQLite row ids, for entities that are not inserted the row id returned will be -1
*/
- public final long[] insertAndReturnIdsArray(Collection<T> entities) {
+ public final long[] insertAndReturnIdsArray(Collection<? extends T> entities) {
final SupportSQLiteStatement stmt = acquire();
try {
final long[] result = new long[entities.size()];
@@ -167,7 +167,7 @@
* @param entities Entities to insert
* @return The SQLite row ids, for entities that are not inserted the row id returned will be -1
*/
- public final Long[] insertAndReturnIdsArrayBox(Collection<T> entities) {
+ public final Long[] insertAndReturnIdsArrayBox(Collection<? extends T> entities) {
final SupportSQLiteStatement stmt = acquire();
try {
final Long[] result = new Long[entities.size()];
@@ -233,7 +233,7 @@
* @param entities Entities to insert
* @return The SQLite row ids, for entities that are not inserted the row id returned will be -1
*/
- public final List<Long> insertAndReturnIdsList(Collection<T> entities) {
+ public final List<Long> insertAndReturnIdsList(Collection<? extends T> entities) {
final SupportSQLiteStatement stmt = acquire();
try {
final List<Long> result = new ArrayList<>(entities.size());
diff --git a/test/screenshot/build.gradle b/test/screenshot/build.gradle
index 2335202..a740b0b 100644
--- a/test/screenshot/build.gradle
+++ b/test/screenshot/build.gradle
@@ -25,6 +25,8 @@
}
dependencies {
+ implementation(ANDROIDX_TEST_RUNNER)
+
androidTestImplementation(JUNIT)
androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
androidTestImplementation(ANDROIDX_TEST_RUNNER)
diff --git a/test/screenshot/src/androidTest/java/androidx/test/screenshot/ScreenshotTestTest.kt b/test/screenshot/src/androidTest/java/androidx/test/screenshot/ScreenshotTestTest.kt
index 05e0ebd..6eef598 100644
--- a/test/screenshot/src/androidTest/java/androidx/test/screenshot/ScreenshotTestTest.kt
+++ b/test/screenshot/src/androidTest/java/androidx/test/screenshot/ScreenshotTestTest.kt
@@ -19,15 +19,18 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import org.junit.Assert.assertTrue
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class ScreenshotTestTest {
+ @get:Rule
+ val screenshotTestRule = ScreenshotTestRule()
+
@Test
@SmallTest
- @ScreenshotTest
fun screenshotTestTest_dummy() {
assertTrue(true)
}
diff --git a/test/screenshot/src/main/java/androidx/test/screenshot/ScreenshotTest.java b/test/screenshot/src/main/java/androidx/test/screenshot/ScreenshotTest.java
deleted file mode 100644
index ecffe28..0000000
--- a/test/screenshot/src/main/java/androidx/test/screenshot/ScreenshotTest.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.test.screenshot;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Annotation to assign a screenshot test qualifier to a test. This annotation can be used at a
- * method or class level.
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.METHOD, ElementType.TYPE})
-public @interface ScreenshotTest {
-}
-
diff --git a/test/screenshot/src/main/java/androidx/test/screenshot/ScreenshotTestRule.java b/test/screenshot/src/main/java/androidx/test/screenshot/ScreenshotTestRule.java
new file mode 100644
index 0000000..56c40a5
--- /dev/null
+++ b/test/screenshot/src/main/java/androidx/test/screenshot/ScreenshotTestRule.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.test.screenshot;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import org.junit.Assume;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+public class ScreenshotTestRule implements TestRule {
+
+ @Override
+ public @NonNull Statement apply(@NonNull Statement base, @Nullable Description description) {
+ return new ScreenshotTestStatement(base);
+ }
+
+ static class ScreenshotTestStatement extends Statement {
+
+ final Statement mBase;
+
+ ScreenshotTestStatement(Statement base) {
+ super();
+ mBase = base;
+ }
+
+ @Override
+ public void evaluate() throws Throwable {
+ Assume.assumeTrue(android.os.Build.MODEL.contains("Cuttlefish"));
+ mBase.evaluate();
+ }
+ }
+}
diff --git a/ui/ui-core/src/main/java/androidx/ui/core/Constraints.kt b/ui/ui-core/src/main/java/androidx/ui/core/Constraints.kt
index 68eb8839..9c3a3e4 100644
--- a/ui/ui-core/src/main/java/androidx/ui/core/Constraints.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/core/Constraints.kt
@@ -44,6 +44,7 @@
val maxHeight: IntPx = IntPx.Infinity
) {
init {
+ // TODO(mount/popam): This verification is costly. Can we avoid it sometimes or at least on production?
require(minWidth.isFinite()) { "Constraints#minWidth should be finite" }
require(minHeight.isFinite()) { "Constraints#minHeight should be finite" }
require(minWidth <= maxWidth) {
diff --git a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/MultipleCollect.kt b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/MultipleCollect.kt
index d48e272..e8f57e0 100644
--- a/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/MultipleCollect.kt
+++ b/ui/ui-framework/integration-tests/framework-demos/src/main/java/androidx/ui/framework/demos/MultipleCollect.kt
@@ -70,17 +70,17 @@
Layout(
childrenArray = arrayOf(header, content, footer),
layoutBlock = { measurables, constraints ->
- val headerPlaceable = measurables[header as () -> Unit].first().measure(
+ val headerPlaceable = measurables[header].first().measure(
Constraints.tightConstraints(constraints.maxWidth, 100.ipx)
)
val footerPadding = 50.ipx
- val footerPlaceable = measurables[footer as () -> Unit].first().measure(
+ val footerPlaceable = measurables[footer].first().measure(
Constraints.tightConstraints(constraints.maxWidth - footerPadding * 2, 100.ipx)
)
val itemHeight =
(constraints.maxHeight - headerPlaceable.height - footerPlaceable.height) /
- measurables[content as () -> Unit].size
- val contentPlaceables = measurables[content as () -> Unit].map { measurable ->
+ measurables[content].size
+ val contentPlaceables = measurables[content].map { measurable ->
measurable.measure(Constraints.tightConstraints(constraints.maxWidth, itemHeight))
}
diff --git a/ui/ui-framework/src/androidTest/java/androidx/ui/core/test/AndroidLayoutDrawTest.kt b/ui/ui-framework/src/androidTest/java/androidx/ui/core/test/AndroidLayoutDrawTest.kt
index cb22af9..27ace8d 100644
--- a/ui/ui-framework/src/androidTest/java/androidx/ui/core/test/AndroidLayoutDrawTest.kt
+++ b/ui/ui-framework/src/androidTest/java/androidx/ui/core/test/AndroidLayoutDrawTest.kt
@@ -454,11 +454,11 @@
measurables.forEachIndexed { index, measurable ->
measurable.measure(childConstraints[index])
}
- assertEquals(headerChildrenCount, measurables[header as () -> Unit].size)
- assertSame(measurables[0], measurables[header as () -> Unit][0])
- assertEquals(footerChildrenCount, measurables[footer as () -> Unit].size)
- assertSame(measurables[1], measurables[footer as () -> Unit][0])
- assertSame(measurables[2], measurables[footer as () -> Unit][1])
+ assertEquals(headerChildrenCount, measurables[header].size)
+ assertSame(measurables[0], measurables[header][0])
+ assertEquals(footerChildrenCount, measurables[footer].size)
+ assertSame(measurables[1], measurables[footer][0])
+ assertSame(measurables[2], measurables[footer][1])
}
}
}
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/Layout.kt b/ui/ui-framework/src/main/java/androidx/ui/core/Layout.kt
index a62f5bb..3938fa1 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/Layout.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/Layout.kt
@@ -66,8 +66,9 @@
internal val layoutNode: LayoutNode
get() = layoutNodeRef.value!!
- internal val childrenMeasurables: List<Measurable> get() =
- ComplexLayoutStateMeasurablesList(layoutNode.childrenLayouts().map { it as Measurable })
+ // TODO(mount/popam): This is inefficient
+ internal val childrenMeasurables: List<Measurable>
+ get() = ComplexLayoutStateMeasurablesList(layoutNode)
private var measureIteration = 0L
@@ -77,7 +78,8 @@
}
fun measure(constraints: Constraints): Placeable {
- val iteration = layoutNode.owner?.measureIteration ?: 0L
+ val owner = layoutNode.owner
+ val iteration = if (owner == null) 0L else owner.measureIteration
if (measureIteration == iteration) {
throw IllegalStateException("measure() may not be called multiple times " +
"on the same Measurable")
@@ -170,8 +172,32 @@
}
internal class ComplexLayoutStateMeasurablesList(
- internal val measurables: List<Measurable>
-) : List<Measurable> by (measurables.filter { it.parentData !is ChildrenEndParentData })
+ layoutNode: LayoutNode
+) : MutableList<Measurable> by mutableListOf() {
+ private val composableRanges = mutableMapOf<@Composable() () -> Unit, IntRange>()
+ fun composables(composable: @Composable() () -> Unit): List<Measurable> {
+ val range = composableRanges[composable] ?: return emptyList()
+ return subList(range.first, range.last)
+ }
+
+ init {
+ // var start = 0 doesn't work because of IR
+ val start = intArrayOf(0)
+ layoutNode.visitLayoutChildren { child ->
+ val layout = child.layout
+ if (layout != null) {
+ layout as Measurable
+ val parentData = layout.parentData
+ if (parentData is ChildrenEndParentData) {
+ composableRanges[parentData.children] = IntRange(start[0], size)
+ start[0] = size
+ } else {
+ this += layout
+ }
+ }
+ }
+ }
+}
/**
* Receiver scope for the [ComplexLayout] lambda.
@@ -436,17 +462,10 @@
* Returns all the [Measurable]s emitted for a particular children lambda.
* TODO(popam): finding measurables for each individual composable is O(n^2), consider improving
*/
- operator fun List<Measurable>.get(children: () -> Unit): List<Measurable> {
+ operator fun List<Measurable>.get(children: @Composable() () -> Unit): List<Measurable> {
if (this !is ComplexLayoutStateMeasurablesList) error("Invalid list of measurables")
- val childrenMeasurablesEnd = measurables.indexOfFirst {
- it.parentData is ChildrenEndParentData &&
- (it.parentData as ChildrenEndParentData).children == children
- }
- val childrenMeasurablesStart = measurables.take(childrenMeasurablesEnd).indexOfLast {
- it.parentData is ChildrenEndParentData
- } + 1
- return measurables.subList(childrenMeasurablesStart, childrenMeasurablesEnd)
+ return composables(children)
}
/**
* Measure the child [Measurable] with a specific set of [Constraints]. The result
diff --git a/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionContainer.kt b/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionContainer.kt
index 3b6ab5a..c238d48 100644
--- a/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionContainer.kt
+++ b/ui/ui-framework/src/main/java/androidx/ui/core/selection/SelectionContainer.kt
@@ -115,14 +115,14 @@
val width = placeable.width
val height = placeable.height
val start =
- measurables[startHandle as () -> Unit].first().measure(
+ measurables[startHandle].first().measure(
Constraints.tightConstraints(
HANDLE_WIDTH.round(),
HANDLE_HEIGHT.round()
)
)
val end =
- measurables[endHandle as () -> Unit].first().measure(
+ measurables[endHandle].first().measure(
Constraints.tightConstraints(
HANDLE_WIDTH.round(),
HANDLE_HEIGHT.round()
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/Flex.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/Flex.kt
index c4175ec0..c0c90f7 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/Flex.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/Flex.kt
@@ -409,16 +409,16 @@
* Box [Constraints], but which abstract away width and height in favor of main axis and cross axis.
*/
private data class OrientationIndependentConstraints(
- var mainAxisMin: IntPx,
- var mainAxisMax: IntPx,
- var crossAxisMin: IntPx,
- var crossAxisMax: IntPx
+ val mainAxisMin: IntPx,
+ val mainAxisMax: IntPx,
+ val crossAxisMin: IntPx,
+ val crossAxisMax: IntPx
) {
constructor(c: Constraints, orientation: FlexOrientation) : this(
- if (orientation == FlexOrientation.Horizontal) c.minWidth else c.minHeight,
- if (orientation == FlexOrientation.Horizontal) c.maxWidth else c.maxHeight,
- if (orientation == FlexOrientation.Horizontal) c.minHeight else c.minWidth,
- if (orientation == FlexOrientation.Horizontal) c.maxHeight else c.maxWidth
+ if (orientation === FlexOrientation.Horizontal) c.minWidth else c.minHeight,
+ if (orientation === FlexOrientation.Horizontal) c.maxWidth else c.maxHeight,
+ if (orientation === FlexOrientation.Horizontal) c.minHeight else c.minWidth,
+ if (orientation === FlexOrientation.Horizontal) c.maxHeight else c.maxWidth
)
// Creates a new instance with the same cross axis constraints and unbounded main axis.
@@ -436,7 +436,7 @@
// Given an orientation, resolves the current instance to traditional constraints.
fun toBoxConstraints(orientation: FlexOrientation) =
- if (orientation == FlexOrientation.Horizontal) {
+ if (orientation === FlexOrientation.Horizontal) {
Constraints(mainAxisMin, mainAxisMax, crossAxisMin, crossAxisMax)
} else {
Constraints(crossAxisMin, crossAxisMax, mainAxisMin, mainAxisMax)
@@ -444,7 +444,7 @@
// Given an orientation, resolves the max width constraint this instance represents.
fun maxWidth(orientation: FlexOrientation) =
- if (orientation == FlexOrientation.Horizontal) {
+ if (orientation === FlexOrientation.Horizontal) {
mainAxisMax
} else {
crossAxisMax
@@ -452,7 +452,7 @@
// Given an orientation, resolves the max height constraint this instance represents.
fun maxHeight(orientation: FlexOrientation) =
- if (orientation == FlexOrientation.Horizontal) {
+ if (orientation === FlexOrientation.Horizontal) {
crossAxisMax
} else {
mainAxisMax
diff --git a/ui/ui-platform/src/main/java/androidx/ui/core/ComponentNodes.kt b/ui/ui-platform/src/main/java/androidx/ui/core/ComponentNodes.kt
index 5dfa08e..1bdfbc3 100644
--- a/ui/ui-platform/src/main/java/androidx/ui/core/ComponentNodes.kt
+++ b/ui/ui-platform/src/main/java/androidx/ui/core/ComponentNodes.kt
@@ -167,7 +167,9 @@
* Execute [block] on all children of this ComponentNode.
*/
fun visitChildren(block: (ComponentNode) -> Unit) {
- children.forEach(block)
+ for (i in 0 until children.size) {
+ block(children[i])
+ }
}
/**
diff --git a/webkit/api/1.1.0-alpha02.txt b/webkit/api/1.1.0-alpha02.txt
index 9190ab6..6ef8368 100644
--- a/webkit/api/1.1.0-alpha02.txt
+++ b/webkit/api/1.1.0-alpha02.txt
@@ -134,10 +134,10 @@
public static final class WebViewAssetLoader.Builder {
ctor public WebViewAssetLoader.Builder();
- method public androidx.webkit.WebViewAssetLoader.Builder allowHttp(boolean);
+ method public androidx.webkit.WebViewAssetLoader.Builder addPathHandler(String, androidx.webkit.WebViewAssetLoader.PathHandler);
method public androidx.webkit.WebViewAssetLoader build();
- method public androidx.webkit.WebViewAssetLoader.Builder onDomain(String);
- method public androidx.webkit.WebViewAssetLoader.Builder register(String, androidx.webkit.WebViewAssetLoader.PathHandler);
+ method public androidx.webkit.WebViewAssetLoader.Builder setDomain(String);
+ method public androidx.webkit.WebViewAssetLoader.Builder setHttpAllowed(boolean);
}
public static interface WebViewAssetLoader.PathHandler {
diff --git a/webkit/api/current.txt b/webkit/api/current.txt
index 9190ab6..6ef8368 100644
--- a/webkit/api/current.txt
+++ b/webkit/api/current.txt
@@ -134,10 +134,10 @@
public static final class WebViewAssetLoader.Builder {
ctor public WebViewAssetLoader.Builder();
- method public androidx.webkit.WebViewAssetLoader.Builder allowHttp(boolean);
+ method public androidx.webkit.WebViewAssetLoader.Builder addPathHandler(String, androidx.webkit.WebViewAssetLoader.PathHandler);
method public androidx.webkit.WebViewAssetLoader build();
- method public androidx.webkit.WebViewAssetLoader.Builder onDomain(String);
- method public androidx.webkit.WebViewAssetLoader.Builder register(String, androidx.webkit.WebViewAssetLoader.PathHandler);
+ method public androidx.webkit.WebViewAssetLoader.Builder setDomain(String);
+ method public androidx.webkit.WebViewAssetLoader.Builder setHttpAllowed(boolean);
}
public static interface WebViewAssetLoader.PathHandler {
diff --git a/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/AssetLoaderAjaxActivity.java b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/AssetLoaderAjaxActivity.java
index 461cae7..7a18003 100644
--- a/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/AssetLoaderAjaxActivity.java
+++ b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/AssetLoaderAjaxActivity.java
@@ -72,11 +72,11 @@
// The developer should ALWAYS use a domain which they are in control of or use
// the default androidplatform.net reserved by Google for this purpose.
mAssetLoader = new WebViewAssetLoader.Builder()
- .onDomain("example.com") // use "example.com" instead of the default domain
+ .setDomain("example.com") // use "example.com" instead of the default domain
// Host app resources ... under https://example.com/androidx_webkit/example/res/...
- .register("/androidx_webkit/example/res/", new ResourcesPathHandler(this))
+ .addPathHandler("/androidx_webkit/example/res/", new ResourcesPathHandler(this))
// Host app assets under https://example.com/androidx_webkit/example/assets/...
- .register("/androidx_webkit/example/assets/", new AssetsPathHandler(this))
+ .addPathHandler("/androidx_webkit/example/assets/", new AssetsPathHandler(this))
.build();
mWebView = findViewById(R.id.webview_asset_loader_webview);
diff --git a/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/AssetLoaderSimpleActivity.java b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/AssetLoaderSimpleActivity.java
index 9c0d16b..b36b986 100644
--- a/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/AssetLoaderSimpleActivity.java
+++ b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/AssetLoaderSimpleActivity.java
@@ -70,7 +70,7 @@
// Host application assets under http://appassets.androidplatform.net/assets/...
mAssetLoader = new WebViewAssetLoader.Builder()
- .register("/assets/", new WebViewAssetLoader.AssetsPathHandler(this))
+ .addPathHandler("/assets/", new WebViewAssetLoader.AssetsPathHandler(this))
.build();
Uri path = new Uri.Builder()
.scheme("https")
diff --git a/webkit/src/androidTest/java/androidx/webkit/WebViewAssetLoaderIntegrationTest.java b/webkit/src/androidTest/java/androidx/webkit/WebViewAssetLoaderIntegrationTest.java
index 742ffc8..70a2b11 100644
--- a/webkit/src/androidTest/java/androidx/webkit/WebViewAssetLoaderIntegrationTest.java
+++ b/webkit/src/androidTest/java/androidx/webkit/WebViewAssetLoaderIntegrationTest.java
@@ -84,7 +84,7 @@
final WebViewTestActivity activity = mActivityRule.getActivity();
WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
- .register("/assets/", new AssetsPathHandler(activity))
+ .addPathHandler("/assets/", new AssetsPathHandler(activity))
.build();
mOnUiThread.setWebViewClient(new AssetLoadingWebViewClient(mOnUiThread, assetLoader));
@@ -108,7 +108,7 @@
final WebViewTestActivity activity = mActivityRule.getActivity();
WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
- .register("/res/", new ResourcesPathHandler(activity))
+ .addPathHandler("/res/", new ResourcesPathHandler(activity))
.build();
mOnUiThread.setWebViewClient(new AssetLoadingWebViewClient(mOnUiThread, assetLoader));
diff --git a/webkit/src/androidTest/java/androidx/webkit/WebViewAssetLoaderTest.java b/webkit/src/androidTest/java/androidx/webkit/WebViewAssetLoaderTest.java
index c16b058..029425a 100644
--- a/webkit/src/androidTest/java/androidx/webkit/WebViewAssetLoaderTest.java
+++ b/webkit/src/androidTest/java/androidx/webkit/WebViewAssetLoaderTest.java
@@ -97,7 +97,7 @@
public void testCustomPathHandler() throws Throwable {
PathHandler pathHandler = new TestPathHandler();
WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
- .register("/test/", pathHandler)
+ .addPathHandler("/test/", pathHandler)
.build();
WebResourceResponse response = assetLoader.shouldInterceptRequest(
@@ -113,15 +113,15 @@
public void testCustomDomain() throws Throwable {
PathHandler pathHandler = new TestPathHandler();
WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
- .onDomain("test.myDomain.net")
- .register("/test/", pathHandler)
+ .setDomain("test.myDomain.net")
+ .addPathHandler("/test/", pathHandler)
.build();
WebResourceResponse response = assetLoader.shouldInterceptRequest(
Uri.parse("https://test.myDomain.net/test/"));
assertResponse(response, CONTENTS);
- Assert.assertNull("non-registered URL should return null response",
+ Assert.assertNull("non-addPathHandlered URL should return null response",
assetLoader.shouldInterceptRequest(
Uri.parse("https://appassets.androidplatform.net/test/")));
}
@@ -131,8 +131,8 @@
public void testAllowingHttp() throws Throwable {
PathHandler pathHandler = new TestPathHandler();
WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
- .allowHttp(true)
- .register("/test/", pathHandler)
+ .setHttpAllowed(true)
+ .addPathHandler("/test/", pathHandler)
.build();
WebResourceResponse response = assetLoader.shouldInterceptRequest(
@@ -149,7 +149,7 @@
public void testDisallowingHttp() throws Throwable {
PathHandler pathHandler = new TestPathHandler();
WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
- .register("/test/", pathHandler)
+ .addPathHandler("/test/", pathHandler)
.build();
WebResourceResponse response = assetLoader.shouldInterceptRequest(
@@ -180,7 +180,7 @@
}
});
WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
- .register("/assets/", assetsPathHandler)
+ .addPathHandler("/assets/", assetsPathHandler)
.build();
WebResourceResponse response = assetLoader.shouldInterceptRequest(
@@ -207,7 +207,7 @@
}
});
WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
- .register("/res/", resourcesPathHandler)
+ .addPathHandler("/res/", resourcesPathHandler)
.build();
WebResourceResponse response = assetLoader.shouldInterceptRequest(
@@ -221,7 +221,7 @@
WebViewAssetLoader.Builder builder = new WebViewAssetLoader.Builder();
for (int i = 1; i <= 5; ++i) {
final String testContent = CONTENTS + Integer.toString(i);
- builder.register("/test_path_" + Integer.toString(i) + "/", new PathHandler() {
+ builder.addPathHandler("/test_path_" + Integer.toString(i) + "/", new PathHandler() {
@Override
public WebResourceResponse handle(@NonNull String path) {
try {
@@ -283,8 +283,8 @@
@SmallTest
public void testMultiplePathHandlersOnTheSamePath() throws Throwable {
WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
- .register("/test_path/", new FakeZipPathHandler())
- .register("/test_path/", new FakeTextPathHandler())
+ .addPathHandler("/test_path/", new FakeZipPathHandler())
+ .addPathHandler("/test_path/", new FakeTextPathHandler())
.build();
WebResourceResponse response = assetLoader.shouldInterceptRequest(
@@ -301,8 +301,8 @@
// Register in reverse order to make sure it works regardless of order.
assetLoader = new WebViewAssetLoader.Builder()
- .register("/test_path/", new FakeTextPathHandler())
- .register("/test_path/", new FakeZipPathHandler())
+ .addPathHandler("/test_path/", new FakeTextPathHandler())
+ .addPathHandler("/test_path/", new FakeZipPathHandler())
.build();
response = assetLoader.shouldInterceptRequest(
diff --git a/webkit/src/main/java/androidx/webkit/WebViewAssetLoader.java b/webkit/src/main/java/androidx/webkit/WebViewAssetLoader.java
index ccfe8da..fa064fc 100644
--- a/webkit/src/main/java/androidx/webkit/WebViewAssetLoader.java
+++ b/webkit/src/main/java/androidx/webkit/WebViewAssetLoader.java
@@ -56,8 +56,8 @@
* A typical usage would be like:
* <pre class="prettyprint">
* final WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
- * .register("/assets/", new AssetsPathHandler(this))
- * .register("/res/", new ResourcesPathHandler(this))
+ * .addPathHandler("/assets/", new AssetsPathHandler(this))
+ * .addPathHandler("/res/", new ResourcesPathHandler(this))
* .build();
*
* webView.setWebViewClient(new WebViewClient() {
@@ -304,12 +304,12 @@
* A builder class for constructing {@link WebViewAssetLoader} objects.
*/
public static final class Builder {
- private boolean mAllowHttp;
+ private boolean mHttpAllowed;
private String mDomain;
@NonNull private List<PathMatcher> mBuilderMatcherList;
public Builder() {
- mAllowHttp = false;
+ mHttpAllowed = false;
mDomain = DEFAULT_DOMAIN;
mBuilderMatcherList = new ArrayList<>();
}
@@ -322,7 +322,7 @@
* @return {@link Builder} object.
*/
@NonNull
- public Builder onDomain(@NonNull String domain) {
+ public Builder setDomain(@NonNull String domain) {
mDomain = domain;
return this;
}
@@ -334,8 +334,8 @@
* @return {@link Builder} object.
*/
@NonNull
- public Builder allowHttp(boolean allowHttp) {
- mAllowHttp = allowHttp;
+ public Builder setHttpAllowed(boolean httpAllowed) {
+ mHttpAllowed = httpAllowed;
return this;
}
@@ -351,8 +351,8 @@
* @throws IllegalArgumentException if the path is invalid.
*/
@NonNull
- public Builder register(@NonNull String path, @NonNull PathHandler handler) {
- mBuilderMatcherList.add(new PathMatcher(mDomain, path, mAllowHttp, handler));
+ public Builder addPathHandler(@NonNull String path, @NonNull PathHandler handler) {
+ mBuilderMatcherList.add(new PathMatcher(mDomain, path, mHttpAllowed, handler));
return this;
}
diff --git a/work/workmanager/api/2.3.0-alpha01.txt b/work/workmanager/api/2.3.0-alpha01.txt
index f0bff1a..8d3b024 100644
--- a/work/workmanager/api/2.3.0-alpha01.txt
+++ b/work/workmanager/api/2.3.0-alpha01.txt
@@ -13,6 +13,7 @@
public final class Configuration {
method public java.util.concurrent.Executor getExecutor();
+ method public androidx.work.InputMergerFactory getInputMergerFactory();
method public int getMaxJobSchedulerId();
method public int getMinJobSchedulerId();
method public java.util.concurrent.Executor getTaskExecutor();
@@ -24,6 +25,7 @@
ctor public Configuration.Builder();
method public androidx.work.Configuration build();
method public androidx.work.Configuration.Builder setExecutor(java.util.concurrent.Executor);
+ method public androidx.work.Configuration.Builder setInputMergerFactory(androidx.work.InputMergerFactory);
method public androidx.work.Configuration.Builder setJobSchedulerJobIdRange(int, int);
method public androidx.work.Configuration.Builder setMaxSchedulerLimit(int);
method public androidx.work.Configuration.Builder setMinimumLoggingLevel(int);
@@ -124,6 +126,11 @@
method public abstract androidx.work.Data merge(java.util.List<androidx.work.Data!>);
}
+ public abstract class InputMergerFactory {
+ ctor public InputMergerFactory();
+ method public abstract androidx.work.InputMerger? createInputMerger(String);
+ }
+
public abstract class ListenableWorker {
ctor @Keep public ListenableWorker(android.content.Context, androidx.work.WorkerParameters);
method public final android.content.Context getApplicationContext();
diff --git a/work/workmanager/api/current.txt b/work/workmanager/api/current.txt
index f0bff1a..8d3b024 100644
--- a/work/workmanager/api/current.txt
+++ b/work/workmanager/api/current.txt
@@ -13,6 +13,7 @@
public final class Configuration {
method public java.util.concurrent.Executor getExecutor();
+ method public androidx.work.InputMergerFactory getInputMergerFactory();
method public int getMaxJobSchedulerId();
method public int getMinJobSchedulerId();
method public java.util.concurrent.Executor getTaskExecutor();
@@ -24,6 +25,7 @@
ctor public Configuration.Builder();
method public androidx.work.Configuration build();
method public androidx.work.Configuration.Builder setExecutor(java.util.concurrent.Executor);
+ method public androidx.work.Configuration.Builder setInputMergerFactory(androidx.work.InputMergerFactory);
method public androidx.work.Configuration.Builder setJobSchedulerJobIdRange(int, int);
method public androidx.work.Configuration.Builder setMaxSchedulerLimit(int);
method public androidx.work.Configuration.Builder setMinimumLoggingLevel(int);
@@ -124,6 +126,11 @@
method public abstract androidx.work.Data merge(java.util.List<androidx.work.Data!>);
}
+ public abstract class InputMergerFactory {
+ ctor public InputMergerFactory();
+ method public abstract androidx.work.InputMerger? createInputMerger(String);
+ }
+
public abstract class ListenableWorker {
ctor @Keep public ListenableWorker(android.content.Context, androidx.work.WorkerParameters);
method public final android.content.Context getApplicationContext();
diff --git a/work/workmanager/src/androidTest/java/androidx/work/InputMergerFactoryTest.kt b/work/workmanager/src/androidTest/java/androidx/work/InputMergerFactoryTest.kt
new file mode 100644
index 0000000..8104d5f
--- /dev/null
+++ b/work/workmanager/src/androidTest/java/androidx/work/InputMergerFactoryTest.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.work
+
+import android.content.Context
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import org.hamcrest.CoreMatchers.notNullValue
+import org.hamcrest.MatcherAssert.assertThat
+import org.hamcrest.Matchers.`is`
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class InputMergerFactoryTest {
+ private lateinit var context: Context
+ private lateinit var factory: InputMergerFactory
+ private lateinit var configuration: Configuration
+
+ @Before
+ fun setUp() {
+ context = ApplicationProvider.getApplicationContext()
+ factory = mock(InputMergerFactory::class.java)
+ configuration = Configuration.Builder()
+ .setInputMergerFactory(factory)
+ .build()
+ }
+
+ @Test
+ fun testInputMergerFactory() {
+ val name = ArrayCreatingInputMerger::class.java.name
+ val merger = configuration.inputMergerFactory.createInputMergerWithDefaultFallback(name)
+ assertThat(merger, notNullValue())
+ verify(factory, times(1)).createInputMerger(name)
+ }
+
+ @Test
+ fun testInputMergerFactory2() {
+ factory = object : InputMergerFactory() {
+ override fun createInputMerger(className: String): InputMerger? {
+ return OverwritingInputMerger()
+ }
+ }
+ configuration = Configuration.Builder()
+ .setInputMergerFactory(factory)
+ .build()
+
+ val name = ArrayCreatingInputMerger::class.java.name
+ val merger = configuration.inputMergerFactory.createInputMergerWithDefaultFallback(name)
+ val instanceCheck = merger is OverwritingInputMerger
+ assertThat(merger, notNullValue())
+ assertThat(instanceCheck, `is`(true))
+ }
+}
diff --git a/work/workmanager/src/main/java/androidx/work/Configuration.java b/work/workmanager/src/main/java/androidx/work/Configuration.java
index 9246dcce..c24f643 100644
--- a/work/workmanager/src/main/java/androidx/work/Configuration.java
+++ b/work/workmanager/src/main/java/androidx/work/Configuration.java
@@ -51,6 +51,7 @@
private final @NonNull Executor mExecutor;
private final @NonNull Executor mTaskExecutor;
private final @NonNull WorkerFactory mWorkerFactory;
+ private final @NonNull InputMergerFactory mInputMergerFactory;
private final int mLoggingLevel;
private final int mMinJobSchedulerId;
private final int mMaxJobSchedulerId;
@@ -78,6 +79,12 @@
mWorkerFactory = builder.mWorkerFactory;
}
+ if (builder.mInputMergerFactory == null) {
+ mInputMergerFactory = InputMergerFactory.getDefaultInputMergerFactory();
+ } else {
+ mInputMergerFactory = builder.mInputMergerFactory;
+ }
+
mLoggingLevel = builder.mLoggingLevel;
mMinJobSchedulerId = builder.mMinJobSchedulerId;
mMaxJobSchedulerId = builder.mMaxJobSchedulerId;
@@ -115,6 +122,14 @@
}
/**
+ * @return The {@link InputMergerFactory} used by {@link WorkManager} to create instances of
+ * {@link InputMerger}s.
+ */
+ public @NonNull InputMergerFactory getInputMergerFactory() {
+ return mInputMergerFactory;
+ }
+
+ /**
* Gets the minimum logging level for {@link WorkManager}.
*
* @return The minimum logging level, corresponding to the constants found in
@@ -186,6 +201,7 @@
Executor mExecutor;
WorkerFactory mWorkerFactory;
+ InputMergerFactory mInputMergerFactory;
Executor mTaskExecutor;
int mLoggingLevel = Log.INFO;
@@ -205,6 +221,17 @@
}
/**
+ * Specifies a custom {@link InputMergerFactory} for WorkManager.
+ * @param inputMergerFactory A {@link InputMergerFactory} for creating {@link InputMerger}s
+ * @return This {@link Builder} instance
+ */
+ @NonNull
+ public Builder setInputMergerFactory(@NonNull InputMergerFactory inputMergerFactory) {
+ mInputMergerFactory = inputMergerFactory;
+ return this;
+ }
+
+ /**
* Specifies a custom {@link Executor} for WorkManager.
*
* @param executor An {@link Executor} for running {@link Worker}s
diff --git a/work/workmanager/src/main/java/androidx/work/InputMergerFactory.java b/work/workmanager/src/main/java/androidx/work/InputMergerFactory.java
new file mode 100644
index 0000000..ac30624
--- /dev/null
+++ b/work/workmanager/src/main/java/androidx/work/InputMergerFactory.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.work;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+
+/**
+ * A factory object that creates {@link InputMerger} instances. The factory is invoked every
+ * time a work runs. You can override the default implementation of this factory by manually
+ * initializing {@link WorkManager} (see {@link WorkManager#initialize(Context, Configuration)}
+ * and specifying a new {@link InputMergerFactory} in
+ * {@link Configuration.Builder#setInputMergerFactory(InputMergerFactory)}.
+ */
+public abstract class InputMergerFactory {
+ /**
+ * Override this method to create an instance of a {@link InputMerger} given its fully
+ * qualified class name.
+ * <p></p>
+ * Throwing an {@link Exception} here will crash the application. If an
+ * {@link InputMergerFactory} is unable to create an instance of a {@link InputMerger}, it
+ * should return {@code null} so it can delegate to the default {@link InputMergerFactory}.
+ *
+ * @param className The fully qualified class name for the {@link InputMerger}
+ * @return an instance of {@link InputMerger}
+ */
+ @Nullable
+ public abstract InputMerger createInputMerger(@NonNull String className);
+
+ /**
+ * Creates an instance of a {@link InputMerger} given its fully
+ * qualified class name with the correct fallback behavior.
+ *
+ * @param className The fully qualified class name for the {@link InputMerger}
+ * @return an instance of {@link InputMerger}
+ *
+ * @hide
+ */
+ @Nullable
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ public final InputMerger createInputMergerWithDefaultFallback(@NonNull String className) {
+ InputMerger inputMerger = createInputMerger(className);
+ if (inputMerger == null) {
+ inputMerger = InputMerger.fromClassName(className);
+ }
+ return inputMerger;
+ }
+
+ /**
+ * @return A default {@link InputMergerFactory} with no custom behavior.
+ *
+ * @hide
+ */
+ @NonNull
+ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ public static InputMergerFactory getDefaultInputMergerFactory() {
+ return new InputMergerFactory() {
+ @Nullable
+ @Override
+ public InputMerger createInputMerger(@NonNull String className) {
+ return null;
+ }
+ };
+ }
+}
diff --git a/work/workmanager/src/main/java/androidx/work/impl/WorkerWrapper.java b/work/workmanager/src/main/java/androidx/work/impl/WorkerWrapper.java
index efa3b4c..de5b1c5 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/WorkerWrapper.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/WorkerWrapper.java
@@ -35,6 +35,7 @@
import androidx.work.Configuration;
import androidx.work.Data;
import androidx.work.InputMerger;
+import androidx.work.InputMergerFactory;
import androidx.work.ListenableWorker;
import androidx.work.Logger;
import androidx.work.WorkInfo;
@@ -199,7 +200,10 @@
if (mWorkSpec.isPeriodic()) {
input = mWorkSpec.input;
} else {
- InputMerger inputMerger = InputMerger.fromClassName(mWorkSpec.inputMergerClassName);
+ InputMergerFactory inputMergerFactory = mConfiguration.getInputMergerFactory();
+ String inputMergerClassName = mWorkSpec.inputMergerClassName;
+ InputMerger inputMerger =
+ inputMergerFactory.createInputMergerWithDefaultFallback(inputMergerClassName);
if (inputMerger == null) {
Logger.get().error(TAG, String.format("Could not create Input Merger %s",
mWorkSpec.inputMergerClassName));