Ammar Githam
4 years ago
24 changed files with 650 additions and 55 deletions
-
2app/src/main/java/awais/instagrabber/adapters/DirectItemsAdapter.java
-
6app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemActionLogViewHolder.java
-
6app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemAnimatedMediaViewHolder.java
-
6app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemDefaultViewHolder.java
-
6app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemLikeViewHolder.java
-
17app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemMediaShareViewHolder.java
-
6app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemPlaceholderViewHolder.java
-
6app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemProfileViewHolder.java
-
6app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemStoryShareViewHolder.java
-
6app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemVideoCallEventViewHolder.java
-
14app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemViewHolder.java
-
16app/src/main/java/awais/instagrabber/customviews/DirectItemFrameLayout.java
-
184app/src/main/java/awais/instagrabber/customviews/helpers/SwipeAndRestoreItemTouchHelperCallback.java
-
214app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java
-
6app/src/main/java/awais/instagrabber/models/enums/DirectItemType.java
-
19app/src/main/java/awais/instagrabber/repositories/requests/directmessages/BroadcastOptions.java
-
6app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItem.java
-
5app/src/main/java/awais/instagrabber/utils/DirectItemFactory.java
-
6app/src/main/java/awais/instagrabber/utils/Utils.java
-
38app/src/main/java/awais/instagrabber/viewmodels/DirectThreadViewModel.java
-
24app/src/main/java/awais/instagrabber/webservices/DirectMessagesService.java
-
10app/src/main/res/drawable/ic_round_reply_24.xml
-
90app/src/main/res/layout/fragment_direct_messages_thread.xml
-
6app/src/main/res/values/strings.xml
@ -0,0 +1,184 @@ |
|||
package awais.instagrabber.customviews.helpers; |
|||
|
|||
|
|||
import android.annotation.SuppressLint; |
|||
import android.content.Context; |
|||
import android.graphics.Canvas; |
|||
import android.graphics.Rect; |
|||
import android.graphics.drawable.Drawable; |
|||
import android.view.HapticFeedbackConstants; |
|||
import android.view.MotionEvent; |
|||
import android.view.View; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.appcompat.content.res.AppCompatResources; |
|||
import androidx.recyclerview.widget.ItemTouchHelper; |
|||
import androidx.recyclerview.widget.RecyclerView; |
|||
|
|||
import awais.instagrabber.R; |
|||
import awais.instagrabber.utils.Utils; |
|||
|
|||
/** |
|||
* Thanks to https://github.com/izjumovfs/SwipeToReply/blob/master/swipetoreply/src/main/java/com/capybaralabs/swipetoreply/SwipeController.java |
|||
*/ |
|||
public class SwipeAndRestoreItemTouchHelperCallback extends ItemTouchHelper.Callback { |
|||
private static final String TAG = "SwipeRestoreCallback"; |
|||
|
|||
private final float swipeThreshold; |
|||
private final float swipeAutoCancelThreshold; |
|||
private final OnSwipeListener onSwipeListener; |
|||
private final Drawable replyIcon; |
|||
// private final Drawable replyIconBackground; |
|||
private final int replyIconShowThreshold; |
|||
private final float replyIconMaxTranslation; |
|||
private final Rect replyIconBounds = new Rect(); |
|||
private final float replyIconXOffset; |
|||
private final int replyIconSize; |
|||
|
|||
private boolean mSwipeBack = false; |
|||
private boolean hasVibrated; |
|||
|
|||
public SwipeAndRestoreItemTouchHelperCallback(final Context context, final OnSwipeListener onSwipeListener) { |
|||
this.onSwipeListener = onSwipeListener; |
|||
swipeThreshold = Utils.displayMetrics.widthPixels * 0.25f; |
|||
swipeAutoCancelThreshold = swipeThreshold + Utils.convertDpToPx(5); |
|||
replyIcon = AppCompatResources.getDrawable(context, R.drawable.ic_round_reply_24); |
|||
if (replyIcon == null) { |
|||
throw new IllegalArgumentException("reply icon is null"); |
|||
} |
|||
replyIcon.setTint(context.getResources().getColor(R.color.white)); //todo need to update according to theme |
|||
replyIconShowThreshold = Utils.convertDpToPx(24); |
|||
replyIconMaxTranslation = swipeThreshold - replyIconShowThreshold; |
|||
// Log.d(TAG, "replyIconShowThreshold: " + replyIconShowThreshold + ", swipeThreshold: " + swipeThreshold); |
|||
replyIconSize = replyIconShowThreshold; // Utils.convertDpToPx(24); |
|||
replyIconXOffset = swipeThreshold * 0.25f /*Utils.convertDpToPx(20)*/; |
|||
} |
|||
|
|||
@Override |
|||
public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) { |
|||
if (!(viewHolder instanceof SwipeableViewHolder)) { |
|||
return makeMovementFlags(ItemTouchHelper.ACTION_STATE_IDLE, ItemTouchHelper.ACTION_STATE_IDLE); |
|||
} |
|||
return makeMovementFlags(ItemTouchHelper.ACTION_STATE_IDLE, ((SwipeableViewHolder) viewHolder).getSwipeDirection()); |
|||
} |
|||
|
|||
@Override |
|||
public boolean onMove(@NonNull RecyclerView recyclerView, |
|||
@NonNull RecyclerView.ViewHolder viewHolder, |
|||
@NonNull RecyclerView.ViewHolder viewHolder1) { |
|||
return false; |
|||
} |
|||
|
|||
@Override |
|||
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int i) {} |
|||
|
|||
@Override |
|||
public int convertToAbsoluteDirection(int flags, int layoutDirection) { |
|||
if (mSwipeBack) { |
|||
mSwipeBack = false; |
|||
return 0; |
|||
} |
|||
return super.convertToAbsoluteDirection(flags, layoutDirection); |
|||
} |
|||
|
|||
@Override |
|||
public void onChildDraw(@NonNull Canvas c, |
|||
@NonNull RecyclerView recyclerView, |
|||
@NonNull RecyclerView.ViewHolder viewHolder, |
|||
float dX, |
|||
float dY, |
|||
int actionState, |
|||
boolean isCurrentlyActive) { |
|||
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) { |
|||
setTouchListener(recyclerView, viewHolder); |
|||
} |
|||
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); |
|||
drawReplyButton(c, viewHolder); |
|||
} |
|||
|
|||
@SuppressLint("ClickableViewAccessibility") |
|||
private void setTouchListener(RecyclerView recyclerView, final RecyclerView.ViewHolder viewHolder) { |
|||
recyclerView.setOnTouchListener((v, event) -> { |
|||
if (event.getAction() == MotionEvent.ACTION_MOVE) { |
|||
if (Math.abs(viewHolder.itemView.getTranslationX()) >= swipeAutoCancelThreshold) { |
|||
if (!hasVibrated) { |
|||
viewHolder.itemView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, |
|||
HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); |
|||
hasVibrated = true; |
|||
} |
|||
// MotionEvent cancelEvent = MotionEvent.obtain(event); |
|||
// cancelEvent.setAction(MotionEvent.ACTION_CANCEL); |
|||
// recyclerView.dispatchTouchEvent(cancelEvent); |
|||
// cancelEvent.recycle(); |
|||
} |
|||
} |
|||
mSwipeBack = event.getAction() == MotionEvent.ACTION_CANCEL || event.getAction() == MotionEvent.ACTION_UP; |
|||
if (mSwipeBack) { |
|||
hasVibrated = false; |
|||
if (Math.abs(viewHolder.itemView.getTranslationX()) >= swipeThreshold) { |
|||
if (onSwipeListener != null) { |
|||
onSwipeListener.onSwipe(viewHolder.getBindingAdapterPosition(), viewHolder); |
|||
} |
|||
} |
|||
} |
|||
return false; |
|||
}); |
|||
} |
|||
|
|||
public interface SwipeableViewHolder { |
|||
int getSwipeDirection(); |
|||
} |
|||
|
|||
public interface OnSwipeListener { |
|||
void onSwipe(final int adapterPosition, final RecyclerView.ViewHolder viewHolder); |
|||
} |
|||
|
|||
private void drawReplyButton(Canvas canvas, final RecyclerView.ViewHolder viewHolder) { |
|||
if (!(viewHolder instanceof SwipeableViewHolder)) return; |
|||
final int swipeDirection = ((SwipeableViewHolder) viewHolder).getSwipeDirection(); |
|||
if (swipeDirection != ItemTouchHelper.START && swipeDirection != ItemTouchHelper.END) return; |
|||
final View view = viewHolder.itemView; |
|||
float translationX = view.getTranslationX(); |
|||
boolean show = false; |
|||
float progress; |
|||
final float translationXAbs = Math.abs(translationX); |
|||
if (translationXAbs >= replyIconShowThreshold) { |
|||
show = true; |
|||
} |
|||
if (show) { |
|||
// replyIconShowThreshold -> swipeThreshold <=> progress 0 -> 1 |
|||
final float replyIconTranslation = translationXAbs - replyIconShowThreshold; |
|||
progress = replyIconTranslation / replyIconMaxTranslation; |
|||
if (progress > 1) { |
|||
progress = 1f; |
|||
} |
|||
if (progress < 0) { |
|||
progress = 0; |
|||
} |
|||
// Log.d(TAG, /*"translationX: " + translationX + ", replyIconTranslation: " + replyIconTranslation +*/ "progress: " + progress); |
|||
} else { |
|||
progress = 0f; |
|||
// Log.d(TAG, /*"translationX: " + translationX + ", replyIconTranslation: " + 0 +*/ "progress: " + progress); |
|||
} |
|||
if (progress > 0) { |
|||
// calculate the reply icon y position, then offset top, bottom with icon size |
|||
final int y = view.getTop() + (view.getMeasuredHeight() / 2); |
|||
final int tempIconSize = (int) (replyIconSize * progress); |
|||
final int tempIconSizeHalf = tempIconSize / 2; |
|||
final int xOffset = (int) (replyIconXOffset * progress); |
|||
final int left; |
|||
if (swipeDirection == ItemTouchHelper.END) { |
|||
// draw arrow of left side |
|||
left = xOffset; |
|||
} else { |
|||
// draw arrow of right side |
|||
left = view.getMeasuredWidth() - xOffset - tempIconSize; |
|||
} |
|||
final int right = tempIconSize + left; |
|||
replyIconBounds.set(left, y - tempIconSizeHalf, right, y + tempIconSizeHalf); |
|||
replyIcon.setBounds(replyIconBounds); |
|||
replyIcon.draw(canvas); |
|||
} |
|||
} |
|||
|
|||
} |
@ -0,0 +1,10 @@ |
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" |
|||
android:width="24dp" |
|||
android:height="24dp" |
|||
android:viewportWidth="24" |
|||
android:viewportHeight="24" |
|||
android:tint="?attr/colorControlNormal"> |
|||
<path |
|||
android:fillColor="@android:color/white" |
|||
android:pathData="M10,9V7.41c0,-0.89 -1.08,-1.34 -1.71,-0.71L3.7,11.29c-0.39,0.39 -0.39,1.02 0,1.41l4.59,4.59c0.63,0.63 1.71,0.19 1.71,-0.7V14.9c5,0 8.5,1.6 11,5.1 -1,-5 -4,-10 -11,-11z"/> |
|||
</vector> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue