Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit eeea6676 authored by Yi-Ling Chuang's avatar Yi-Ling Chuang
Browse files

Add implementation of homepage swipe to dismiss.

- Only enable swipe for slice full/half card.
- Add isPendingDismiss in ContextualCard to determine if we should show
dismissal view.
- Take out long press feature.

Bug: 126214056
Test: robotests
Change-Id: Ib03e605347b2f50d3c62fcd4f95875a21cc9ef1c
parent bab3bf6f
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -71,6 +71,7 @@ public class ContextualCard {
    private final Drawable mIconDrawable;
    @LayoutRes
    private final int mViewType;
    private final boolean mIsPendingDismiss;

    public String getName() {
        return mName;
@@ -156,6 +157,10 @@ public class ContextualCard {
        return mViewType;
    }

    public boolean isPendingDismiss() {
        return mIsPendingDismiss;
    }

    public Builder mutate() {
        return mBuilder;
    }
@@ -181,6 +186,7 @@ public class ContextualCard {
        mIconDrawable = builder.mIconDrawable;
        mIsLargeCard = builder.mIsLargeCard;
        mViewType = builder.mViewType;
        mIsPendingDismiss = builder.mIsPendingDismiss;
    }

    ContextualCard(Cursor c) {
@@ -226,6 +232,8 @@ public class ContextualCard {
        mBuilder.setIconDrawable(mIconDrawable);
        mViewType = getViewTypeByCardType(mCardType);
        mBuilder.setViewType(mViewType);
        mIsPendingDismiss = false;
        mBuilder.setIsPendingDismiss(mIsPendingDismiss);
    }

    @Override
@@ -277,6 +285,7 @@ public class ContextualCard {
        private boolean mIsLargeCard;
        @LayoutRes
        private int mViewType;
        private boolean mIsPendingDismiss;

        public Builder setName(String name) {
            mName = name;
@@ -373,6 +382,11 @@ public class ContextualCard {
            return this;
        }

        public Builder setIsPendingDismiss(boolean isPendingDismiss) {
            mIsPendingDismiss = isPendingDismiss;
            return this;
        }

        public ContextualCard build() {
            return new ContextualCard(this);
        }
+6 −3
Original line number Diff line number Diff line
@@ -28,7 +28,7 @@ import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.android.settings.homepage.contextualcards.conditional.ConditionContextualCardRenderer;
import com.android.settings.homepage.contextualcards.slices.SwipeDismissalDelegate.DismissalItemTouchHelperListener;
import com.android.settings.homepage.contextualcards.slices.SwipeDismissalDelegate;
import com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer;

import java.util.ArrayList;
@@ -36,7 +36,7 @@ import java.util.List;
import java.util.Map;

public class ContextualCardsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
        implements ContextualCardUpdateListener, DismissalItemTouchHelperListener {
        implements ContextualCardUpdateListener, SwipeDismissalDelegate.Listener {
    static final int SPAN_COUNT = 2;

    private static final String TAG = "ContextualCardsAdapter";
@@ -140,6 +140,9 @@ public class ContextualCardsAdapter extends RecyclerView.Adapter<RecyclerView.Vi

    @Override
    public void onSwiped(int position) {

        final ContextualCard card = mContextualCards.get(position).mutate()
                .setIsPendingDismiss(true).build();
        mContextualCards.set(position, card);
        notifyItemChanged(position);
    }
}
+6 −10
Original line number Diff line number Diff line
@@ -135,23 +135,19 @@ public class SliceContextualCardRenderer implements ContextualCardRenderer, Life
                // Deferred setup is never dismissible.
                break;
            case VIEW_TYPE_HALF_WIDTH:
                initDismissalActions(holder, card, R.id.content);
                initDismissalActions(holder, card);
                break;
            default:
                initDismissalActions(holder, card, R.id.slice_view);
        }
                initDismissalActions(holder, card);
        }

    private void initDismissalActions(RecyclerView.ViewHolder holder, ContextualCard card,
            int initialViewId) {
        // initialView is the first view in the ViewFlipper.
        final View initialView = holder.itemView.findViewById(initialViewId);
        initialView.setOnLongClickListener(v -> {
        if (card.isPendingDismiss()) {
            flipCardToDismissalView(holder);
            mFlippedCardSet.add(holder);
            return true;
        });
        }
    }

    private void initDismissalActions(RecyclerView.ViewHolder holder, ContextualCard card) {
        final Button btnKeep = holder.itemView.findViewById(R.id.keep);
        btnKeep.setOnClickListener(v -> {
            mFlippedCardSet.remove(holder);
+42 −4
Original line number Diff line number Diff line
@@ -18,32 +18,63 @@ package com.android.settings.homepage.contextualcards.slices;

import android.content.Context;
import android.graphics.Canvas;
import android.widget.ViewFlipper;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;

import com.android.settings.R;
import com.android.settings.homepage.contextualcards.ContextualCard;

public class SwipeDismissalDelegate extends ItemTouchHelper.Callback {

    private static final String TAG = "DismissItemTouchHelper";

    public interface DismissalItemTouchHelperListener {
    public interface Listener {
        void onSwiped(int position);
    }

    private final Context mContext;
    private final DismissalItemTouchHelperListener mListener;
    private final SwipeDismissalDelegate.Listener mListener;

    public SwipeDismissalDelegate(Context context, DismissalItemTouchHelperListener listener) {
    public SwipeDismissalDelegate(Context context, SwipeDismissalDelegate.Listener listener) {
        mContext = context;
        mListener = listener;
    }

    /**
     * Determine whether the ability to drag or swipe should be enabled or not.
     *
     * Only allow swipe on {@link ContextualCard} built with view type
     * {@link SliceContextualCardRenderer#VIEW_TYPE_FULL_WIDTH} or
     * {@link SliceContextualCardRenderer#VIEW_TYPE_HALF_WIDTH}.
     *
     * When the dismissal view is displayed, the swipe will also be disabled.
     */
    @Override
    public int getMovementFlags(@NonNull RecyclerView recyclerView,
            @NonNull RecyclerView.ViewHolder viewHolder) {
        switch (viewHolder.getItemViewType()) {
            case SliceContextualCardRenderer.VIEW_TYPE_FULL_WIDTH:
            case SliceContextualCardRenderer.VIEW_TYPE_HALF_WIDTH:
                //TODO(b/129438972): Convert this to a regular view.
                final ViewFlipper viewFlipper = viewHolder.itemView.findViewById(R.id.view_flipper);

                // As we are using ViewFlipper to switch between the initial view and
                // dismissal view, here we are making sure the current displayed view is the
                // initial view of either slice full card or half card, and only allow swipe on
                // these two types.
                if (viewFlipper.getCurrentView().getId() != getInitialViewId(viewHolder)) {
                    // Disable swiping when we are in the dismissal view
                    return 0;
                }
                return makeMovementFlags(0 /*dragFlags*/,
                        ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT /*swipeFlags*/);
            default:
                return 0;
        }
    }

    @Override
    public boolean onMove(@NonNull RecyclerView recyclerView,
@@ -63,4 +94,11 @@ public class SwipeDismissalDelegate extends ItemTouchHelper.Callback {
            boolean isCurrentlyActive) {
        super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
    }

    private int getInitialViewId(RecyclerView.ViewHolder viewHolder) {
        if (viewHolder.getItemViewType() == SliceContextualCardRenderer.VIEW_TYPE_HALF_WIDTH) {
            return R.id.content;
        }
        return R.id.slice_view;
    }
}
 No newline at end of file
+8 −31
Original line number Diff line number Diff line
@@ -16,13 +16,11 @@

package com.android.settings.homepage.contextualcards.slices;

import static com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer.VIEW_TYPE_DEFERRED_SETUP;
import static com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer.VIEW_TYPE_FULL_WIDTH;

import static com.google.common.truth.Truth.assertThat;

import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;

import android.app.Activity;
@@ -118,34 +116,25 @@ public class SliceContextualCardRendererTest {
    }

    @Test
    public void longClick_shouldFlipCard() {
    public void bindView_isPendingDismiss_shouldFlipToDismissalView() {
        final RecyclerView.ViewHolder viewHolder = getSliceViewHolder();
        final View card = viewHolder.itemView.findViewById(R.id.slice_view);
        final ViewFlipper viewFlipper = viewHolder.itemView.findViewById(R.id.view_flipper);
        final View dismissalView = viewHolder.itemView.findViewById(R.id.dismissal_view);
        mRenderer.bindView(viewHolder, buildContextualCard(TEST_SLICE_URI));
        final ContextualCard card = buildContextualCard(
                TEST_SLICE_URI).mutate().setIsPendingDismiss(true).build();

        card.performLongClick();
        mRenderer.bindView(viewHolder, card);

        assertThat(viewFlipper.getCurrentView()).isEqualTo(dismissalView);
    }

    @Test
    public void longClick_deferredSetupCard_shouldNotBeClickable() {
        final RecyclerView.ViewHolder viewHolder = getDeferredSetupViewHolder();
        final View contentView = viewHolder.itemView.findViewById(R.id.content);
        mRenderer.bindView(viewHolder, buildContextualCard(TEST_SLICE_URI));

        assertThat(contentView.isLongClickable()).isFalse();
    }

    @Test
    public void longClick_shouldAddViewHolderToSet() {
    public void bindView_isPendingDismiss_shouldAddViewHolderToSet() {
        final RecyclerView.ViewHolder viewHolder = getSliceViewHolder();
        final View card = viewHolder.itemView.findViewById(R.id.slice_view);
        mRenderer.bindView(viewHolder, buildContextualCard(TEST_SLICE_URI));
        final ContextualCard card = buildContextualCard(
                TEST_SLICE_URI).mutate().setIsPendingDismiss(true).build();

        card.performLongClick();
        mRenderer.bindView(viewHolder, card);

        assertThat(mRenderer.mFlippedCardSet).contains(viewHolder);
    }
@@ -232,18 +221,6 @@ public class SliceContextualCardRendererTest {
        return mRenderer.createViewHolder(view, VIEW_TYPE_FULL_WIDTH);
    }

    private RecyclerView.ViewHolder getDeferredSetupViewHolder() {
        final RecyclerView recyclerView = new RecyclerView(mActivity);
        recyclerView.setLayoutManager(new LinearLayoutManager(mActivity));
        final View view = LayoutInflater.from(mActivity).inflate(VIEW_TYPE_DEFERRED_SETUP,
                recyclerView, false);
        final RecyclerView.ViewHolder viewHolder = spy(
                mRenderer.createViewHolder(view, VIEW_TYPE_DEFERRED_SETUP));
        doReturn(VIEW_TYPE_DEFERRED_SETUP).when(viewHolder).getItemViewType();

        return viewHolder;
    }

    private ContextualCard buildContextualCard(Uri sliceUri) {
        return new ContextualCard.Builder()
                .setName("test_name")
Loading