1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.dialer.speeddial.draghelper; 18 19 import android.content.Context; 20 import android.graphics.Canvas; 21 import android.support.annotation.NonNull; 22 import android.support.annotation.Nullable; 23 import android.support.v7.widget.RecyclerView; 24 import android.support.v7.widget.RecyclerView.ViewHolder; 25 import android.support.v7.widget.helper.ItemTouchHelper; 26 import com.android.dialer.logging.DialerImpression; 27 import com.android.dialer.logging.Logger; 28 29 /** {@link ItemTouchHelper} for Speed Dial favorite contacts. */ 30 public class SpeedDialItemTouchHelperCallback extends ItemTouchHelper.Callback { 31 32 private final ItemTouchHelperAdapter adapter; 33 private final Context context; 34 35 // When dragged item is in removeView, onMove() and onChildDraw() are called in turn. This 36 // behavior changes when dragged item entering/leaving removeView. The boolean field 37 // movedOverRemoveView is for onMove() and onChildDraw() to flip. 38 private boolean movedOverRemoveView; 39 private boolean inRemoveView; 40 SpeedDialItemTouchHelperCallback(Context context, ItemTouchHelperAdapter adapter)41 public SpeedDialItemTouchHelperCallback(Context context, ItemTouchHelperAdapter adapter) { 42 this.context = context; 43 this.adapter = adapter; 44 } 45 46 @Override isLongPressDragEnabled()47 public boolean isLongPressDragEnabled() { 48 // We'll manually call ItemTouchHelper#startDrag 49 return false; 50 } 51 52 @Override isItemViewSwipeEnabled()53 public boolean isItemViewSwipeEnabled() { 54 // We don't want to enable swiping 55 return false; 56 } 57 58 @Override canDropOver( @onNull RecyclerView recyclerView, @NonNull ViewHolder current, @NonNull ViewHolder target)59 public boolean canDropOver( 60 @NonNull RecyclerView recyclerView, @NonNull ViewHolder current, @NonNull ViewHolder target) { 61 return adapter.canDropOver(target); 62 } 63 64 @Override getMovementFlags(@onNull RecyclerView recyclerView, @NonNull ViewHolder viewHolder)65 public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull ViewHolder viewHolder) { 66 if (!adapter.canDropOver(viewHolder)) { 67 return makeMovementFlags(0, 0); 68 } 69 70 int dragFlags = 71 ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.START | ItemTouchHelper.END; 72 return makeMovementFlags(dragFlags, /* swipeFlags */ 0); 73 } 74 75 @Override onMove( @onNull RecyclerView recyclerView, @NonNull ViewHolder viewHolder, @NonNull ViewHolder target)76 public boolean onMove( 77 @NonNull RecyclerView recyclerView, 78 @NonNull ViewHolder viewHolder, 79 @NonNull ViewHolder target) { 80 if (target.getItemViewType() == 0) { // 0 for RowType.REMOVE_VIEW 81 movedOverRemoveView = true; 82 if (!inRemoveView) { 83 // onMove() first called 84 adapter.enterRemoveView(); 85 inRemoveView = true; 86 } 87 return false; 88 } else if (inRemoveView) { 89 // Move out of removeView fast 90 inRemoveView = false; 91 movedOverRemoveView = false; 92 adapter.leaveRemoveView(); 93 } 94 adapter.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition()); 95 return true; 96 } 97 98 @Override onMoved( @onNull RecyclerView recyclerView, @NonNull ViewHolder viewHolder, int fromPos, @NonNull ViewHolder viewHolder1, int toPos, int x, int y)99 public void onMoved( 100 @NonNull RecyclerView recyclerView, 101 @NonNull ViewHolder viewHolder, 102 int fromPos, 103 @NonNull ViewHolder viewHolder1, 104 int toPos, 105 int x, 106 int y) { 107 Logger.get(context) 108 .logImpression(DialerImpression.Type.FAVORITE_MOVE_FAVORITE_BY_DRAG_AND_DROP); 109 super.onMoved(recyclerView, viewHolder, fromPos, viewHolder1, toPos, x, y); 110 } 111 112 @Override onChildDraw( @onNull Canvas canvas, @NonNull RecyclerView recyclerView, @NonNull ViewHolder viewHolder, float dx, float dy, int i, boolean isCurrentlyActive)113 public void onChildDraw( 114 @NonNull Canvas canvas, 115 @NonNull RecyclerView recyclerView, 116 @NonNull ViewHolder viewHolder, 117 float dx, 118 float dy, 119 int i, 120 boolean isCurrentlyActive) { 121 if (inRemoveView) { 122 if (!isCurrentlyActive) { 123 // View animating back to its original state, which means drop in this case 124 inRemoveView = false; 125 adapter.dropOnRemoveView(viewHolder); 126 } 127 if (!movedOverRemoveView) { 128 // when the view is over a droppable target, onMove() will be called before onChildDraw() 129 // thus if onMove() is not called, it is not over a droppable target. 130 inRemoveView = false; 131 adapter.leaveRemoveView(); 132 } 133 } 134 movedOverRemoveView = false; 135 super.onChildDraw(canvas, recyclerView, viewHolder, dx, dy, i, isCurrentlyActive); 136 } 137 138 @Override onSelectedChanged(@ullable ViewHolder viewHolder, int actionState)139 public void onSelectedChanged(@Nullable ViewHolder viewHolder, int actionState) { 140 super.onSelectedChanged(viewHolder, actionState); 141 adapter.onSelectedChanged(viewHolder, actionState); 142 } 143 144 @Override onSwiped(@onNull ViewHolder viewHolder, int direction)145 public void onSwiped(@NonNull ViewHolder viewHolder, int direction) { 146 // No-op since we don't support swiping 147 } 148 149 /** RecyclerView adapters interested in drag and drop should implement this interface. */ 150 public interface ItemTouchHelperAdapter { 151 onItemMove(int fromPosition, int toPosition)152 void onItemMove(int fromPosition, int toPosition); 153 canDropOver(ViewHolder target)154 boolean canDropOver(ViewHolder target); 155 onSelectedChanged(@ullable ViewHolder viewHolder, int actionState)156 void onSelectedChanged(@Nullable ViewHolder viewHolder, int actionState); 157 enterRemoveView()158 void enterRemoveView(); 159 leaveRemoveView()160 void leaveRemoveView(); 161 dropOnRemoveView(ViewHolder fromViewHolder)162 void dropOnRemoveView(ViewHolder fromViewHolder); 163 } 164 } 165