[go: nahoru, domu]

Merge "Update API lint baselines" into androidx-master-dev
diff --git a/appcompat/appcompat/api/res-1.3.0-alpha01.txt b/appcompat/appcompat/api/res-1.3.0-alpha01.txt
index cb8c1e3..cdb62d2 100644
--- a/appcompat/appcompat/api/res-1.3.0-alpha01.txt
+++ b/appcompat/appcompat/api/res-1.3.0-alpha01.txt
@@ -25,6 +25,7 @@
 attr actionModeShareDrawable
 attr actionModeSplitBackground
 attr actionModeStyle
+attr actionModeTheme
 attr actionModeWebSearchDrawable
 attr actionOverflowButtonStyle
 attr actionOverflowMenuStyle
diff --git a/appcompat/appcompat/api/res-current.txt b/appcompat/appcompat/api/res-current.txt
index cb8c1e3..cdb62d2 100644
--- a/appcompat/appcompat/api/res-current.txt
+++ b/appcompat/appcompat/api/res-current.txt
@@ -25,6 +25,7 @@
 attr actionModeShareDrawable
 attr actionModeSplitBackground
 attr actionModeStyle
+attr actionModeTheme
 attr actionModeWebSearchDrawable
 attr actionOverflowButtonStyle
 attr actionOverflowMenuStyle
diff --git a/appcompat/appcompat/src/main/res-public/values/public_attrs.xml b/appcompat/appcompat/src/main/res-public/values/public_attrs.xml
index bc81c6e..c943ed8 100644
--- a/appcompat/appcompat/src/main/res-public/values/public_attrs.xml
+++ b/appcompat/appcompat/src/main/res-public/values/public_attrs.xml
@@ -43,6 +43,7 @@
      <public type="attr" name="actionModeShareDrawable"/>
      <public type="attr" name="actionModeSplitBackground"/>
      <public type="attr" name="actionModeStyle"/>
+     <public type="attr" name="actionModeTheme"/>
      <public type="attr" name="actionModeWebSearchDrawable"/>
      <public type="attr" name="actionOverflowButtonStyle"/>
      <public type="attr" name="actionOverflowMenuStyle"/>
diff --git a/appcompat/appcompat/src/main/res/layout-v26/abc_screen_toolbar.xml b/appcompat/appcompat/src/main/res/layout-v26/abc_screen_toolbar.xml
index 25412ba..8d2ea46 100644
--- a/appcompat/appcompat/src/main/res/layout-v26/abc_screen_toolbar.xml
+++ b/appcompat/appcompat/src/main/res/layout-v26/abc_screen_toolbar.xml
@@ -46,7 +46,7 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:visibility="gone"
-            android:theme="?attr/actionBarTheme"
+            android:theme="?attr/actionModeTheme"
             style="?attr/actionModeStyle"/>
 
     </androidx.appcompat.widget.ActionBarContainer>
diff --git a/appcompat/appcompat/src/main/res/layout/abc_action_mode_bar.xml b/appcompat/appcompat/src/main/res/layout/abc_action_mode_bar.xml
index 76f34f4..96783f8 100644
--- a/appcompat/appcompat/src/main/res/layout/abc_action_mode_bar.xml
+++ b/appcompat/appcompat/src/main/res/layout/abc_action_mode_bar.xml
@@ -21,5 +21,5 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:visibility="gone"
-        android:theme="?attr/actionBarTheme"
+        android:theme="?attr/actionModeTheme"
         style="?attr/actionModeStyle"/>
diff --git a/appcompat/appcompat/src/main/res/layout/abc_screen_toolbar.xml b/appcompat/appcompat/src/main/res/layout/abc_screen_toolbar.xml
index 5c0dbcd..64d7321 100644
--- a/appcompat/appcompat/src/main/res/layout/abc_screen_toolbar.xml
+++ b/appcompat/appcompat/src/main/res/layout/abc_screen_toolbar.xml
@@ -45,7 +45,7 @@
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:visibility="gone"
-                android:theme="?attr/actionBarTheme"
+                android:theme="?attr/actionModeTheme"
                 style="?attr/actionModeStyle"/>
 
     </androidx.appcompat.widget.ActionBarContainer>
diff --git a/appcompat/appcompat/src/main/res/values/attrs.xml b/appcompat/appcompat/src/main/res/values/attrs.xml
index b1e0492..b4dbd1a 100644
--- a/appcompat/appcompat/src/main/res/values/attrs.xml
+++ b/appcompat/appcompat/src/main/res/values/attrs.xml
@@ -110,6 +110,10 @@
              action bar. This will be inherited by any widget inflated
              into the action bar. -->
         <attr name="actionBarTheme" format="reference" />
+        <!-- Reference to a theme that should be used to inflate the
+             action bar in action mode. This will be inherited by any widget inflated
+             into the action bar. -->
+        <attr name="actionModeTheme" format="reference" />
         <!-- Reference to a theme that should be used to inflate widgets
              and layouts destined for the action bar. Most of the time
              this will be a reference to the current theme, but when
@@ -165,7 +169,6 @@
         <!-- PopupWindow style to use for action modes when showing as a window overlay. -->
         <attr name="actionModePopupWindowStyle" format="reference"/>
 
-
         <!-- =================== -->
         <!-- Text styles -->
         <!-- =================== -->
diff --git a/appcompat/appcompat/src/main/res/values/themes_base.xml b/appcompat/appcompat/src/main/res/values/themes_base.xml
index db97f83..dfe7bd4 100644
--- a/appcompat/appcompat/src/main/res/values/themes_base.xml
+++ b/appcompat/appcompat/src/main/res/values/themes_base.xml
@@ -213,6 +213,7 @@
         <item name="actionDropDownStyle">@style/Widget.AppCompat.Spinner.DropDown.ActionBar</item>
 
         <!-- Action Mode -->
+        <item name="actionModeTheme">?attr/actionBarTheme</item>
         <item name="actionModeStyle">@style/Widget.AppCompat.ActionMode</item>
         <item name="actionModeBackground">@drawable/abc_cab_background_top_material</item>
         <item name="actionModeSplitBackground">?attr/colorPrimaryDark</item>
@@ -382,6 +383,7 @@
         <item name="actionMenuTextColor">?android:attr/textColorPrimaryDisableOnly</item>
 
         <!-- Action Mode -->
+        <item name="actionModeTheme">?attr/actionBarTheme</item>
         <item name="actionModeStyle">@style/Widget.AppCompat.ActionMode</item>
         <item name="actionModeBackground">@drawable/abc_cab_background_top_material</item>
         <item name="actionModeSplitBackground">?attr/colorPrimaryDark</item>
@@ -525,6 +527,7 @@
         <item name="actionBarPopupTheme">@style/ThemeOverlay.AppCompat.Light</item>
         <item name="actionBarWidgetTheme">@null</item>
         <item name="actionBarTheme">@style/ThemeOverlay.AppCompat.Dark.ActionBar</item>
+        <item name="actionModeTheme">?attr/actionBarTheme</item>
 
         <!-- Panel attributes -->
         <item name="listChoiceBackgroundIndicator">@drawable/abc_list_selector_holo_dark</item>
diff --git a/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt b/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt
index a5da910..7659c54 100644
--- a/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt
@@ -148,6 +148,7 @@
     prebuilts(LibraryGroups.WEBKIT, "1.3.0-alpha03")
     ignore(LibraryGroups.WINDOW.group, "window-sidecar")
     prebuilts(LibraryGroups.WINDOW, "1.0.0-alpha01")
+            .addStubs("window/stubs/window-sidecar-release-0.1.0-alpha01.aar")
     ignore(LibraryGroups.WORK.group, "work-gcm")
     ignore(LibraryGroups.WORK.group, "work-runtime-lint")
     ignore(LibraryGroups.WORK.group, "work-rxjava3")
diff --git a/datastore/datastore-proto/build.gradle b/datastore/datastore-proto/build.gradle
index a84fdf8..6ad99a8 100644
--- a/datastore/datastore-proto/build.gradle
+++ b/datastore/datastore-proto/build.gradle
@@ -15,8 +15,8 @@
  */
 
 import static androidx.build.dependencies.DependenciesKt.*
+
 import androidx.build.LibraryGroups
-import androidx.build.LibraryVersions
 import androidx.build.AndroidXExtension
 import androidx.build.Publish
 
@@ -24,18 +24,36 @@
     id("AndroidXPlugin")
     id("com.android.library")
     id("kotlin-android")
-}
+    id("com.google.protobuf")
 
