Improve responsiveness of archive animations, other swipe tweaks.

fork-5.53.8
Greyson Parrelli 2021-11-09 10:08:51 -05:00 zatwierdzone przez Alex Hart
rodzic 8c45600365
commit 6499ed4637
5 zmienionych plików z 91 dodań i 17 usunięć

Wyświetl plik

@ -125,6 +125,7 @@ public class ConversationListArchiveFragment extends ConversationListFragment im
@Override @Override
protected void onItemSwiped(long threadId, int unreadCount) { protected void onItemSwiped(long threadId, int unreadCount) {
archiveDecoration.onArchiveStarted(); archiveDecoration.onArchiveStarted();
itemAnimator.enable();
new SnackbarAsyncTask<Long>(getViewLifecycleOwner().getLifecycle(), new SnackbarAsyncTask<Long>(getViewLifecycleOwner().getLifecycle(),
requireView(), requireView(),

Wyświetl plik

@ -157,6 +157,7 @@ import org.thoughtcrime.securesms.util.views.Stub;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.websocket.WebSocketConnectionState; import org.whispersystems.signalservice.api.websocket.WebSocketConnectionState;
import java.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@ -214,6 +215,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
private SignalBottomActionBar bottomActionBar; private SignalBottomActionBar bottomActionBar;
protected ConversationListArchiveItemDecoration archiveDecoration; protected ConversationListArchiveItemDecoration archiveDecoration;
protected ConversationListItemAnimator itemAnimator;
private Stopwatch startupStopwatch; private Stopwatch startupStopwatch;
public static ConversationListFragment newInstance() { public static ConversationListFragment newInstance() {
@ -272,9 +274,10 @@ public class ConversationListFragment extends MainFragment implements ActionMode
cameraFab.show(); cameraFab.show();
archiveDecoration = new ConversationListArchiveItemDecoration(new ColorDrawable(getResources().getColor(R.color.conversation_list_archive_background_end))); archiveDecoration = new ConversationListArchiveItemDecoration(new ColorDrawable(getResources().getColor(R.color.conversation_list_archive_background_end)));
itemAnimator = new ConversationListItemAnimator();
list.setLayoutManager(new LinearLayoutManager(requireActivity())); list.setLayoutManager(new LinearLayoutManager(requireActivity()));
list.setItemAnimator(new ConversationListItemAnimator()); list.setItemAnimator(itemAnimator);
list.addOnScrollListener(new ScrollListener()); list.addOnScrollListener(new ScrollListener());
list.addItemDecoration(archiveDecoration); list.addItemDecoration(archiveDecoration);
@ -1276,6 +1279,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
@SuppressLint("StaticFieldLeak") @SuppressLint("StaticFieldLeak")
protected void onItemSwiped(long threadId, int unreadCount) { protected void onItemSwiped(long threadId, int unreadCount) {
archiveDecoration.onArchiveStarted(); archiveDecoration.onArchiveStarted();
itemAnimator.enable();
new SnackbarAsyncTask<Long>(getViewLifecycleOwner().getLifecycle(), new SnackbarAsyncTask<Long>(getViewLifecycleOwner().getLifecycle(),
requireView(), requireView(),
@ -1315,7 +1319,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
ApplicationDependencies.getMessageNotifier().updateNotification(context); ApplicationDependencies.getMessageNotifier().updateNotification(context);
} }
} }
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, threadId); }.executeOnExecutor(SignalExecutors.BOUNDED, threadId);
} }
private class PaymentNotificationListener implements UnreadPaymentsView.Listener { private class PaymentNotificationListener implements UnreadPaymentsView.Listener {
@ -1358,14 +1362,18 @@ public class ConversationListFragment extends MainFragment implements ActionMode
private class ArchiveListenerCallback extends ItemTouchHelper.SimpleCallback { private class ArchiveListenerCallback extends ItemTouchHelper.SimpleCallback {
private static final long SWIPE_ANIMATION_DURATION = 175;
private static final float MIN_ICON_SCALE = 0.85f; private static final float MIN_ICON_SCALE = 0.85f;
private static final float MAX_ICON_SCALE = 1f; private static final float MAX_ICON_SCALE = 1f;
private final int archiveColorStart; private final int archiveColorStart;
private final int archiveColorEnd; private final int archiveColorEnd;
private WeakReference<RecyclerView.ViewHolder> lastTouched;
ArchiveListenerCallback(@ColorInt int archiveColorStart, @ColorInt int archiveColorEnd) { ArchiveListenerCallback(@ColorInt int archiveColorStart, @ColorInt int archiveColorEnd) {
super(0, ItemTouchHelper.RIGHT); super(0, ItemTouchHelper.END);
this.archiveColorStart = archiveColorStart; this.archiveColorStart = archiveColorStart;
this.archiveColorEnd = archiveColorEnd; this.archiveColorEnd = archiveColorEnd;
} }
@ -1389,13 +1397,33 @@ public class ConversationListFragment extends MainFragment implements ActionMode
return 0; return 0;
} }
lastTouched = new WeakReference<>(viewHolder);
return super.getSwipeDirs(recyclerView, viewHolder); return super.getSwipeDirs(recyclerView, viewHolder);
} }
@SuppressLint("StaticFieldLeak")
@Override @Override
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) { public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
if (viewHolder.itemView instanceof ConversationListItemInboxZero) return; if (lastTouched != null) {
Log.w(TAG, "Falling back to slower onSwiped() event.");
onTrueSwipe(viewHolder);
lastTouched = null;
}
}
@Override
public long getAnimationDuration(@NonNull RecyclerView recyclerView, int animationType, float animateDx, float animateDy) {
if (animationType == ItemTouchHelper.ANIMATION_TYPE_SWIPE_SUCCESS && lastTouched != null && lastTouched.get() != null) {
onTrueSwipe(lastTouched.get());
lastTouched = null;
} else if (animationType == ItemTouchHelper.ANIMATION_TYPE_SWIPE_CANCEL) {
lastTouched = null;
}
return SWIPE_ANIMATION_DURATION;
}
private void onTrueSwipe(RecyclerView.ViewHolder viewHolder) {
final long threadId = ((ConversationListItem)viewHolder.itemView).getThreadId(); final long threadId = ((ConversationListItem)viewHolder.itemView).getThreadId();
final int unreadCount = ((ConversationListItem)viewHolder.itemView).getUnreadCount(); final int unreadCount = ((ConversationListItem)viewHolder.itemView).getUnreadCount();
@ -1409,24 +1437,26 @@ public class ConversationListFragment extends MainFragment implements ActionMode
boolean isCurrentlyActive) boolean isCurrentlyActive)
{ {
if (viewHolder.itemView instanceof ConversationListItemInboxZero) return; if (viewHolder.itemView instanceof ConversationListItemInboxZero) return;
float absoluteDx = Math.abs(dX);
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) { if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
Resources resources = getResources(); Resources resources = getResources();
View itemView = viewHolder.itemView; View itemView = viewHolder.itemView;
float percentDx = Math.abs(dX) / viewHolder.itemView.getWidth(); float percentDx = absoluteDx / viewHolder.itemView.getWidth();
int color = ArgbEvaluatorCompat.getInstance().evaluate(Math.min(1f, percentDx * (1 / 0.25f)), archiveColorStart, archiveColorEnd); int color = ArgbEvaluatorCompat.getInstance().evaluate(Math.min(1f, percentDx * (1 / 0.25f)), archiveColorStart, archiveColorEnd);
float scaleStartPoint = DimensionUnit.DP.toPixels(48f); float scaleStartPoint = DimensionUnit.DP.toPixels(48f);
float scaleEndPoint = DimensionUnit.DP.toPixels(96f); float scaleEndPoint = DimensionUnit.DP.toPixels(96f);
float scale; float scale;
if (dX < scaleStartPoint) { if (absoluteDx < scaleStartPoint) {
scale = MIN_ICON_SCALE; scale = MIN_ICON_SCALE;
} else if (dX > scaleEndPoint) { } else if (absoluteDx > scaleEndPoint) {
scale = MAX_ICON_SCALE; scale = MAX_ICON_SCALE;
} else { } else {
scale = Math.min(MAX_ICON_SCALE, MIN_ICON_SCALE + ((dX - scaleStartPoint) / (scaleEndPoint - scaleStartPoint)) * (MAX_ICON_SCALE - MIN_ICON_SCALE)); scale = Math.min(MAX_ICON_SCALE, MIN_ICON_SCALE + ((absoluteDx - scaleStartPoint) / (scaleEndPoint - scaleStartPoint)) * (MAX_ICON_SCALE - MIN_ICON_SCALE));
} }
if (dX > 0) { if (absoluteDx > 0) {
if (archiveDrawable == null) { if (archiveDrawable == null) {
archiveDrawable = Objects.requireNonNull(AppCompatResources.getDrawable(requireContext(), getArchiveIconRes())); archiveDrawable = Objects.requireNonNull(AppCompatResources.getDrawable(requireContext(), getArchiveIconRes()));
archiveDrawable.setColorFilter(new SimpleColorFilter(Color.WHITE)); archiveDrawable.setColorFilter(new SimpleColorFilter(Color.WHITE));
@ -1441,8 +1471,13 @@ public class ConversationListFragment extends MainFragment implements ActionMode
float gutter = resources.getDimension(R.dimen.dsl_settings_gutter); float gutter = resources.getDimension(R.dimen.dsl_settings_gutter);
float extra = resources.getDimension(R.dimen.conversation_list_fragment_archive_padding); float extra = resources.getDimension(R.dimen.conversation_list_fragment_archive_padding);
if (ViewUtil.isLtr(requireContext())) {
canvas.translate(itemView.getLeft() + gutter + extra, canvas.translate(itemView.getLeft() + gutter + extra,
itemView.getTop() + (itemView.getBottom() - itemView.getTop() - archiveDrawable.getIntrinsicHeight()) / 2f); itemView.getTop() + (itemView.getBottom() - itemView.getTop() - archiveDrawable.getIntrinsicHeight()) / 2f);
} else {
canvas.translate(itemView.getRight() - gutter - extra,
itemView.getTop() + (itemView.getBottom() - itemView.getTop() - archiveDrawable.getIntrinsicHeight()) / 2f);
}
canvas.scale(scale, scale, archiveDrawable.getIntrinsicWidth() / 2f, archiveDrawable.getIntrinsicHeight() / 2f); canvas.scale(scale, scale, archiveDrawable.getIntrinsicWidth() / 2f, archiveDrawable.getIntrinsicHeight() / 2f);
@ -1450,7 +1485,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode
canvas.restore(); canvas.restore();
ViewCompat.setElevation(viewHolder.itemView, DimensionUnit.DP.toPixels(4f)); ViewCompat.setElevation(viewHolder.itemView, DimensionUnit.DP.toPixels(4f));
} else if (dX == 0) { } else if (absoluteDx == 0) {
ViewCompat.setElevation(viewHolder.itemView, DimensionUnit.DP.toPixels(0f)); ViewCompat.setElevation(viewHolder.itemView, DimensionUnit.DP.toPixels(0f));
} }
@ -1464,6 +1499,8 @@ public class ConversationListFragment extends MainFragment implements ActionMode
public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) { public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder); super.clearView(recyclerView, viewHolder);
ViewCompat.setElevation(viewHolder.itemView, 0); ViewCompat.setElevation(viewHolder.itemView, 0);
lastTouched = null;
itemAnimator.postDisable(requireView().getHandler());
} }
} }

