title: Drag to dismiss Activity not calling onDrag
body: <p>I'm trying to create an activity which can be dismissed on drag using the example in <a href="https://github.com/nickbutcher/plaid/blob/master/app/src/main/java/io/plaidapp/ui/widget/ElasticDragDismissFrameLayout.java" rel="nofollow noreferrer">Nick Butcher's Plaid app</a>. I have literally used his code as is and the <code>draggableListener</code> does seem to be added, but for some reason, <code>onDrag</code> doesn't seem to be called when I drag on the activity, and therefore the activity doesn't finish.</p>
  
  <h2>ElasticDragDismissFrameLayout.java</h2>
  
  <pre><code>package io.traffitool.ui.widget;
  
  import android.app.Activity;
  import android.content.Context;
  import android.content.res.TypedArray;
  import android.graphics.Color;
  import android.util.AttributeSet;
  import android.util.Log;
  import android.view.View;
  import android.widget.FrameLayout;
  
  import java.util.ArrayList;
  import java.util.List;
  
  import io.traffitool.R;
  import io.traffitool.util.AnimUtils;
  import io.traffitool.util.ColorUtils;
  import io.traffitool.util.ViewUtils;
  
  public class ElasticDragDismissFrameLayout extends FrameLayout {
  
      // configurable attribs
      private float dragDismissDistance = Float.MAX_VALUE;
      private float dragDismissFraction = -1f;
      private float dragDismissScale = 1f;
      private boolean shouldScale = false;
      private float dragElacticity = 0.8f;
  
      // state
      private float totalDrag;
      private boolean draggingDown = false;
      private boolean draggingUp = false;
  
      private List&lt;ElasticDragDismissCallback&gt; callbacks;
  
      public ElasticDragDismissFrameLayout(Context context) {
          this(context, null, 0, 0);
      }
  
      public ElasticDragDismissFrameLayout(Context context, AttributeSet attrs) {
          this(context, attrs, 0, 0);
      }
  
      public ElasticDragDismissFrameLayout(Context context, AttributeSet attrs,
                                           int defStyleAttr) {
          this(context, attrs, defStyleAttr, 0);
      }
  
      public ElasticDragDismissFrameLayout(Context context, AttributeSet attrs,
                                           int defStyleAttr, int defStyleRes) {
          super(context, attrs, defStyleAttr, defStyleRes);
  
          final TypedArray a = getContext().obtainStyledAttributes(
                  attrs, R.styleable.ElasticDragDismissFrameLayout, 0, 0);
  
          if (a.hasValue(R.styleable.ElasticDragDismissFrameLayout_dragDismissDistance)) {
              dragDismissDistance = a.getDimensionPixelSize(R.styleable
                      .ElasticDragDismissFrameLayout_dragDismissDistance, 0);
          } else if (a.hasValue(R.styleable.ElasticDragDismissFrameLayout_dragDismissFraction)) {
              dragDismissFraction = a.getFloat(R.styleable
                      .ElasticDragDismissFrameLayout_dragDismissFraction, dragDismissFraction);
          }
          if (a.hasValue(R.styleable.ElasticDragDismissFrameLayout_dragDismissScale)) {
              dragDismissScale = a.getFloat(R.styleable
                      .ElasticDragDismissFrameLayout_dragDismissScale, dragDismissScale);
              shouldScale = dragDismissScale != 1f;
          }
          if (a.hasValue(R.styleable.ElasticDragDismissFrameLayout_dragElasticity)) {
              dragElacticity = a.getFloat(R.styleable.ElasticDragDismissFrameLayout_dragElasticity,
                      dragElacticity);
          }
          a.recycle();
      }
  
      public static abstract class ElasticDragDismissCallback {
  
          /**
           * Called for each drag event.
           *
           * @param elasticOffset       Indicating the drag offset with elasticity applied i.e. may
           *                            exceed 1.
           * @param elasticOffsetPixels The elastically scaled drag distance in pixels.
           * @param rawOffset           Value from [0, 1] indicating the raw drag offset i.e.
           *                            without elasticity applied. A value of 1 indicates that the
           *                            dismiss distance has been reached.
           * @param rawOffsetPixels     The raw distance the user has dragged
           */
          void onDrag(float elasticOffset, float elasticOffsetPixels,
                      float rawOffset, float rawOffsetPixels) {
              Log.d("EDDFL: ", "onDrag");
          }
  
          /**
           * Called when dragging is released and has exceeded the threshold dismiss distance.
           */
          void onDragDismissed() {
              Log.d("EDDFL: ", "onDragDismissed");
          }
  
      }
  
      @Override
      public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
          return (nestedScrollAxes &amp; View.SCROLL_AXIS_VERTICAL) != 0;
      }
  
      @Override
      public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
          // if we're in a drag gesture and the user reverses up the we should take those events
          if (draggingDown &amp;&amp; dy &gt; 0 || draggingUp &amp;&amp; dy &lt; 0) {
              dragScale(dy);
              consumed[1] = dy;
          }
      }
  
      @Override
      public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
                                 int dxUnconsumed, int dyUnconsumed) {
          dragScale(dyUnconsumed);
      }
  
      @Override
      public void onStopNestedScroll(View child) {
          if (Math.abs(totalDrag) &gt;= dragDismissDistance) {
              dispatchDismissCallback();
          } else { // settle back to natural position
              animate()
                      .translationY(0f)
                      .scaleX(1f)
                      .scaleY(1f)
                      .setDuration(200L)
                      .setInterpolator(AnimUtils.getFastOutSlowInInterpolator(getContext()))
                      .setListener(null)
                      .start();
              totalDrag = 0;
              draggingDown = draggingUp = false;
              dispatchDragCallback(0f, 0f, 0f, 0f);
          }
      }
  
      @Override
      protected void onSizeChanged(int w, int h, int oldw, int oldh) {
          super.onSizeChanged(w, h, oldw, oldh);
          if (dragDismissFraction &gt; 0f) {
              dragDismissDistance = h * dragDismissFraction;
          }
      }
  
      public void addListener(ElasticDragDismissCallback listener) {
          if (callbacks == null) {
              callbacks = new ArrayList&lt;&gt;();
          }
          callbacks.add(listener);
      }
  
      public void removeListener(ElasticDragDismissCallback listener) {
          if (callbacks != null &amp;&amp; callbacks.size() &gt; 0) {
              callbacks.remove(listener);
          }
      }
  
      private void dragScale(int scroll) {
          if (scroll == 0) return;
  
          totalDrag += scroll;
  
          // track the direction &amp; set the pivot point for scaling
          // don't double track i.e. if start dragging down and then reverse, keep tracking as
          // dragging down until they reach the 'natural' position
          if (scroll &lt; 0 &amp;&amp; !draggingUp &amp;&amp; !draggingDown) {
              draggingDown = true;
              if (shouldScale) setPivotY(getHeight());
          } else if (scroll &gt; 0 &amp;&amp; !draggingDown &amp;&amp; !draggingUp) {
              draggingUp = true;
              if (shouldScale) setPivotY(0f);
          }
          // how far have we dragged relative to the distance to perform a dismiss
          // (0–1 where 1 = dismiss distance). Decreasing logarithmically as we approach the limit
          float dragFraction = (float) Math.log10(1 + (Math.abs(totalDrag) / dragDismissDistance));
  
          // calculate the desired translation given the drag fraction
          float dragTo = dragFraction * dragDismissDistance * dragElacticity;
  
          if (draggingUp) {
              // as we use the absolute magnitude when calculating the drag fraction, need to
              // re-apply the drag direction
              dragTo *= -1;
          }
          setTranslationY(dragTo);
  
          if (shouldScale) {
              final float scale = 1 - ((1 - dragDismissScale) * dragFraction);
              setScaleX(scale);
              setScaleY(scale);
          }
  
          // if we've reversed direction and gone past the settle point then clear the flags to
          // allow the list to get the scroll events &amp; reset any transforms
          if ((draggingDown &amp;&amp; totalDrag &gt;= 0)
                  || (draggingUp &amp;&amp; totalDrag &lt;= 0)) {
              totalDrag = dragTo = dragFraction = 0;
              draggingDown = draggingUp = false;
              setTranslationY(0f);
              setScaleX(1f);
              setScaleY(1f);
          }
          dispatchDragCallback(dragFraction, dragTo,
                  Math.min(1f, Math.abs(totalDrag) / dragDismissDistance), totalDrag);
      }
  
      private void dispatchDragCallback(float elasticOffset, float elasticOffsetPixels,
                                        float rawOffset, float rawOffsetPixels) {
          if (callbacks != null &amp;&amp; !callbacks.isEmpty()) {
              for (ElasticDragDismissCallback callback : callbacks) {
                  callback.onDrag(elasticOffset, elasticOffsetPixels,
                          rawOffset, rawOffsetPixels);
              }
          }
      }
  
      private void dispatchDismissCallback() {
          if (callbacks != null &amp;&amp; !callbacks.isEmpty()) {
              for (ElasticDragDismissCallback callback : callbacks) {
                  callback.onDragDismissed();
              }
          }
      }
  
      /**
       * An {@link ElasticDragDismissCallback} which fades system chrome (i.e. status bar and
       * navigation bar) whilst elastic drags are performed and
       * {@link Activity#finishAfterTransition() finishes} the activity when drag dismissed.
       */
      public static class SystemChromeFader extends ElasticDragDismissCallback {
  
          private final Activity activity;
          private final int statusBarAlpha;
          private final int navBarAlpha;
          private final boolean fadeNavBar;
  
          public SystemChromeFader(Activity activity) {
              this.activity = activity;
              statusBarAlpha = Color.alpha(activity.getWindow().getStatusBarColor());
              navBarAlpha = Color.alpha(activity.getWindow().getNavigationBarColor());
              fadeNavBar = ViewUtils.isNavBarOnBottom(activity);
          }
  
          @Override
          public void onDrag(float elasticOffset, float elasticOffsetPixels,
                             float rawOffset, float rawOffsetPixels) {
              if (elasticOffsetPixels &gt; 0) {
                  // dragging downward, fade the status bar in proportion
                  activity.getWindow().setStatusBarColor(ColorUtils.modifyAlpha(activity.getWindow()
                          .getStatusBarColor(), (int) ((1f - rawOffset) * statusBarAlpha)));
              } else if (elasticOffsetPixels == 0) {
                  // reset
                  activity.getWindow().setStatusBarColor(ColorUtils.modifyAlpha(
                          activity.getWindow().getStatusBarColor(), statusBarAlpha));
                  activity.getWindow().setNavigationBarColor(ColorUtils.modifyAlpha(
                          activity.getWindow().getNavigationBarColor(), navBarAlpha));
              } else if (fadeNavBar) {
                  // dragging upward, fade the navigation bar in proportion
                  activity.getWindow().setNavigationBarColor(
                          ColorUtils.modifyAlpha(activity.getWindow().getNavigationBarColor(),
                                  (int) ((1f - rawOffset) * navBarAlpha)));
              }
          }
  
          public void onDragDismissed() {
              activity.finishAfterTransition();
          }
      }
  
  }
  </code></pre>
  
  <h2>ProjectActivity.java</h2>
  
  <pre><code>package io.traffitool.ui;
  
  import android.app.Activity;
  import android.os.Bundle;
  import android.support.v7.widget.RecyclerView;
  import android.util.Log;
  
  
  import butterknife.BindView;
  import butterknife.ButterKnife;
  import io.traffitool.R;
  import io.traffitool.ui.widget.ElasticDragDismissFrameLayout;
  
  public class ProjectActivity extends Activity {
  
      private static final String TAG = "ProjectActivity: ";
  
      @BindView(R.id.draggable_frame) ElasticDragDismissFrameLayout draggableFrame;
      @BindView(R.id.list)RecyclerView list;
  
      private ElasticDragDismissFrameLayout.SystemChromeFader chromeFader;
  
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_project);
          ButterKnife.bind(this);
  
          chromeFader = new ElasticDragDismissFrameLayout.SystemChromeFader(this) {
              @Override
              public void onDragDismissed() {
                  Log.d(TAG, "onDragDismissed");
                  setResultandFinish();
              }
          };
  
          draggableFrame.addListener(chromeFader);
          Log.d(TAG, "draggableListenerAdded");
  
      }
  
  
      void setResultandFinish() {
          Log.d(TAG, "setResultandFinish");
          finish();
      }
  
  }
  </code></pre>
  
  <h2>activity_project.xml</h2>
  
  <pre><code>&lt;?xml version="1.0" encoding="utf-8"?&gt;
  
  
  &lt;io.traffitool.ui.widget.ElasticDragDismissFrameLayout
      xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto"
      xmlns:tools="http://schemas.android.com/tools"
      android:id="@+id/draggable_frame"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:transitionGroup="false"
      app:dragDismissDistance="@dimen/drag_dismiss_distance"
      app:dragDismissScale="0.95"
      tools:context=".ui.ProjectActivity"&gt;
  
          &lt;android.support.v7.widget.RecyclerView
              android:id="@+id/list"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:clipToPadding="false"
              android:orientation="vertical"
              android:paddingBottom="@dimen/padding_normal"
              android:scrollbarStyle="insideOverlay"
              android:scrollbars="vertical"
              app:layoutManager="android.support.v7.widget.LinearLayoutManager" /&gt;
  
  &lt;/io.traffitool.ui.widget.ElasticDragDismissFrameLayout&gt;
  </code></pre>