-android {
-    buildTypes {
-        debug {
-            testCoverageEnabled = false // Breaks Kotlin compiler.
-        }
-    }
 }
 
 dependencies {
-    api(KOTLIN_STDLIB)
+    api(project(":datastore:datastore-core"))
+    api(PROTOBUF_LITE)
+
+    testImplementation(TRUTH)
+    testImplementation(JUNIT)
+    testImplementation(project(":internal-testutils-truth"))
+}
+
+protobuf {
+    protoc {
+        artifact = PROTOBUF_COMPILER
+    }
+
+    // Generates the java proto-lite code for the protos in this project. See
+    // https://github.com/google/protobuf-gradle-plugin#customizing-protobuf-compilation
+    // for more information.
+    generateProtoTasks {
+        all().each { task ->
+            task.builtins {
+                java {
+                    option 'lite'
+                }
+            }
+        }
+    }
 }
 
 androidx {
@@ -45,4 +63,4 @@
     inceptionYear = "2020"
     description = "Android Proto DataStore"
     url = AndroidXExtension.SUPPORT_URL
-}
+}
\ No newline at end of file
diff --git a/datastore/datastore-proto/src/main/java/androidx/datastore/protos/ProtoSerializer.kt b/datastore/datastore-proto/src/main/java/androidx/datastore/protos/ProtoSerializer.kt
new file mode 100644
index 0000000..29ee671
--- /dev/null
+++ b/datastore/datastore-proto/src/main/java/androidx/datastore/protos/ProtoSerializer.kt
@@ -0,0 +1,51 @@
+/*
+ * 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.datastore.protos
+
+import androidx.datastore.DataStore
+import com.google.protobuf.ExtensionRegistryLite
+import com.google.protobuf.InvalidProtocolBufferException
+import com.google.protobuf.MessageLite
+import java.io.InputStream
+import java.io.OutputStream
+
+/** Serializer for using DataStore with protos. */
+internal class ProtoSerializer<T : MessageLite>(
+    /** The default proto of this type, obtained via {@code T.getDefaultInstance()} */
+    override val defaultValue: T,
+    /**
+     *  Set the extensionRegistryLite to use when deserializing T. If no extension registry is
+     *  necessary, use {@code ExtensionRegistryLite.getEmptyRegistry()}.
+     */
+    private val extensionRegistryLite: ExtensionRegistryLite
+) : DataStore.Serializer<T> {
+
+    @Suppress("UNCHECKED_CAST")
+    override fun readFrom(input: InputStream): T {
+        try {
+            return defaultValue.parserForType.parseFrom(input, extensionRegistryLite) as T
+        } catch (invalidProtocolBufferException: InvalidProtocolBufferException) {
+            throw DataStore.Serializer.CorruptionException(
+                "Cannot read proto.", invalidProtocolBufferException
+            )
+        }
+    }
+
+    override fun writeTo(t: T, output: OutputStream) {
+        t.writeTo(output)
+    }
+}
\ No newline at end of file
diff --git a/datastore/datastore-proto/src/test/java/androidx/datastore/protos/ProtoSerializerTest.kt b/datastore/datastore-proto/src/test/java/androidx/datastore/protos/ProtoSerializerTest.kt
new file mode 100644
index 0000000..3f74bb0
--- /dev/null
+++ b/datastore/datastore-proto/src/test/java/androidx/datastore/protos/ProtoSerializerTest.kt
@@ -0,0 +1,96 @@
+/*
+ * 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.datastore.protos
+
+import androidx.datastore.DataStore
+import org.junit.Test
+import androidx.testing.TestMessageProto.FooProto
+import androidx.testing.TestMessageProto.ExtendableProto
+import androidx.testing.TestMessageProto.ExtensionProto
+import androidx.testutils.assertThrows
+import com.google.common.truth.Truth.assertThat
+import com.google.protobuf.ExtensionRegistryLite
+import org.junit.Rule
+import org.junit.rules.TemporaryFolder
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class ProtoSerializerTest {
+    @get:Rule
+    val temporaryFolder = TemporaryFolder()
+
+    @Test
+    fun testReadWriteProto() {
+        val file = temporaryFolder.newFile()
+
+        val fooProtoWithText = FooProto.newBuilder().setText("abc").build()
+
+        val protoSerializer = ProtoSerializer<FooProto>(
+            FooProto.getDefaultInstance(),
+            ExtensionRegistryLite.getEmptyRegistry()
+        )
+
+        file.outputStream().use {
+            protoSerializer.writeTo(fooProtoWithText, it)
+        }
+
+        val readProto = file.inputStream().use { protoSerializer.readFrom(it) }
+
+        assertThat(readProto).isEqualTo(fooProtoWithText)
+    }
+
+    @Test
+    fun testReadWriteProtoWithExtension() {
+        val file = temporaryFolder.newFile()
+
+        val registry = ExtensionRegistryLite.newInstance()
+        registry.add(ExtensionProto.extension)
+
+        val protoSerializer = ProtoSerializer<ExtendableProto>(
+            ExtendableProto.getDefaultInstance(),
+            registry
+        )
+
+        val extendedProto = ExtendableProto.newBuilder().setExtension(
+            ExtensionProto.extension,
+            ExtensionProto.newBuilder().setFoo(FooProto.newBuilder().setText("abc").build()).build()
+        ).build()
+
+        file.outputStream().use {
+            protoSerializer.writeTo(extendedProto, it)
+        }
+
+        val readProto = file.inputStream().use { protoSerializer.readFrom(it) }
+        assertThat(readProto).isEqualTo(extendedProto)
+    }
+
+    @Test
+    fun testThrowsCorruptionException() {
+        val file = temporaryFolder.newFile()
+        file.writeBytes(byteArrayOf(0x00, 0x02)) // Protos cannot start with 0x00.
+
+        val protoSerializer = ProtoSerializer<FooProto>(
+            FooProto.getDefaultInstance(),
+            ExtensionRegistryLite.getEmptyRegistry()
+        )
+
+        assertThrows<DataStore.Serializer.CorruptionException> {
+            file.inputStream().use { protoSerializer.readFrom(it) }
+        }
+    }
+}
\ No newline at end of file
diff --git a/datastore/datastore-proto/src/test/proto/test.proto b/datastore/datastore-proto/src/test/proto/test.proto
new file mode 100644
index 0000000..5a79dd2
--- /dev/null
+++ b/datastore/datastore-proto/src/test/proto/test.proto
@@ -0,0 +1,27 @@
+// Protos for use in tests
+syntax = "proto2";
+
+package androidx.testing;
+
+option java_package = "androidx.testing";
+option java_outer_classname = "TestMessageProto";
+
+message FooProto {
+  optional string text = 1;
+  optional bool boolean = 2;
+  optional int32 integer = 3;
+  optional bytes bytes = 4;
+}
+
+message ExtendableProto {
+  extensions 1000 to max;
+}
+
+message ExtensionProto {
+  extend ExtendableProto {
+    optional ExtensionProto extension = 226219688;
+  }
+
+  optional FooProto foo = 1;
+}
+
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentTestUtil.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentTestUtil.kt
index cc8172e..6490a091 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentTestUtil.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentTestUtil.kt
@@ -118,39 +118,7 @@
     } else {
         assertThat(transition.capturedEpicenter).isEqualTo(epicenter)
     }
-    val targets = transition.trackedTargets
-    val sb = StringBuilder()
-    sb.append("Expected: [")
-        .append(expected.size)
-        .append("] {")
-    var isFirst = true
-    for (view in expected) {
-        if (isFirst) {
-            isFirst = false
-        } else {
-            sb.append(", ")
-        }
-        sb.append(view)
-    }
-    sb.append("}, but got: [")
-        .append(targets.size)
-        .append("] {")
-    isFirst = true
-    for (view in targets) {
-        if (isFirst) {
-            isFirst = false
-        } else {
-            sb.append(", ")
-        }
-        sb.append(view)
-    }
-    sb.append("}")
-    val errorMessage = sb.toString()
-
-    assertWithMessage(errorMessage).that(targets.size).isEqualTo(expected.size)
-    for (view in expected) {
-        assertWithMessage(errorMessage).that(targets.contains(view)).isTrue()
-    }
+    assertThat(transition.trackedTargets).containsExactlyElementsIn(expected)
     transition.clearTargets()
 }
 
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentTransitionTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentTransitionTest.kt
index 10a644c..01fa0cf 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentTransitionTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentTransitionTest.kt
@@ -79,7 +79,7 @@
         // enter transition
         val fragment = setupInitialFragment()
         val blue = activityRule.findBlue()
-        val green = activityRule.findBlue()
+        val green = activityRule.findGreen()
 
         // exit transition
         fragmentManager.beginTransaction()
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/DefaultSpecialEffectsController.java b/fragment/fragment/src/main/java/androidx/fragment/app/DefaultSpecialEffectsController.java
index 3409abe..16448d8 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/DefaultSpecialEffectsController.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/DefaultSpecialEffectsController.java
@@ -19,6 +19,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.content.Context;
+import android.graphics.Rect;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.animation.Animation;
@@ -27,6 +28,7 @@
 import androidx.annotation.Nullable;
 import androidx.collection.ArrayMap;
 import androidx.core.os.CancellationSignal;
+import androidx.core.view.OneShotPreDrawListener;
 import androidx.core.view.ViewCompat;
 import androidx.core.view.ViewGroupCompat;
 
@@ -262,13 +264,16 @@
 
         // Now find the shared element transition if it exists
         Object sharedElementTransition = null;
-        HashMap<String, String> nameOverrides = new HashMap<>();
+        View firstOutEpicenterView = null;
+        boolean hasLastInEpicenter = false;
+        final Rect lastInEpicenterRect = new Rect();
+        ArrayList<View> sharedElementFirstOutViews = new ArrayList<>();
+        ArrayList<View> sharedElementLastInViews = new ArrayList<>();
         for (final TransitionInfo transitionInfo : transitionInfos) {
             boolean hasSharedElementTransition = transitionInfo.hasSharedElementTransition();
             if (hasSharedElementTransition) {
                 sharedElementTransition = transitionImpl.cloneTransition(
                         transitionInfo.getSharedElementTransition());
-                transitionImpl.addTarget(sharedElementTransition, nonExistentView);
                 Fragment sharedElementFragment = transitionInfo.getOperation().getFragment();
                 // A pop means returning from the target names to the source names
                 // so we have to swap the source/target sets
@@ -278,9 +283,70 @@
                 ArrayList<String> exitingNames = isPop
                         ? sharedElementFragment.getSharedElementSourceNames()
                         : sharedElementFragment.getSharedElementTargetNames();
-                for (int index = 0; index < enteringNames.size(); index++) {
-                    nameOverrides.put(enteringNames.get(index), exitingNames.get(index));
+                if (firstOut != null) {
+                    // Capture all of the Views from the firstOut fragment that are
+                    // part of the shared element transition
+                    ArrayMap<String, View> firstOutViews = new ArrayMap<>();
+                    findNamedViews(firstOutViews, firstOut.getFragment().mView);
+                    // TODO call through to onMapSharedElements
+                    firstOutViews.retainAll(exitingNames);
+
+                    // Find all views under the shared element views and add them
+                    // to the shared element transition
+                    for (View sharedElementView : firstOutViews.values()) {
+                        captureTransitioningViews(sharedElementFirstOutViews,
+                                sharedElementView);
+                    }
+
+                    // Compute the epicenter of the firstOut transition
+                    if (!exitingNames.isEmpty()) {
+                        String epicenterViewName = exitingNames.get(0);
+                        firstOutEpicenterView = firstOutViews.get(epicenterViewName);
+                        transitionImpl.setEpicenter(sharedElementTransition,
+                                firstOutEpicenterView);
+                    }
                 }
+                if (lastIn != null) {
+                    // Capture all of the Views from the lastIn fragment that are
+                    // part of the shared element transition
+                    ArrayMap<String, View> lastInViews = new ArrayMap<>();
+                    findNamedViews(lastInViews, lastIn.getFragment().mView);
+                    // TODO call through to onMapSharedElements
+                    lastInViews.retainAll(enteringNames);
+
+                    // Find all views under the shared element views and add them
+                    // to the shared element transition
+                    for (View sharedElementView : lastInViews.values()) {
+                        captureTransitioningViews(sharedElementLastInViews,
+                                sharedElementView);
+                    }
+
+                    // Compute the epicenter of the firstOut transition
+                    if (!enteringNames.isEmpty()) {
+                        String epicenterViewName = enteringNames.get(0);
+                        final View lastInEpicenterView = lastInViews.get(epicenterViewName);
+                        if (lastInEpicenterView != null) {
+                            hasLastInEpicenter = true;
+                            // We can't set the epicenter here directly since the View might
+                            // not have been laid out as of yet, so instead we set a Rect as
+                            // the epicenter and compute the bounds one frame later
+                            final FragmentTransitionImpl impl = transitionImpl;
+                            OneShotPreDrawListener.add(getContainer(), new Runnable() {
+                                @Override
+                                public void run() {
+                                    impl.getBoundsOnScreen(lastInEpicenterView,
+                                            lastInEpicenterRect);
+                                }
+                            });
+                        }
+                    }
+                }
+                // Now set the transition's targets
+                ArrayList<View> sharedElementTargets = new ArrayList<>();
+                sharedElementTargets.add(nonExistentView);
+                sharedElementTargets.addAll(sharedElementFirstOutViews);
+                sharedElementTargets.addAll(sharedElementLastInViews);
+                transitionImpl.addTargets(sharedElementTransition, sharedElementTargets);
             }
         }
         ArrayList<View> enteringViews = new ArrayList<>();
@@ -309,31 +375,21 @@
                 captureTransitioningViews(transitioningViews,
                         transitionInfo.getOperation().getFragment().mView);
                 if (involvedInSharedElementTransition) {
-                    ArrayMap<String, View> sharedElementViews = new ArrayMap<>();
-                    findNamedViews(sharedElementViews,
-                            transitionInfo.getOperation().getFragment().mView);
-                    // TODO call through to onMapSharedElements
-                    if (operation == firstOut) {
-                        sharedElementViews.retainAll(nameOverrides.values());
-                    } else {
-                        sharedElementViews.retainAll(nameOverrides.keySet());
-                    }
                     // Remove all of the shared element views from the transition
-                    // by first finding all of the transitioning views under the
-                    // selected shared element views
-                    ArrayList<View> sharedElementTransitioningViews = new ArrayList<>();
-                    for (View sharedElementView : sharedElementViews.values()) {
-                        captureTransitioningViews(sharedElementTransitioningViews,
-                                sharedElementView);
+                    if (operation == firstOut) {
+                        transitioningViews.removeAll(sharedElementFirstOutViews);
+                    } else {
+                        transitioningViews.removeAll(sharedElementLastInViews);
                     }
-                    transitioningViews.removeAll(sharedElementTransitioningViews);
-                    // And add them to the shared element transition
-                    transitionImpl.addTargets(sharedElementTransition,
-                            new ArrayList<>(sharedElementTransitioningViews));
                 }
                 transitionImpl.addTargets(transition, transitioningViews);
                 if (transitionInfo.getOperation().getType().equals(Operation.Type.ADD)) {
                     enteringViews.addAll(transitioningViews);
+                    if (hasLastInEpicenter) {
+                        transitionImpl.setEpicenter(transition, lastInEpicenterRect);
+                    }
+                } else {
+                    transitionImpl.setEpicenter(transition, firstOutEpicenterView);
                 }
                 // Now determine how this transition should be merged together
                 if (transitionInfo.isOverlapAllowed()) {
diff --git a/samples/Support7Demos/src/main/AndroidManifest.xml b/samples/Support7Demos/src/main/AndroidManifest.xml
index 0433f43..4e19e24 100644
--- a/samples/Support7Demos/src/main/AndroidManifest.xml
+++ b/samples/Support7Demos/src/main/AndroidManifest.xml
@@ -228,7 +228,7 @@
 
         <activity android:name=".app.ActionBarActionMode"
                   android:label="@string/action_bar_action_mode"
-                  android:theme="@style/Theme.Custom">
+                  android:theme="@style/Theme.Custom.WithActionMode">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="com.example.android.supportv7.SAMPLE_CODE" />
@@ -331,7 +331,7 @@
 
         <activity android:name=".app.ToolbarActionMode"
                   android:label="@string/toolbar_action_mode"
-                  android:theme="@style/Theme.AppCompat.Light.NoActionBar">
+                  android:theme="@style/Theme.Custom.NoActionBar.WithActionMode">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="com.example.android.supportv7.SAMPLE_CODE" />
diff --git a/samples/Support7Demos/src/main/res/values/colors.xml b/samples/Support7Demos/src/main/res/values/colors.xml
index 49cc256..332e515 100644
--- a/samples/Support7Demos/src/main/res/values/colors.xml
+++ b/samples/Support7Demos/src/main/res/values/colors.xml
@@ -27,4 +27,5 @@
     <color name="color_sky_day">#90F0FF</color>
     <color name="color_sky_night">#3050CF</color>
     <color name="color_sky">@color/color_sky_day</color>
+    <color name="color_grass">#20D024</color>
 </resources>
diff --git a/samples/Support7Demos/src/main/res/values/styles.xml b/samples/Support7Demos/src/main/res/values/styles.xml
index 0892fd3..7a7c66a 100644
--- a/samples/Support7Demos/src/main/res/values/styles.xml
+++ b/samples/Support7Demos/src/main/res/values/styles.xml
@@ -33,6 +33,20 @@
         <item name="android:textColorLink">@color/link_color</item>
     </style>
 
+    <style name="ThemeOverlay.ActionMode" parent="ThemeOverlay.AppCompat.ActionBar">
+        <item name="colorControlNormal">#FF0000</item>
+    </style>
+
+    <style name="Theme.Custom.WithActionMode">
+        <item name="actionModeBackground">@color/color_grass</item>
+        <item name="actionModeTheme">@style/ThemeOverlay.ActionMode</item>
+    </style>
+
+    <style name="Theme.Custom.NoActionBar.WithActionMode" parent="Theme.AppCompat.Light.NoActionBar">
+        <item name="actionModeBackground">@color/color_sky_day</item>
+        <item name="actionModeTheme">@style/ThemeOverlay.ActionMode</item>
+    </style>
+
     <style name="Theme.SampleMediaRouter" parent="Theme.AppCompat">
         <item name="colorPrimary">#fff44336</item>
         <item name="colorPrimaryDark">#d32f2f</item>
diff --git a/ui/ui-core/api/0.1.0-dev12.txt b/ui/ui-core/api/0.1.0-dev12.txt
index 78ceed6..cfca0a1 100644
--- a/ui/ui-core/api/0.1.0-dev12.txt
+++ b/ui/ui-core/api/0.1.0-dev12.txt
@@ -964,6 +964,10 @@
     method public default void onStop(androidx.ui.unit.PxPosition velocity);
   }
 
+  public final class DragSlopExceededGestureFilterKt {
+    method public static androidx.ui.core.Modifier dragSlopExceededGestureFilter(androidx.ui.core.Modifier, kotlin.jvm.functions.Function0<kotlin.Unit> onDragSlopExceeded, kotlin.jvm.functions.Function1<? super androidx.ui.core.Direction,java.lang.Boolean>? canDrag = null);
+  }
+
   public final class GestureUtilsKt {
     method public static boolean anyPointersInBounds(java.util.List<androidx.ui.core.PointerInputChange>, androidx.ui.unit.IntPxSize bounds);
   }
@@ -1029,10 +1033,6 @@
     method public static androidx.ui.core.Modifier tapGestureFilter(androidx.ui.core.Modifier, kotlin.jvm.functions.Function1<? super androidx.ui.unit.PxPosition,kotlin.Unit> onTap);
   }
 
-  public final class TouchSlopExceededGestureFilterKt {
-    method public static androidx.ui.core.Modifier touchSlopExceededGestureFilter(androidx.ui.core.Modifier, kotlin.jvm.functions.Function0<kotlin.Unit> onTouchSlopExceeded, kotlin.jvm.functions.Function1<? super androidx.ui.core.Direction,java.lang.Boolean>? canDrag = null);
-  }
-
 }
 
 package androidx.ui.core.gesture.customevents {
diff --git a/ui/ui-core/api/current.txt b/ui/ui-core/api/current.txt
index 78ceed6..cfca0a1 100644
--- a/ui/ui-core/api/current.txt
+++ b/ui/ui-core/api/current.txt
@@ -964,6 +964,10 @@
     method public default void onStop(androidx.ui.unit.PxPosition velocity);
   }
 
+  public final class DragSlopExceededGestureFilterKt {
+    method public static androidx.ui.core.Modifier dragSlopExceededGestureFilter(androidx.ui.core.Modifier, kotlin.jvm.functions.Function0<kotlin.Unit> onDragSlopExceeded, kotlin.jvm.functions.Function1<? super androidx.ui.core.Direction,java.lang.Boolean>? canDrag = null);
+  }
+
   public final class GestureUtilsKt {
     method public static boolean anyPointersInBounds(java.util.List<androidx.ui.core.PointerInputChange>, androidx.ui.unit.IntPxSize bounds);
   }
@@ -1029,10 +1033,6 @@
     method public static androidx.ui.core.Modifier tapGestureFilter(androidx.ui.core.Modifier, kotlin.jvm.functions.Function1<? super androidx.ui.unit.PxPosition,kotlin.Unit> onTap);
   }
 
-  public final class TouchSlopExceededGestureFilterKt {
-    method public static androidx.ui.core.Modifier touchSlopExceededGestureFilter(androidx.ui.core.Modifier, kotlin.jvm.functions.Function0<kotlin.Unit> onTouchSlopExceeded, kotlin.jvm.functions.Function1<? super androidx.ui.core.Direction,java.lang.Boolean>? canDrag = null);
-  }
-
 }
 
 package androidx.ui.core.gesture.customevents {
diff --git a/ui/ui-core/api/public_plus_experimental_0.1.0-dev12.txt b/ui/ui-core/api/public_plus_experimental_0.1.0-dev12.txt
index 1ec43c6..b7a6c6c 100644
--- a/ui/ui-core/api/public_plus_experimental_0.1.0-dev12.txt
+++ b/ui/ui-core/api/public_plus_experimental_0.1.0-dev12.txt
@@ -966,6 +966,10 @@
     method public default void onStop(androidx.ui.unit.PxPosition velocity);
   }
 
+  public final class DragSlopExceededGestureFilterKt {
+    method public static androidx.ui.core.Modifier dragSlopExceededGestureFilter(androidx.ui.core.Modifier, kotlin.jvm.functions.Function0<kotlin.Unit> onDragSlopExceeded, kotlin.jvm.functions.Function1<? super androidx.ui.core.Direction,java.lang.Boolean>? canDrag = null);
+  }
+
   public final class GestureUtilsKt {
     method public static boolean anyPointersInBounds(java.util.List<androidx.ui.core.PointerInputChange>, androidx.ui.unit.IntPxSize bounds);
   }
@@ -1031,10 +1035,6 @@
     method public static androidx.ui.core.Modifier tapGestureFilter(androidx.ui.core.Modifier, kotlin.jvm.functions.Function1<? super androidx.ui.unit.PxPosition,kotlin.Unit> onTap);
   }
 
-  public final class TouchSlopExceededGestureFilterKt {
-    method public static androidx.ui.core.Modifier touchSlopExceededGestureFilter(androidx.ui.core.Modifier, kotlin.jvm.functions.Function0<kotlin.Unit> onTouchSlopExceeded, kotlin.jvm.functions.Function1<? super androidx.ui.core.Direction,java.lang.Boolean>? canDrag = null);
-  }
-
 }
 
 package androidx.ui.core.gesture.customevents {
diff --git a/ui/ui-core/api/public_plus_experimental_current.txt b/ui/ui-core/api/public_plus_experimental_current.txt
index 1ec43c6..b7a6c6c 100644
--- a/ui/ui-core/api/public_plus_experimental_current.txt
+++ b/ui/ui-core/api/public_plus_experimental_current.txt
@@ -966,6 +966,10 @@
     method public default void onStop(androidx.ui.unit.PxPosition velocity);
   }
 
+  public final class DragSlopExceededGestureFilterKt {
+    method public static androidx.ui.core.Modifier dragSlopExceededGestureFilter(androidx.ui.core.Modifier, kotlin.jvm.functions.Function0<kotlin.Unit> onDragSlopExceeded, kotlin.jvm.functions.Function1<? super androidx.ui.core.Direction,java.lang.Boolean>? canDrag = null);
+  }
+
   public final class GestureUtilsKt {
     method public static boolean anyPointersInBounds(java.util.List<androidx.ui.core.PointerInputChange>, androidx.ui.unit.IntPxSize bounds);
   }
@@ -1031,10 +1035,6 @@
     method public static androidx.ui.core.Modifier tapGestureFilter(androidx.ui.core.Modifier, kotlin.jvm.functions.Function1<? super androidx.ui.unit.PxPosition,kotlin.Unit> onTap);
   }
 
-  public final class TouchSlopExceededGestureFilterKt {
-    method public static androidx.ui.core.Modifier touchSlopExceededGestureFilter(androidx.ui.core.Modifier, kotlin.jvm.functions.Function0<kotlin.Unit> onTouchSlopExceeded, kotlin.jvm.functions.Function1<? super androidx.ui.core.Direction,java.lang.Boolean>? canDrag = null);
-  }
-
 }
 
 package androidx.ui.core.gesture.customevents {
diff --git a/ui/ui-core/api/restricted_0.1.0-dev12.txt b/ui/ui-core/api/restricted_0.1.0-dev12.txt
index 17a1e175..98726d2 100644
--- a/ui/ui-core/api/restricted_0.1.0-dev12.txt
+++ b/ui/ui-core/api/restricted_0.1.0-dev12.txt
@@ -1020,6 +1020,10 @@
     method public default void onStop(androidx.ui.unit.PxPosition velocity);
   }
 
+  public final class DragSlopExceededGestureFilterKt {
+    method public static androidx.ui.core.Modifier dragSlopExceededGestureFilter(androidx.ui.core.Modifier, kotlin.jvm.functions.Function0<kotlin.Unit> onDragSlopExceeded, kotlin.jvm.functions.Function1<? super androidx.ui.core.Direction,java.lang.Boolean>? canDrag = null);
+  }
+
   public final class GestureUtilsKt {
     method public static boolean anyPointersInBounds(java.util.List<androidx.ui.core.PointerInputChange>, androidx.ui.unit.IntPxSize bounds);
   }
@@ -1085,10 +1089,6 @@
     method public static androidx.ui.core.Modifier tapGestureFilter(androidx.ui.core.Modifier, kotlin.jvm.functions.Function1<? super androidx.ui.unit.PxPosition,kotlin.Unit> onTap);
   }
 
-  public final class TouchSlopExceededGestureFilterKt {
-    method public static androidx.ui.core.Modifier touchSlopExceededGestureFilter(androidx.ui.core.Modifier, kotlin.jvm.functions.Function0<kotlin.Unit> onTouchSlopExceeded, kotlin.jvm.functions.Function1<? super androidx.ui.core.Direction,java.lang.Boolean>? canDrag = null);
-  }
-
 }
 
 package androidx.ui.core.gesture.customevents {
diff --git a/ui/ui-core/api/restricted_current.txt b/ui/ui-core/api/restricted_current.txt
index 17a1e175..98726d2 100644
--- a/ui/ui-core/api/restricted_current.txt
+++ b/ui/ui-core/api/restricted_current.txt
@@ -1020,6 +1020,10 @@
     method public default void onStop(androidx.ui.unit.PxPosition velocity);
   }
 
+  public final class DragSlopExceededGestureFilterKt {
+    method public static androidx.ui.core.Modifier dragSlopExceededGestureFilter(androidx.ui.core.Modifier, kotlin.jvm.functions.Function0<kotlin.Unit> onDragSlopExceeded, kotlin.jvm.functions.Function1<? super androidx.ui.core.Direction,java.lang.Boolean>? canDrag = null);
+  }
+
   public final class GestureUtilsKt {
     method public static boolean anyPointersInBounds(java.util.List<androidx.ui.core.PointerInputChange>, androidx.ui.unit.IntPxSize bounds);
   }
@@ -1085,10 +1089,6 @@
     method public static androidx.ui.core.Modifier tapGestureFilter(androidx.ui.core.Modifier, kotlin.jvm.functions.Function1<? super androidx.ui.unit.PxPosition,kotlin.Unit> onTap);
   }
 
-  public final class TouchSlopExceededGestureFilterKt {
-    method public static androidx.ui.core.Modifier touchSlopExceededGestureFilter(androidx.ui.core.Modifier, kotlin.jvm.functions.Function0<kotlin.Unit> onTouchSlopExceeded, kotlin.jvm.functions.Function1<? super androidx.ui.core.Direction,java.lang.Boolean>? canDrag = null);
-  }
-
 }
 
 package androidx.ui.core.gesture.customevents {
diff --git a/ui/ui-core/integration-tests/ui-core-demos/src/main/java/androidx/ui/core/demos/CoreDemos.kt b/ui/ui-core/integration-tests/ui-core-demos/src/main/java/androidx/ui/core/demos/CoreDemos.kt
index b9a26e3..4b207f9 100644
--- a/ui/ui-core/integration-tests/ui-core-demos/src/main/java/androidx/ui/core/demos/CoreDemos.kt
+++ b/ui/ui-core/integration-tests/ui-core-demos/src/main/java/androidx/ui/core/demos/CoreDemos.kt
@@ -35,7 +35,7 @@
 import androidx.ui.core.demos.gestures.RawDragGestureDetectorDemo
 import androidx.ui.core.demos.gestures.ScaleGestureDetectorDemo
 import androidx.ui.core.demos.gestures.TouchSlopDragGestureDetectorDemo
-import androidx.ui.core.demos.gestures.TouchSlopExceededGestureDetectorDemo
+import androidx.ui.core.demos.gestures.DragSlopExceededGestureFilterDemo
 import androidx.ui.core.demos.keyinput.KeyInputDemo
 import androidx.ui.core.demos.viewinterop.ViewInComposeDemo
 
@@ -50,8 +50,8 @@
         ComposableDemo("TouchSlopDragGestureDetector") { TouchSlopDragGestureDetectorDemo() },
         ComposableDemo("LongPressDragGestureDetector") { LongPressDragGestureDetectorDemo() },
         ComposableDemo("RawDragGestureDetector") { RawDragGestureDetectorDemo() },
-        ComposableDemo("TouchSlopExceededGestureDetector") {
-            TouchSlopExceededGestureDetectorDemo()
+        ComposableDemo("DragSlopExceededGestureFilter") {
+            DragSlopExceededGestureFilterDemo()
         },
         ComposableDemo("ScaleGestureDetector") { ScaleGestureDetectorDemo() }
     )),
diff --git a/ui/ui-core/integration-tests/ui-core-demos/src/main/java/androidx/ui/core/demos/gestures/TouchSlopExceededGestureDetectorDemo.kt b/ui/ui-core/integration-tests/ui-core-demos/src/main/java/androidx/ui/core/demos/gestures/DragSlopExceededGestureFilterDemo.kt
similarity index 66%
rename from ui/ui-core/integration-tests/ui-core-demos/src/main/java/androidx/ui/core/demos/gestures/TouchSlopExceededGestureDetectorDemo.kt
rename to ui/ui-core/integration-tests/ui-core-demos/src/main/java/androidx/ui/core/demos/gestures/DragSlopExceededGestureFilterDemo.kt
index 7a8eb05..82999a0 100644
--- a/ui/ui-core/integration-tests/ui-core-demos/src/main/java/androidx/ui/core/demos/gestures/TouchSlopExceededGestureDetectorDemo.kt
+++ b/ui/ui-core/integration-tests/ui-core-demos/src/main/java/androidx/ui/core/demos/gestures/DragSlopExceededGestureFilterDemo.kt
@@ -21,19 +21,21 @@
 import androidx.ui.core.Alignment
 import androidx.ui.core.Direction
 import androidx.ui.core.Modifier
-import androidx.ui.core.gesture.touchSlopExceededGestureFilter
+import androidx.ui.core.gesture.dragSlopExceededGestureFilter
 import androidx.ui.foundation.Box
+import androidx.ui.foundation.Text
 import androidx.ui.graphics.Color
+import androidx.ui.layout.Column
 import androidx.ui.layout.fillMaxSize
 import androidx.ui.layout.preferredSize
 import androidx.ui.layout.wrapContentSize
 import androidx.ui.unit.dp
 
 /**
- * Simple demo that shows off TouchSlopExceededGestureDetector.
+ * Simple demo that demonstrates the functionality of [dragSlopExceededGestureFilter].
  */
 @Composable
