Bug 1525796 - Ignore close tab action if user is moving the item; r=JanH
authorPetru-Mugurel Lingurar <petru.lingurar@softvision.ro>
Fri, 15 Feb 2019 08:18:33 +0000
changeset 459544 ed60c55e6f005b5a382606ec1d4fbc71fd5272c6
parent 459439 a2bc2cdbcea34ff5e6a5d0576bc36ad5eede0550
child 459545 11f5a8e7853b0d751996a28b8797e7837b198e51
push id35563
push userccoroiu@mozilla.com
push dateSat, 16 Feb 2019 09:36:04 +0000
treeherdermozilla-central@1cfd69d05aa1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersJanH
bugs1525796
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1525796 - Ignore close tab action if user is moving the item; r=JanH Using an ItemTouchHelper for various motion actions means it will have a strong reference to that item's layout and prevent it from being destroyed when the adapter tries to remove it before the animations are finished - the item will be removed from RecyclerView's Adapter but the item's layout will still remain on screen. Differential Revision: https://phabricator.services.mozilla.com/D19673
mobile/android/base/java/org/mozilla/gecko/tabs/TabsGridLayout.java
mobile/android/base/java/org/mozilla/gecko/tabs/TabsLayout.java
mobile/android/base/java/org/mozilla/gecko/tabs/TabsListLayout.java
mobile/android/base/java/org/mozilla/gecko/tabs/TabsTouchHelperCallback.java
--- a/mobile/android/base/java/org/mozilla/gecko/tabs/TabsGridLayout.java
+++ b/mobile/android/base/java/org/mozilla/gecko/tabs/TabsGridLayout.java
@@ -21,23 +21,23 @@ abstract class TabsGridLayout extends Ta
 
         setClipToPadding(false);
         setScrollBarStyle(SCROLLBARS_OUTSIDE_OVERLAY);
 
         setItemAnimator(new TabsGridLayoutAnimator());
 
         final int dragDirections = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.START | ItemTouchHelper.END;
         // A TouchHelper handler for drag and drop and swipe to close.