Wyświetl plik

@ -1,16 +1,42 @@
package org.thoughtcrime.securesms.conversationlist; package org.thoughtcrime.securesms.conversationlist;
import android.os.Handler;
import androidx.annotation.MainThread;
import androidx.recyclerview.widget.DefaultItemAnimator; import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.RecyclerView;
import org.signal.core.util.logging.Log;
public class ConversationListItemAnimator extends DefaultItemAnimator { public class ConversationListItemAnimator extends DefaultItemAnimator {
private static final String TAG = Log.tag(ConversationListItemAnimator.class);
private static final long ANIMATION_DURATION = 200;
private boolean shouldDisable;
public ConversationListItemAnimator() { public ConversationListItemAnimator() {
setSupportsChangeAnimations(false); setSupportsChangeAnimations(false);
} }
@Override public boolean animateRemove(RecyclerView.ViewHolder holder) { @MainThread
return super.animateRemove(holder); public void enable() {
setMoveDuration(ANIMATION_DURATION);
shouldDisable = false;
}
/**
* We need to reasonable ensure that the animation has started before we disable things here, so we add a slight delay.
*/
@MainThread
public void postDisable(Handler handler) {
shouldDisable = true;
handler.postDelayed(() -> {
if (shouldDisable) {
setMoveDuration(0);
} else {
Log.w(TAG, "Disable was canceled by an enable.");
}
}, 50);
} }
} }

Wyświetl plik

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" >
<solid android:color="@color/signal_background_primary"/>
<corners
android:topRightRadius="8dp"
android:bottomRightRadius="8dp"/>
<padding android:bottom="5dp" android:left="5dp" android:right="5dp" android:top="5dp"/>
</shape>

Wyświetl plik

@ -1,6 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" > <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" >
<solid android:color="@color/signal_background_primary"/> <solid android:color="@color/signal_background_primary"/>
<corners android:radius="8dp"/> <corners
android:topLeftRadius="8dp"
android:bottomLeftRadius="8dp"/>
<padding android:bottom="5dp" android:left="5dp" android:right="5dp" android:top="5dp"/> <padding android:bottom="5dp" android:left="5dp" android:right="5dp" android:top="5dp"/>
</shape> </shape>