-fun TouchSlopExceededGestureDetectorDemo() {
+fun DragSlopExceededGestureFilterDemo() {
 
     val verticalColor = Color(0xfff44336)
     val horizontalColor = Color(0xff2196f3)
@@ -76,11 +78,24 @@
             horizontalColor
         }
 
-    Box(
-        Modifier.fillMaxSize()
-            .wrapContentSize(Alignment.Center)
-            .touchSlopExceededGestureFilter(onTouchSlopExceeded, canDrag)
-            .preferredSize(96.dp),
-        backgroundColor = color
-    )
+    Column {
+        Text(
+            "Demonstrates functionality of Modifier.dragSlopExceededGestureFilter, which calls " +
+                    "its callback when touch slop has been exceeded by the average distance" +
+                    " change of all pointers.  This also demonstrates controlling which" +
+                    " directions can be dragged to exceed touch slop."
+        )
+        Text(
+            "When red, a drag on the box will turn the box blue only when you drag up or down on" +
+                    " the screen.  When blue, a drag on the box will turn the box red when you" +
+                    " drag to the right or left."
+        )
+        Box(
+            Modifier.fillMaxSize()
+                .wrapContentSize(Alignment.Center)
+                .dragSlopExceededGestureFilter(onTouchSlopExceeded, canDrag)
+                .preferredSize(96.dp),
+            backgroundColor = color
+        )
+    }
 }