-        final TabsTouchHelperCallback callback = new TabsTouchHelperCallback(this, dragDirections, this) {
+        tabTouchCallback = new TabsTouchHelperCallback(this, dragDirections, this) {
             @Override
             protected float alphaForItemSwipeDx(float dX, int distanceToAlphaMin) {
                 return 1f - 2f * Math.abs(dX) / distanceToAlphaMin;
             }
         };
-        final ItemTouchHelper touchHelper = new ItemTouchHelper(callback);
+        final ItemTouchHelper touchHelper = new ItemTouchHelper(tabTouchCallback);
         touchHelper.attachToRecyclerView(this);
     }
 
     @Override
     public void onCloseAll() {
         autoHidePanel();
         if (isNormal()) {
             Tabs.getInstance().closeAllTabs();
--- a/mobile/android/base/java/org/mozilla/gecko/tabs/TabsLayout.java
+++ b/mobile/android/base/java/org/mozilla/gecko/tabs/TabsLayout.java
@@ -28,33 +28,38 @@ public abstract class TabsLayout extends
         TabsTouchHelperCallback.DragListener {
 
     private static final String LOGTAG = "Gecko" + TabsLayout.class.getSimpleName();
 
     private final boolean isPrivate;
     private TabsPanel tabsPanel;
     private final TabsLayoutAdapter tabsAdapter;
     private View emptyView;
+    protected TabsTouchHelperCallback tabTouchCallback;
 
     public TabsLayout(Context context, AttributeSet attrs, int itemViewLayoutResId) {
         super(context, attrs);
 
         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TabsLayout);
         isPrivate = (a.getInt(R.styleable.TabsLayout_tabs, 0x0) == 1);
         a.recycle();
 
         tabsAdapter = new TabsLayoutAdapter(context, itemViewLayoutResId, isPrivate,
                 /* close on click listener */
                 new Button.OnClickListener() {
                     @Override
                     public void onClick(View v) {
-                        // The view here is the close button, which has a reference
-                        // to the parent TabsLayoutItemView in its tag, hence the getTag() call.
-                        TabsLayoutItemView itemView = (TabsLayoutItemView) v.getTag();
-                        closeTab(itemView);
+                        // Bug 1525796: ignore action if the user already interacts with the whole view
+                        // with the help of an ItemTouchHelper
+                        if (tabTouchCallback == null || !tabTouchCallback.isInteractionInProgress()) {
+                            // The view here is the close button, which has a reference
+                            // to the parent TabsLayoutItemView in its tag, hence the getTag() call.
+                            TabsLayoutItemView itemView = (TabsLayoutItemView) v.getTag();
+                            closeTab(itemView);
+                        }
                     }
                 });
         setAdapter(tabsAdapter);
 
         RecyclerViewClickSupport.addTo(this).setOnItemClickListener(this);
 
         setRecyclerListener(new RecyclerListener() {
             @Override
--- a/mobile/android/base/java/org/mozilla/gecko/tabs/TabsListLayout.java
+++ b/mobile/android/base/java/org/mozilla/gecko/tabs/TabsListLayout.java
@@ -29,24 +29,24 @@ public class TabsListLayout extends Tabs
         super(context, attrs, R.layout.tabs_list_item_view);
 
         setHasFixedSize(true);
 
         setLayoutManager(new LinearLayoutManager(context));
 
         final int dragDirections = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
         // A TouchHelper handler for drag and drop and swipe to close.
-        final TabsTouchHelperCallback callback = new TabsTouchHelperCallback(this, dragDirections, this) {
+        tabTouchCallback = new TabsTouchHelperCallback(this, dragDirections, this) {
             @Override
             protected float alphaForItemSwipeDx(float dX, int distanceToAlphaMin) {
                 return Math.max(0.1f,
                         Math.min(1f, 1f - 2f * Math.abs(dX) / distanceToAlphaMin));
             }
         };
-        final ItemTouchHelper touchHelper = new ItemTouchHelper(callback);
+        final ItemTouchHelper touchHelper = new ItemTouchHelper(tabTouchCallback);
         touchHelper.attachToRecyclerView(this);
 
         setItemAnimator(new TabsListLayoutAnimator(ANIMATION_DURATION));
     }
 
     @Override
     public void onCloseAll() {
         final int childCount = getChildCount();
--- a/mobile/android/base/java/org/mozilla/gecko/tabs/TabsTouchHelperCallback.java
+++ b/mobile/android/base/java/org/mozilla/gecko/tabs/TabsTouchHelperCallback.java
@@ -11,16 +11,17 @@ import android.support.annotation.Nullab
 import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.helper.ItemTouchHelper;
 import android.view.View;
 
 class TabsTouchHelperCallback extends ItemTouchHelper.Callback {
     private final @Nullable DismissListener dismissListener;
     private final @NonNull DragListener dragListener;
     private final int movementFlags;
+    private boolean isInteractionInProgress;
 
     interface DismissListener {
         void onItemDismiss(View view);
     }
 
     interface DragListener {
         boolean onItemMove(int fromPosition, int toPosition);
     }
@@ -91,11 +92,30 @@ class TabsTouchHelperCallback extends It
             // the width of the item being swiped.
             viewHolder.itemView.setAlpha(alphaForItemSwipeDx(dX, viewHolder.itemView.getWidth()));
         }
     }
 
     @Override
     public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
         super.clearView(recyclerView, viewHolder);
+
         viewHolder.itemView.setAlpha(1);
+
+        isInteractionInProgress = false;
+    }
+
+    @Override
+    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
+        super.onSelectedChanged(viewHolder, actionState);
+
+        if (viewHolder != null) {
+            isInteractionInProgress = true;
+        }
+    }
+
+    /**
+     * Get if user started interacting with RecyclerView's items and their animations have not yet completed.
+     */
+    public boolean isInteractionInProgress() {
+        return isInteractionInProgress;
     }
 }