diff --git a/ui/ui-core/src/main/java/androidx/ui/core/gesture/DragGestureFilter.kt b/ui/ui-core/src/main/java/androidx/ui/core/gesture/DragGestureFilter.kt
index 584dd61..c178e08 100644
--- a/ui/ui-core/src/main/java/androidx/ui/core/gesture/DragGestureFilter.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/core/gesture/DragGestureFilter.kt
@@ -67,7 +67,7 @@
     //  DragObserver.onStart but if the pointer doesn't move and releases, (or if cancel is called)
     //  The appropriate callbacks to DragObserver will not be called.
     rawDragGestureFilter(glue.rawDragObserver, glue::enabledOrStarted)
-        .touchSlopExceededGestureFilter(glue::enableDrag, canDrag)
+        .dragSlopExceededGestureFilter(glue::enableDrag, canDrag)
         .rawPressStartGestureFilter(
             glue::startDrag,
             startDragImmediately,
diff --git a/ui/ui-core/src/main/java/androidx/ui/core/gesture/DragSlopExceededGestureFilter.kt b/ui/ui-core/src/main/java/androidx/ui/core/gesture/DragSlopExceededGestureFilter.kt
new file mode 100644
index 0000000..51cce8f
--- /dev/null
+++ b/ui/ui-core/src/main/java/androidx/ui/core/gesture/DragSlopExceededGestureFilter.kt
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.ui.core.gesture
+
+import androidx.compose.remember
+import androidx.ui.core.DensityAmbient
+import androidx.ui.core.Direction
+import androidx.ui.core.Modifier
+import androidx.ui.core.PointerEventPass
+import androidx.ui.core.PointerInputChange
+import androidx.ui.core.changedToUpIgnoreConsumed
+import androidx.ui.core.composed
+import androidx.ui.core.pointerinput.PointerInputFilter
+import androidx.ui.core.positionChange
+import androidx.ui.geometry.Offset
+import androidx.ui.unit.IntPxSize
+import androidx.ui.unit.Px
+import androidx.ui.unit.PxPosition
+
+/**
+ * This gesture filter detects when the average distance change of all pointers surpasses touch
+ * slop.
+ *
+ * The value of touch slop is currently defined internally as the constant [TouchSlop].
+ *
+ * @param onDragSlopExceeded Called when touch slop is exceeded in a supported direction. See
+ * [canDrag].
+ * @param canDrag Set to limit the directions under which touch slop can be exceeded. Return true
+ * if you want a drag to be started due to the touch slop being surpassed in the given [Direction].
+ * If [canDrag] is not provided, touch slop will be able to be exceeded in all directions.
+ */
+fun Modifier.dragSlopExceededGestureFilter(
+    onDragSlopExceeded: () -> Unit,
+    canDrag: ((Direction) -> Boolean)? = null
+): Modifier = composed {
+    val touchSlop = with(DensityAmbient.current) { TouchSlop.toPx() }
+    val filter = remember { DragSlopExceededGestureFilter(touchSlop) }
+    filter.canDrag = canDrag
+    filter.>
+    PointerInputModifierImpl(filter)
+}
+
+internal class DragSlopExceededGestureFilter(
+    private val touchSlop: Px
+) : PointerInputFilter() {
+    private var dxForPass = 0f
+    private var dyForPass = 0f
+    private var dxUnderSlop = 0f
+    private var dyUnderSlop = 0f
+    private var passedSlop = false
+
+    var canDrag: ((Direction) -> Boolean)? = null
+    var onDragSlopExceeded: () -> Unit = {}
+
+    override fun onPointerInput(
+        changes: List<PointerInputChange>,
+        pass: PointerEventPass,
+        bounds: IntPxSize
+    ): List<PointerInputChange> {
+
+        if (!passedSlop &&
+            (pass == PointerEventPass.PostUp || pass == PointerEventPass.PostDown)
+        ) {
+            // Get current average change.
+            val averagePositionChange = getAveragePositionChange(changes)
+            val dx = averagePositionChange.dx
+            val dy = averagePositionChange.dy
+
+            // Track changes during postUp and during postDown.  This allows for fancy dragging
+            // due to a parent being dragged and will likely be removed.
+            // TODO(b/157087973): Likely remove this two pass complexity.
+            if (pass == PointerEventPass.PostUp) {
+                dxForPass = dx
+                dyForPass = dy
+                dxUnderSlop += dx
+                dyUnderSlop += dy
+            } else {
+                dxUnderSlop += dx - dxForPass
+                dyUnderSlop += dy - dyForPass
+            }
+
+            // Map the distance to the direction enum for a call to canDrag.
+            val directionX = averagePositionChange.horizontalDirection()
+            val directionY = averagePositionChange.verticalDirection()
+
+            val canDragX = directionX != null && canDrag?.invoke(directionX) ?: true
+            val canDragY = directionY != null && canDrag?.invoke(directionY) ?: true
+
+            val passedSlopX = canDragX && Math.abs(dxUnderSlop) > touchSlop.value
+            val passedSlopY = canDragY && Math.abs(dyUnderSlop) > touchSlop.value
+
+            if (passedSlopX || passedSlopY) {
+                passedSlop = true
+                onDragSlopExceeded.invoke()
+            } else {
+                // If we have passed slop in a direction that we can't drag in, we should reset
+                // our tracking back to zero so that a user doesn't have to later scroll the slop
+                // + the extra distance they scrolled in the wrong direction.
+                if (!canDragX &&
+                    ((directionX == Direction.LEFT && dxUnderSlop < 0) ||
+                            (directionX == Direction.RIGHT && dxUnderSlop > 0))
+                ) {
+                    dxUnderSlop = 0f
+                }
+                if (!canDragY &&
+                    ((directionY == Direction.UP && dyUnderSlop < 0) ||
+                            (directionY == Direction.DOWN && dyUnderSlop > 0))
+                ) {
+                    dyUnderSlop = 0f
+                }
+            }
+        }
+
+        if (pass == PointerEventPass.PostDown &&
+            changes.all { it.changedToUpIgnoreConsumed() }
+        ) {
+            reset()
+        }
+        return changes
+    }
+
+    override fun onCancel() {
+        reset()
+    }
+
+    private fun reset() {
+        passedSlop = false
+        dxForPass = 0f
+        dyForPass = 0f
+        dxUnderSlop = 0f
+        dyUnderSlop = 0f
+    }
+}
+
+/**
+ * Get's the average distance change of all pointers as an Offset.
+ */
+private fun getAveragePositionChange(changes: List<PointerInputChange>): Offset {
+    val sum = changes.fold(PxPosition.Origin) { sum, change ->
+        sum + change.positionChange()
+    }
+    val sizeAsFloat = changes.size.toFloat()
+    // TODO(b/148980115): Once PxPosition is removed, sum will be an Offset, and this line can
+    //  just be straight division.
+    return Offset(sum.x.value / sizeAsFloat, sum.y.value / sizeAsFloat)
+}
+
+/**
+ * Maps an [Offset] value to a horizontal [Direction].
+ */
+private fun Offset.horizontalDirection() =
+    when {
+        this.dx < 0f -> Direction.LEFT
+        this.dx > 0f -> Direction.RIGHT
+        else -> null
+    }
+
+/**
+ * Maps a [Offset] value to a vertical [Direction].
+ */
+private fun Offset.verticalDirection() =
+    when {
+        this.dy < 0f -> Direction.UP
+        this.dy > 0f -> Direction.DOWN
+        else -> null
+    }
\ No newline at end of file
diff --git a/ui/ui-core/src/main/java/androidx/ui/core/gesture/TouchSlopExceededGestureFilter.kt b/ui/ui-core/src/main/java/androidx/ui/core/gesture/TouchSlopExceededGestureFilter.kt
deleted file mode 100644
index 88c64ab..0000000
--- a/ui/ui-core/src/main/java/androidx/ui/core/gesture/TouchSlopExceededGestureFilter.kt
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.ui.core.gesture
-
-import androidx.compose.remember
-import androidx.ui.core.DensityAmbient
-import androidx.ui.core.Direction
-import androidx.ui.core.Modifier
-import androidx.ui.core.PointerEventPass
-import androidx.ui.core.PointerId
-import androidx.ui.core.PointerInputChange
-import androidx.ui.core.changedToDownIgnoreConsumed
-import androidx.ui.core.changedToUpIgnoreConsumed
-import androidx.ui.core.composed
-import androidx.ui.core.pointerinput.PointerInputFilter
-import androidx.ui.core.positionChange
-import androidx.ui.unit.IntPxSize
-import androidx.ui.unit.Px
-import androidx.ui.util.fastForEach
-
-/**
- * This gesture filter detects when at least one pointer has moved far enough to exceed touch slop.
- *
- * The value of touch slop is currently defined internally as the constant [TouchSlop].
- *
- * @param onTouchSlopExceeded Lamba that will be called when touch slop by at least 1 pointer has
- * been exceeded in a supported direction. See [canDrag].
- * @param canDrag Set to limit the directions under which touch slop can be exceeded. Return true
- * if you want a drag to be started due to the touch slop being surpassed in the given [Direction].
- * If [canDrag] is not provided, touch slop will be able to be exceeded in all directions.
- */
-fun Modifier.touchSlopExceededGestureFilter(
-    onTouchSlopExceeded: () -> Unit,
-    canDrag: ((Direction) -> Boolean)? = null
-): Modifier = composed {
-    val touchSlop = with(DensityAmbient.current) { TouchSlop.toPx() }
-    val filter = remember { TouchSlopExceededGestureFilter(touchSlop) }
-    filter.canDrag = canDrag
-    filter.>
-    PointerInputModifierImpl(filter)
-}
-
-// TODO(shepshapard): Shouldn't touchSlop be Px and not IntPx? What if the density bucket of the
-//  device is not a whole number?
-internal class TouchSlopExceededGestureFilter(
-    private val touchSlop: Px
-) : PointerInputFilter() {
-    private val pointerTrackers: MutableMap<PointerId, PointerTrackingData> = mutableMapOf()
-    private var passedSlop = false
-
-    var canDrag: ((Direction) -> Boolean)? = null
-    var onTouchSlopExceeded: () -> Unit = {}
-
-    override fun onPointerInput(
-        changes: List<PointerInputChange>,
-        pass: PointerEventPass,
-        bounds: IntPxSize
-    ): List<PointerInputChange> {
-        if (pass == PointerEventPass.PostUp) {
-            changes.fastForEach {
-                if (it.changedToUpIgnoreConsumed()) {
-                    pointerTrackers.remove(it.id)
-                } else if (it.changedToDownIgnoreConsumed()) {
-                    pointerTrackers[it.id] = PointerTrackingData()
-                }
-            }
-
-            if (!passedSlop) {
-                pointerTrackers.forEach {
-                    it.value.dxForPass = 0f
-                    it.value.dyForPass = 0f
-                }
-            }
-        }
-
-        if (!passedSlop &&
-            (pass == PointerEventPass.PostUp || pass == PointerEventPass.PostDown)
-        ) {
-
-            changes.filter { it.current.down && !it.changedToDownIgnoreConsumed() }.fastForEach {
-
-                if (!passedSlop) {
-
-                    // TODO(shepshapard): handle the case that the pointerTrackingData is null,
-                    //  either with an exception or a logged error, or something else. It should
-                    //  only ever be able to be null at this point if we received a "move"
-                    //  change for a pointer before we received an change that the pointer
-                    //  became "down".
-                    val pointerTracker: PointerTrackingData = pointerTrackers[it.id]!!
-
-                    val positionChanged = it.positionChange()
-                    val dx = positionChanged.x.value
-                    val dy = positionChanged.y.value
-
-                    // TODO(shepshapard): I believe the logic in this block could be simplified
-                    //   to be much more clear.  Will need to revisit. The need to make
-                    //   improvements may be rendered obsolete with upcoming changes however.
-
-                    val directionX = when {
-                        dx == 0f -> null
-                        dx < 0f -> Direction.LEFT
-                        else -> Direction.RIGHT
-                    }
-                    val directionY = when {
-                        dy == 0f -> null
-                        dy < 0f -> Direction.UP
-                        else -> Direction.DOWN
-                    }
-
-                    val internalCanDrag = canDrag
-
-                    val canDragX =
-                        directionX != null &&
-                                (internalCanDrag == null || internalCanDrag.invoke(directionX))
-                    val canDragY =
-                        directionY != null &&
-                                (internalCanDrag == null || internalCanDrag.invoke(directionY))
-
-                    if (pass == PointerEventPass.PostUp) {
-                        pointerTracker.dxForPass = dx
-                        pointerTracker.dyForPass = dy
-                        pointerTracker.dxUnderSlop += dx
-                        pointerTracker.dyUnderSlop += dy
-                    } else {
-                        pointerTracker.dxUnderSlop += dx - pointerTracker.dxForPass
-                        pointerTracker.dyUnderSlop += dy - pointerTracker.dyForPass
-                    }
-
-                    val passedSlopX =
-                        canDragX && Math.abs(pointerTracker.dxUnderSlop) > touchSlop.value
-                    val passedSlopY =
-                        canDragY && Math.abs(pointerTracker.dyUnderSlop) > touchSlop.value
-
-                    if (passedSlopX || passedSlopY) {
-                        passedSlop = true
-                        onTouchSlopExceeded.invoke()
-                    } else {
-                        if (!canDragX &&
-                            ((directionX == Direction.LEFT &&
-                                    pointerTracker.dxUnderSlop < 0) ||
-                                    (directionX == Direction.RIGHT &&
-                                            pointerTracker.dxUnderSlop > 0))
-                        ) {
-                            pointerTracker.dxUnderSlop = 0f
-                        }
-                        if (!canDragY &&
-                            ((directionY == Direction.LEFT &&
-                                    pointerTracker.dyUnderSlop < 0) ||
-                                    (directionY == Direction.DOWN &&
-                                            pointerTracker.dyUnderSlop > 0))
-                        ) {
-                            pointerTracker.dyUnderSlop = 0f
-                        }
-                    }
-                }
-            }
-        }
-
-        if (passedSlop &&
-            pass == PointerEventPass.PostDown &&
-            changes.all { it.changedToUpIgnoreConsumed() }
-        ) {
-            passedSlop = false
-        }
-        return changes
-    }
-
-    override fun onCancel() {
-        pointerTrackers.clear()
-        passedSlop = false
-    }
-
-    internal data class PointerTrackingData(
-        var dxUnderSlop: Float = 0f,
-        var dyUnderSlop: Float = 0f,
-        var dxForPass: Float = 0f,
-        var dyForPass: Float = 0f
-    )
-}
\ No newline at end of file
diff --git a/ui/ui-core/src/main/java/androidx/ui/core/pointerinput/HitPathTracker.kt b/ui/ui-core/src/main/java/androidx/ui/core/pointerinput/HitPathTracker.kt
index 768741b..2999035 100644
--- a/ui/ui-core/src/main/java/androidx/ui/core/pointerinput/HitPathTracker.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/core/pointerinput/HitPathTracker.kt
@@ -411,6 +411,7 @@
             }
 
         if (relevantChanges.isEmpty()) {
+            // If there are not relevant changes, there is nothing to process so return.
             return false
         }
 
diff --git a/ui/ui-core/src/test/java/androidx/ui/core/gesture/TouchSlopExceededGestureFilterTest.kt b/ui/ui-core/src/test/java/androidx/ui/core/gesture/DragSlopExceededGestureFilterTest.kt
similarity index 86%
rename from ui/ui-core/src/test/java/androidx/ui/core/gesture/TouchSlopExceededGestureFilterTest.kt
rename to ui/ui-core/src/test/java/androidx/ui/core/gesture/DragSlopExceededGestureFilterTest.kt
index 1fb74f0..c5e83ab 100644
--- a/ui/ui-core/src/test/java/androidx/ui/core/gesture/TouchSlopExceededGestureFilterTest.kt
+++ b/ui/ui-core/src/test/java/androidx/ui/core/gesture/DragSlopExceededGestureFilterTest.kt
@@ -46,29 +46,29 @@
 private const val TestTouchSlop = 10
 
 @RunWith(JUnit4::class)
-class TouchSlopExceededGestureFilterTest {
+class DragSlopExceededGestureFilterTest {
 
-    private val onTouchSlopExceeded: () -> Unit = { onTouchSlopExceededCallCount++ }
+    private val onDragSlopExceeded: () -> Unit = { onDragSlopExceededCallCount++ }
     private val canDrag: (Direction) -> Boolean = { direction ->
         canDragDirections.add(direction)
         canDragReturn
     }
-    private var onTouchSlopExceededCallCount: Int = 0
+    private var onDragSlopExceededCallCount: Int = 0
     private var canDragReturn = false
     private var canDragDirections: MutableList<Direction> = mutableListOf()
-    private lateinit var filter: TouchSlopExceededGestureFilter
+    private lateinit var filter: DragSlopExceededGestureFilter
 
     private val TinyNum = .01f
 
     @Before
     fun setup() {
-        >
+        >
         canDragReturn = true
         canDragDirections.clear()
         filter =
-            TouchSlopExceededGestureFilter(TestTouchSlop.px)
+            DragSlopExceededGestureFilter(TestTouchSlop.px)
         filter.canDrag = canDrag
-        filter.>
+        filter.>
     }
 
     // Verify the circumstances under which canDrag should not be called.
@@ -269,7 +269,7 @@
         )
         filter::onPointerInput.invokeOverAllPasses(move)
 
-        assertThat(onTouchSlopExceededCallCount).isEqualTo(0)
+        assertThat(onDragSlopExceededCallCount).isEqualTo(0)
     }
 
     @Test
@@ -286,7 +286,7 @@
         )
         filter::onPointerInput.invokeOverAllPasses(move)
 
-        assertThat(onTouchSlopExceededCallCount).isEqualTo(0)
+        assertThat(onDragSlopExceededCallCount).isEqualTo(0)
     }
 
     @Test
@@ -300,7 +300,7 @@
 
         // Assert
 
-        assertThat(onTouchSlopExceededCallCount).isEqualTo(0)
+        assertThat(onDragSlopExceededCallCount).isEqualTo(0)
     }
 
     @Test
@@ -327,7 +327,7 @@
 
         // Assert
 
-        assertThat(onTouchSlopExceededCallCount).isEqualTo(1)
+        assertThat(onDragSlopExceededCallCount).isEqualTo(1)
     }
 
     // TODO(b/129701831): This test assumes that if a pointer moves by slop in both x and y, we are
@@ -379,7 +379,7 @@
         change = change.moveTo(90.milliseconds, -slop, -slop)
         filter::onPointerInput.invokeOverAllPasses(change)
 
-        assertThat(onTouchSlopExceededCallCount).isEqualTo(0)
+        assertThat(onDragSlopExceededCallCount).isEqualTo(0)
     }
 
     // Verify the circumstances under which onTouchSlopExceeded should be called.
@@ -397,7 +397,7 @@
         )
         filter::onPointerInput.invokeOverAllPasses(move)
 
-        assertThat(onTouchSlopExceededCallCount).isEqualTo(1)
+        assertThat(onDragSlopExceededCallCount).isEqualTo(1)
     }
 
     @Test
@@ -418,7 +418,7 @@
         )
         filter::onPointerInput.invokeOverAllPasses(move2)
 
-        assertThat(onTouchSlopExceededCallCount).isEqualTo(1)
+        assertThat(onDragSlopExceededCallCount).isEqualTo(1)
     }
 
     @Test
@@ -437,7 +437,7 @@
         event = event.moveBy(Duration(milliseconds = 10), 0f, beyondSlop)
         filter::onPointerInput.invokeOverAllPasses(event)
 
-        assertThat(onTouchSlopExceededCallCount).isEqualTo(1)
+        assertThat(onDragSlopExceededCallCount).isEqualTo(1)
     }
 
     @Test
@@ -449,7 +449,7 @@
         val move = down.moveBy(Duration(milliseconds = 100), beyondSlop, 0f)
         filter::onPointerInput.invokeOverAllPasses(move)
 
-        assertThat(onTouchSlopExceededCallCount).isEqualTo(1)
+        assertThat(onDragSlopExceededCallCount).isEqualTo(1)
     }
 
     @Test
@@ -465,7 +465,7 @@
             beyondSlop
         )
         // Sanity check that onTouchSlopExceeded has not been called.
-        assertThat(onTouchSlopExceededCallCount).isEqualTo(0)
+        assertThat(onDragSlopExceededCallCount).isEqualTo(0)
 
         canDragReturn = true
         filter::onPointerInput.invokeOverAllPasses(change)
@@ -476,11 +476,11 @@
         )
         filter::onPointerInput.invokeOverAllPasses(change)
 
-        assertThat(onTouchSlopExceededCallCount).isEqualTo(1)
+        assertThat(onDragSlopExceededCallCount).isEqualTo(1)
     }
 
     @Test
-    fun onPointerInputChanges_2PointsMoveInOpposite_onTouchSlopExceededCallOnce() {
+    fun onPointerInputChanges_2PointsMoveInOpposite_onTouchSlopExceededNotCalled() {
 
         // Arrange
 
@@ -506,11 +506,11 @@
 
         // Assert
 
-        assertThat(onTouchSlopExceededCallCount).isEqualTo(1)
+        assertThat(onDragSlopExceededCallCount).isEqualTo(0)
     }
 
     @Test
-    fun onPointerInputChanges_3PointsMoveAverage0_onTouchSlopExceededCallOnce() {
+    fun onPointerInputChanges_3PointsMoveAverage0_onDragSlopExceededNotCalled() {
 
         // Arrange
 
@@ -544,44 +544,113 @@
 
         // Assert
 
-        assertThat(onTouchSlopExceededCallCount).isEqualTo(1)
+        assertThat(onDragSlopExceededCallCount).isEqualTo(0)
     }
 
     @Test
-    fun onPointerInputChanges_5Points1MoveBeyondSlop_onTouchSlopExceededCallOnce() {
+    fun onPointerInputChanges_2Points1MoveJustBeyondSlop_onDragSlopExceededNotCalled() {
 
         // Arrange
 
         val beyondSlop = TestTouchSlop + TinyNum
 
-        val pointers = arrayOf(down(0), down(1), down(2), down(3), down(4))
-        filter::onPointerInput.invokeOverAllPasses(*pointers)
+        var pointer1 = down(0)
+        var pointer2 = down(1)
+        filter::onPointerInput.invokeOverAllPasses(pointer1, pointer2)
 
         // Act
 
         // These movements average to no movement.
-        for (i in 0..3) {
-            pointers[i] = pointers[i].moveBy(
+
+        pointer1 =
+            pointer1.moveBy(
                 Duration(milliseconds = 100),
                 0f,
                 0f
             )
-        }
-        pointers[4] =
-            pointers[4].moveBy(
+        pointer2 =
+            pointer2.moveBy(
                 Duration(milliseconds = 100),
                 beyondSlop * -1,
                 0f
             )
-        filter::onPointerInput.invokeOverAllPasses(*pointers)
+        filter::onPointerInput.invokeOverAllPasses(pointer1, pointer2)
 
         // Assert
 
-        assertThat(onTouchSlopExceededCallCount).isEqualTo(1)
+        assertThat(onDragSlopExceededCallCount).isEqualTo(0)
     }
 
     @Test
-    fun onPointerInputChanges_1PointMovesBeyondSlopAndThenManyTimes_onTouchSlopExceededCallOnce() {
+    fun onPointerInputChanges_2Points1MoveJustUnderTwiceSlop_onDragSlopExceededNotCalled() {
+
+        // Arrange
+
+        val beyondSlop = TestTouchSlop + TinyNum
+
+        var pointer1 = down(0)
+        var pointer2 = down(1)
+        filter::onPointerInput.invokeOverAllPasses(pointer1, pointer2)
+
+        // Act
+
+        // These movements average to no movement.
+
+        pointer1 =
+            pointer1.moveBy(
+                Duration(milliseconds = 100),
+                0f,
+                0f
+            )
+        pointer2 =
+            pointer2.moveBy(
+                Duration(milliseconds = 100),
+                beyondSlop * 2 - 1,
+                0f
+            )
+        filter::onPointerInput.invokeOverAllPasses(pointer1, pointer2)
+
+        // Assert
+
+        assertThat(onDragSlopExceededCallCount).isEqualTo(0)
+    }
+
+    @Test
+    fun onPointerInputChanges_2Points1MoveToTwiceSlop_onDragSlopExceededNotCalled() {
+
+        // Arrange
+
+        val beyondSlop = TestTouchSlop + TinyNum
+
+        var pointer1 = down(0)
+        var pointer2 = down(1)
+        filter::onPointerInput.invokeOverAllPasses(pointer1, pointer2)
+
+        // Act
+
+        // These movements average to no movement.
+
+        pointer1 =
+            pointer1.moveBy(
+                Duration(milliseconds = 100),
+                0f,
+                0f
+            )
+        pointer2 =
+            pointer2.moveBy(
+                Duration(milliseconds = 100),
+                beyondSlop * 2,
+                0f
+            )
+        filter::onPointerInput.invokeOverAllPasses(pointer1, pointer2)
+
+        // Assert
+
+        assertThat(onDragSlopExceededCallCount).isEqualTo(1)
+    }
+
+    @Test
+    fun onPointerInputChanges_1PointMovesBeyondSlopAndThenManyTimes_onDragSlopExceededCallOnce() {
 
         // Arrange
 
@@ -599,11 +668,11 @@
 
         // Assert
 
-        assertThat(onTouchSlopExceededCallCount).isEqualTo(1)
+        assertThat(onDragSlopExceededCallCount).isEqualTo(1)
     }
 
     @Test
-    fun onPointerInputChanges_1ModifiedToMoveBeyondSlopBeforePostUp_onTouchSlopExceededCallOnce() {
+    fun onPointerInputChanges_1ModifiedToMoveBeyondSlopBeforePostUp_onDragSlopExceededCallOnce() {
         val beyondSlop = TestTouchSlop + TinyNum
 
         val down = down(0)
@@ -622,11 +691,11 @@
 
         // Assert
 
-        assertThat(onTouchSlopExceededCallCount).isEqualTo(1)
+        assertThat(onDragSlopExceededCallCount).isEqualTo(1)
     }
 
     @Test
-    fun onPointerInputChanges_1ModedToMoveBeyondSlopBeforePostDown_onTouchSlopExceededCallOnce() {
+    fun onPointerInputChanges_1ModedToMoveBeyondSlopBeforePostDown_onDragSlopExceededCallOnce() {
         val beyondSlop = TestTouchSlop + TinyNum
 
         val down = down(0)
@@ -651,11 +720,11 @@
 
         // Assert
 
-        assertThat(onTouchSlopExceededCallCount).isEqualTo(1)
+        assertThat(onDragSlopExceededCallCount).isEqualTo(1)
     }
 
     @Test
-    fun onPointerInputChanges_moveUnderToPostUpThenModOverToPostDown_onTouchSlopExceededCallOnce() {
+    fun onPointerInputChanges_moveUnderToPostUpThenModOverToPostDown_onDragSlopExceededCallOnce() {
         val halfSlop = TestTouchSlop / 2
         val restOfSlopAndBeyond = TestTouchSlop - halfSlop + TinyNum
 
@@ -681,11 +750,11 @@
 
         // Assert
 
-        assertThat(onTouchSlopExceededCallCount).isEqualTo(1)
+        assertThat(onDragSlopExceededCallCount).isEqualTo(1)
     }
 
     @Test
-    fun onPointerInputChanges_moveBeyondSlopAllPassesUpToPostUp_onTouchSlopExceededCallOnce() {
+    fun onPointerInputChanges_moveBeyondSlopAllPassesUpToPostUp_onDragSlopExceededCallOnce() {
         val beyondSlop = TestTouchSlop + TinyNum
 
         val down = down(0)
@@ -704,7 +773,7 @@
 
         // Assert
 
-        assertThat(onTouchSlopExceededCallCount).isEqualTo(1)
+        assertThat(onDragSlopExceededCallCount).isEqualTo(1)
     }
 
     // Verification that TouchSlopExceededGestureDetector does not consume any changes.
@@ -796,7 +865,7 @@
     // Verification that TouchSlopExceededGestureDetector resets after up correctly.
 
     @Test
-    fun onPointerInputChanges_MoveBeyondUpDownMoveBeyond_onTouchSlopExceededCalledTwice() {
+    fun onPointerInputChanges_MoveBeyondUpDownMoveBeyond_onDragSlopExceededCalledTwice() {
         val beyondSlop = TestTouchSlop + TinyNum
 
         repeat(2) {
@@ -810,13 +879,13 @@
             filter::onPointerInput.invokeOverAllPasses(up)
         }
 
-        assertThat(onTouchSlopExceededCallCount).isEqualTo(2)
+        assertThat(onDragSlopExceededCallCount).isEqualTo(2)
     }
 
     // Verification that cancellation behavior is correct.
 
     @Test
-    fun onCancel_underSlopCancelUnderSlop_onTouchSlopExceededNotCalled() {
+    fun onCancel_underSlopCancelUnderSlop_onDragSlopExceededNotCalled() {
         val underSlop = TestTouchSlop - TinyNum
 
         // Arrange
@@ -847,7 +916,7 @@
 
         // Assert
 
-        assertThat(onTouchSlopExceededCallCount).isEqualTo(0)
+        assertThat(onDragSlopExceededCallCount).isEqualTo(0)
     }
 
     @Test
@@ -882,6 +951,6 @@
 
         // Assert
 
-        assertThat(onTouchSlopExceededCallCount).isEqualTo(2)
+        assertThat(onDragSlopExceededCallCount).isEqualTo(2)
     }
 }
\ No newline at end of file
diff --git a/window/stubs/window-sidecar-release-0.1.0-alpha01.aar b/window/stubs/window-sidecar-release-0.1.0-alpha01.aar
new file mode 100644
index 0000000..50f101d
--- /dev/null
+++ b/window/stubs/window-sidecar-release-0.1.0-alpha01.aar
Binary files differ