mirror of
https://github.com/KokaKiwi/BarInsta
synced 2024-11-15 19:27:31 +00:00
Swipe to reply
This commit is contained in:
parent
59aa14e2f6
commit
13d95523a3
@ -342,7 +342,7 @@ public final class DirectItemsAdapter extends RecyclerView.Adapter<RecyclerView.
|
|||||||
|
|
||||||
public static class DirectItemOrHeader {
|
public static class DirectItemOrHeader {
|
||||||
Date date;
|
Date date;
|
||||||
DirectItem item;
|
public DirectItem item;
|
||||||
|
|
||||||
public boolean isHeader() {
|
public boolean isHeader() {
|
||||||
return date != null;
|
return date != null;
|
||||||
|
@ -10,6 +10,7 @@ import android.text.style.StyleSpan;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -86,4 +87,9 @@ public class DirectItemActionLogViewHolder extends DirectItemViewHolder {
|
|||||||
protected boolean allowLongClick() {
|
protected boolean allowLongClick() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSwipeDirection() {
|
||||||
|
return ItemTouchHelper.ACTION_STATE_IDLE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import android.view.ViewGroup;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.core.util.Pair;
|
import androidx.core.util.Pair;
|
||||||
|
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||||
|
|
||||||
import com.facebook.drawee.backends.pipeline.Fresco;
|
import com.facebook.drawee.backends.pipeline.Fresco;
|
||||||
|
|
||||||
@ -59,4 +60,9 @@ public class DirectItemAnimatedMediaViewHolder extends DirectItemViewHolder {
|
|||||||
.setAutoPlayAnimations(true)
|
.setAutoPlayAnimations(true)
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSwipeDirection() {
|
||||||
|
return ItemTouchHelper.ACTION_STATE_IDLE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package awais.instagrabber.adapters.viewholder.directmessages;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||||
|
|
||||||
import awais.instagrabber.R;
|
import awais.instagrabber.R;
|
||||||
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
|
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
|
||||||
@ -35,4 +36,9 @@ public class DirectItemDefaultViewHolder extends DirectItemViewHolder {
|
|||||||
protected boolean allowLongClick() {
|
protected boolean allowLongClick() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSwipeDirection() {
|
||||||
|
return ItemTouchHelper.ACTION_STATE_IDLE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package awais.instagrabber.adapters.viewholder.directmessages;
|
package awais.instagrabber.adapters.viewholder.directmessages;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||||
|
|
||||||
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
|
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
|
||||||
import awais.instagrabber.databinding.LayoutDmBaseBinding;
|
import awais.instagrabber.databinding.LayoutDmBaseBinding;
|
||||||
@ -27,4 +28,9 @@ public class DirectItemLikeViewHolder extends DirectItemViewHolder {
|
|||||||
protected boolean canForward() {
|
protected boolean canForward() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSwipeDirection() {
|
||||||
|
return ItemTouchHelper.ACTION_STATE_IDLE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import android.view.ViewGroup;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.core.util.Pair;
|
import androidx.core.util.Pair;
|
||||||
|
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||||
|
|
||||||
import com.facebook.drawee.drawable.ScalingUtils;
|
import com.facebook.drawee.drawable.ScalingUtils;
|
||||||
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
|
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
|
||||||
@ -36,6 +37,7 @@ public class DirectItemMediaShareViewHolder extends DirectItemViewHolder {
|
|||||||
private final LayoutDmMediaShareBinding binding;
|
private final LayoutDmMediaShareBinding binding;
|
||||||
private final RoundingParams incomingRoundingParams;
|
private final RoundingParams incomingRoundingParams;
|
||||||
private final RoundingParams outgoingRoundingParams;
|
private final RoundingParams outgoingRoundingParams;
|
||||||
|
private DirectItemType itemType;
|
||||||
|
|
||||||
public DirectItemMediaShareViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
|
public DirectItemMediaShareViewHolder(@NonNull final LayoutDmBaseBinding baseBinding,
|
||||||
@NonNull final LayoutDmMediaShareBinding binding,
|
@NonNull final LayoutDmMediaShareBinding binding,
|
||||||
@ -148,13 +150,14 @@ public class DirectItemMediaShareViewHolder extends DirectItemViewHolder {
|
|||||||
@Nullable
|
@Nullable
|
||||||
private Media getMedia(@NonNull final DirectItem item) {
|
private Media getMedia(@NonNull final DirectItem item) {
|
||||||
Media media = null;
|
Media media = null;
|
||||||
if (item.getItemType() == DirectItemType.MEDIA_SHARE) {
|
itemType = item.getItemType();
|
||||||
|
if (itemType == DirectItemType.MEDIA_SHARE) {
|
||||||
media = item.getMediaShare();
|
media = item.getMediaShare();
|
||||||
} else if (item.getItemType() == DirectItemType.CLIP) {
|
} else if (itemType == DirectItemType.CLIP) {
|
||||||
final DirectItemClip clip = item.getClip();
|
final DirectItemClip clip = item.getClip();
|
||||||
if (clip == null) return null;
|
if (clip == null) return null;
|
||||||
media = clip.getClip();
|
media = clip.getClip();
|
||||||
} else if (item.getItemType() == DirectItemType.FELIX_SHARE) {
|
} else if (itemType == DirectItemType.FELIX_SHARE) {
|
||||||
final DirectItemFelixShare felixShare = item.getFelixShare();
|
final DirectItemFelixShare felixShare = item.getFelixShare();
|
||||||
if (felixShare == null) return null;
|
if (felixShare == null) return null;
|
||||||
media = felixShare.getVideo();
|
media = felixShare.getVideo();
|
||||||
@ -166,4 +169,12 @@ public class DirectItemMediaShareViewHolder extends DirectItemViewHolder {
|
|||||||
protected int getReactionsTranslationY() {
|
protected int getReactionsTranslationY() {
|
||||||
return reactionTranslationYType2;
|
return reactionTranslationYType2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSwipeDirection() {
|
||||||
|
if (itemType != null && (itemType == DirectItemType.CLIP || itemType == DirectItemType.FELIX_SHARE)) {
|
||||||
|
return ItemTouchHelper.ACTION_STATE_IDLE;
|
||||||
|
}
|
||||||
|
return super.getSwipeDirection();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package awais.instagrabber.adapters.viewholder.directmessages;
|
package awais.instagrabber.adapters.viewholder.directmessages;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||||
|
|
||||||
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
|
import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemCallback;
|
||||||
import awais.instagrabber.databinding.LayoutDmBaseBinding;
|
import awais.instagrabber.databinding.LayoutDmBaseBinding;
|
||||||
@ -38,4 +39,9 @@ public class DirectItemPlaceholderViewHolder extends DirectItemViewHolder {
|
|||||||
protected boolean allowLongClick() {
|
protected boolean allowLongClick() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSwipeDirection() {
|
||||||
|
return ItemTouchHelper.ACTION_STATE_IDLE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import android.content.res.Resources;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||||
|
|
||||||
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
|
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
|
||||||
import com.facebook.drawee.generic.RoundingParams;
|
import com.facebook.drawee.generic.RoundingParams;
|
||||||
@ -117,4 +118,9 @@ public class DirectItemProfileViewHolder extends DirectItemViewHolder {
|
|||||||
binding.isVerified.setVisibility(View.GONE);
|
binding.isVerified.setVisibility(View.GONE);
|
||||||
itemView.setOnClickListener(v -> openLocation(location.getPk()));
|
itemView.setOnClickListener(v -> openLocation(location.getPk()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSwipeDirection() {
|
||||||
|
return ItemTouchHelper.ACTION_STATE_IDLE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import android.view.ViewGroup;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.core.util.Pair;
|
import androidx.core.util.Pair;
|
||||||
|
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||||
|
|
||||||
import com.facebook.drawee.drawable.ScalingUtils;
|
import com.facebook.drawee.drawable.ScalingUtils;
|
||||||
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
|
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
|
||||||
@ -110,4 +111,9 @@ public class DirectItemStoryShareViewHolder extends DirectItemViewHolder {
|
|||||||
protected boolean canForward() {
|
protected boolean canForward() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSwipeDirection() {
|
||||||
|
return ItemTouchHelper.ACTION_STATE_IDLE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import android.text.style.ForegroundColorSpan;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -76,4 +77,9 @@ public class DirectItemVideoCallEventViewHolder extends DirectItemViewHolder {
|
|||||||
protected boolean allowLongClick() {
|
protected boolean allowLongClick() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSwipeDirection() {
|
||||||
|
return ItemTouchHelper.ACTION_STATE_IDLE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||||
import androidx.core.widget.ImageViewCompat;
|
import androidx.core.widget.ImageViewCompat;
|
||||||
|
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import androidx.transition.TransitionManager;
|
import androidx.transition.TransitionManager;
|
||||||
|
|
||||||
@ -33,6 +34,7 @@ import awais.instagrabber.adapters.DirectItemsAdapter.DirectItemInternalLongClic
|
|||||||
import awais.instagrabber.customviews.DirectItemContextMenu;
|
import awais.instagrabber.customviews.DirectItemContextMenu;
|
||||||
import awais.instagrabber.customviews.DirectItemFrameLayout;
|
import awais.instagrabber.customviews.DirectItemFrameLayout;
|
||||||
import awais.instagrabber.customviews.RamboTextViewV2;
|
import awais.instagrabber.customviews.RamboTextViewV2;
|
||||||
|
import awais.instagrabber.customviews.helpers.SwipeAndRestoreItemTouchHelperCallback.SwipeableViewHolder;
|
||||||
import awais.instagrabber.databinding.LayoutDmBaseBinding;
|
import awais.instagrabber.databinding.LayoutDmBaseBinding;
|
||||||
import awais.instagrabber.models.enums.DirectItemType;
|
import awais.instagrabber.models.enums.DirectItemType;
|
||||||
import awais.instagrabber.models.enums.MediaItemType;
|
import awais.instagrabber.models.enums.MediaItemType;
|
||||||
@ -46,7 +48,7 @@ import awais.instagrabber.repositories.responses.directmessages.DirectThread;
|
|||||||
import awais.instagrabber.utils.DeepLinkParser;
|
import awais.instagrabber.utils.DeepLinkParser;
|
||||||
import awais.instagrabber.utils.ResponseBodyUtils;
|
import awais.instagrabber.utils.ResponseBodyUtils;
|
||||||
|
|
||||||
public abstract class DirectItemViewHolder extends RecyclerView.ViewHolder {
|
public abstract class DirectItemViewHolder extends RecyclerView.ViewHolder implements SwipeableViewHolder {
|
||||||
private static final String TAG = DirectItemViewHolder.class.getSimpleName();
|
private static final String TAG = DirectItemViewHolder.class.getSimpleName();
|
||||||
|
|
||||||
private final LayoutDmBaseBinding binding;
|
private final LayoutDmBaseBinding binding;
|
||||||
@ -72,6 +74,7 @@ public abstract class DirectItemViewHolder extends RecyclerView.ViewHolder {
|
|||||||
private DirectItemInternalLongClickListener longClickListener;
|
private DirectItemInternalLongClickListener longClickListener;
|
||||||
private DirectItem item;
|
private DirectItem item;
|
||||||
private ViewPropertyAnimator shrinkGrowAnimator;
|
private ViewPropertyAnimator shrinkGrowAnimator;
|
||||||
|
private MessageDirection messageDirection;
|
||||||
// private View.OnLayoutChangeListener layoutChangeListener;
|
// private View.OnLayoutChangeListener layoutChangeListener;
|
||||||
|
|
||||||
public DirectItemViewHolder(@NonNull final LayoutDmBaseBinding binding,
|
public DirectItemViewHolder(@NonNull final LayoutDmBaseBinding binding,
|
||||||
@ -108,7 +111,7 @@ public abstract class DirectItemViewHolder extends RecyclerView.ViewHolder {
|
|||||||
|
|
||||||
public void bind(final int position, final DirectItem item) {
|
public void bind(final int position, final DirectItem item) {
|
||||||
this.item = item;
|
this.item = item;
|
||||||
final MessageDirection messageDirection = isSelf(item) ? MessageDirection.OUTGOING : MessageDirection.INCOMING;
|
messageDirection = isSelf(item) ? MessageDirection.OUTGOING : MessageDirection.INCOMING;
|
||||||
itemView.post(() -> bindBase(item, messageDirection, position));
|
itemView.post(() -> bindBase(item, messageDirection, position));
|
||||||
itemView.post(() -> bindItem(item, messageDirection));
|
itemView.post(() -> bindItem(item, messageDirection));
|
||||||
itemView.post(() -> setupLongClickListener(position, messageDirection));
|
itemView.post(() -> setupLongClickListener(position, messageDirection));
|
||||||
@ -266,6 +269,7 @@ public abstract class DirectItemViewHolder extends RecyclerView.ViewHolder {
|
|||||||
// if (media == null) break;
|
// if (media == null) break;
|
||||||
// url = ResponseBodyUtils.getThumbUrl(media.getImageVersions2());
|
// url = ResponseBodyUtils.getThumbUrl(media.getImageVersions2());
|
||||||
// break;
|
// break;
|
||||||
|
// case LOCATION
|
||||||
}
|
}
|
||||||
if (text == null && url == null) {
|
if (text == null && url == null) {
|
||||||
binding.quoteLine.setVisibility(View.GONE);
|
binding.quoteLine.setVisibility(View.GONE);
|
||||||
@ -568,6 +572,12 @@ public abstract class DirectItemViewHolder extends RecyclerView.ViewHolder {
|
|||||||
shrinkGrowAnimator.start();
|
shrinkGrowAnimator.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSwipeDirection() {
|
||||||
|
if (item == null || messageDirection == null) return ItemTouchHelper.ACTION_STATE_IDLE;
|
||||||
|
return messageDirection == MessageDirection.OUTGOING ? ItemTouchHelper.START : ItemTouchHelper.END;
|
||||||
|
}
|
||||||
|
|
||||||
public enum MessageDirection {
|
public enum MessageDirection {
|
||||||
INCOMING,
|
INCOMING,
|
||||||
OUTGOING
|
OUTGOING
|
||||||
|
@ -73,9 +73,20 @@ public class DirectItemFrameLayout extends FrameLayout {
|
|||||||
touchY = ev.getRawY();
|
touchY = ev.getRawY();
|
||||||
break;
|
break;
|
||||||
case MotionEvent.ACTION_MOVE:
|
case MotionEvent.ACTION_MOVE:
|
||||||
if (longPressed || Math.abs(touchX - ev.getRawX()) > touchSlop || Math.abs(touchY - ev.getRawY()) > touchSlop) {
|
final float diffX = touchX - ev.getRawX();
|
||||||
|
final float diffXAbs = Math.abs(diffX);
|
||||||
|
final boolean isMoved = diffXAbs > touchSlop || Math.abs(touchY - ev.getRawY()) > touchSlop;
|
||||||
|
if (longPressed || isMoved) {
|
||||||
handler.removeCallbacks(longPressStartRunnable);
|
handler.removeCallbacks(longPressStartRunnable);
|
||||||
handler.removeCallbacks(longPressRunnable);
|
handler.removeCallbacks(longPressRunnable);
|
||||||
|
if (!longPressed) {
|
||||||
|
if (onItemLongClickListener != null) {
|
||||||
|
onItemLongClickListener.onLongClickCancel(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if (diffXAbs > touchSlop) {
|
||||||
|
// setTranslationX(-diffX);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MotionEvent.ACTION_UP:
|
case MotionEvent.ACTION_UP:
|
||||||
@ -91,6 +102,9 @@ public class DirectItemFrameLayout extends FrameLayout {
|
|||||||
case MotionEvent.ACTION_CANCEL:
|
case MotionEvent.ACTION_CANCEL:
|
||||||
handler.removeCallbacks(longPressRunnable);
|
handler.removeCallbacks(longPressRunnable);
|
||||||
handler.removeCallbacks(longPressStartRunnable);
|
handler.removeCallbacks(longPressStartRunnable);
|
||||||
|
if (onItemLongClickListener != null) {
|
||||||
|
onItemLongClickListener.onLongClickCancel(this);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
final boolean dispatchTouchEvent = super.dispatchTouchEvent(ev);
|
final boolean dispatchTouchEvent = super.dispatchTouchEvent(ev);
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -41,9 +41,11 @@ import androidx.navigation.NavBackStackEntry;
|
|||||||
import androidx.navigation.NavController;
|
import androidx.navigation.NavController;
|
||||||
import androidx.navigation.NavDirections;
|
import androidx.navigation.NavDirections;
|
||||||
import androidx.navigation.fragment.NavHostFragment;
|
import androidx.navigation.fragment.NavHostFragment;
|
||||||
|
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import androidx.recyclerview.widget.SimpleItemAnimator;
|
import androidx.recyclerview.widget.SimpleItemAnimator;
|
||||||
|
import androidx.transition.TransitionManager;
|
||||||
import androidx.vectordrawable.graphics.drawable.Animatable2Compat;
|
import androidx.vectordrawable.graphics.drawable.Animatable2Compat;
|
||||||
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat;
|
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat;
|
||||||
|
|
||||||
@ -73,6 +75,7 @@ import awais.instagrabber.customviews.emoji.Emoji;
|
|||||||
import awais.instagrabber.customviews.helpers.HeaderItemDecoration;
|
import awais.instagrabber.customviews.helpers.HeaderItemDecoration;
|
||||||
import awais.instagrabber.customviews.helpers.HeightProvider;
|
import awais.instagrabber.customviews.helpers.HeightProvider;
|
||||||
import awais.instagrabber.customviews.helpers.RecyclerLazyLoaderAtEdge;
|
import awais.instagrabber.customviews.helpers.RecyclerLazyLoaderAtEdge;
|
||||||
|
import awais.instagrabber.customviews.helpers.SwipeAndRestoreItemTouchHelperCallback;
|
||||||
import awais.instagrabber.customviews.helpers.TextWatcherAdapter;
|
import awais.instagrabber.customviews.helpers.TextWatcherAdapter;
|
||||||
import awais.instagrabber.databinding.FragmentDirectMessagesThreadBinding;
|
import awais.instagrabber.databinding.FragmentDirectMessagesThreadBinding;
|
||||||
import awais.instagrabber.dialogs.DirectItemReactionDialogFragment;
|
import awais.instagrabber.dialogs.DirectItemReactionDialogFragment;
|
||||||
@ -81,16 +84,19 @@ import awais.instagrabber.fragments.PostViewV2Fragment;
|
|||||||
import awais.instagrabber.fragments.UserSearchFragment;
|
import awais.instagrabber.fragments.UserSearchFragment;
|
||||||
import awais.instagrabber.fragments.UserSearchFragmentDirections;
|
import awais.instagrabber.fragments.UserSearchFragmentDirections;
|
||||||
import awais.instagrabber.models.Resource;
|
import awais.instagrabber.models.Resource;
|
||||||
|
import awais.instagrabber.models.enums.MediaItemType;
|
||||||
import awais.instagrabber.repositories.requests.StoryViewerOptions;
|
import awais.instagrabber.repositories.requests.StoryViewerOptions;
|
||||||
import awais.instagrabber.repositories.responses.Media;
|
import awais.instagrabber.repositories.responses.Media;
|
||||||
import awais.instagrabber.repositories.responses.User;
|
import awais.instagrabber.repositories.responses.User;
|
||||||
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
|
import awais.instagrabber.repositories.responses.directmessages.DirectItem;
|
||||||
import awais.instagrabber.repositories.responses.directmessages.DirectItemEmojiReaction;
|
import awais.instagrabber.repositories.responses.directmessages.DirectItemEmojiReaction;
|
||||||
import awais.instagrabber.repositories.responses.directmessages.DirectItemStoryShare;
|
import awais.instagrabber.repositories.responses.directmessages.DirectItemStoryShare;
|
||||||
|
import awais.instagrabber.repositories.responses.directmessages.DirectItemVisualMedia;
|
||||||
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
|
import awais.instagrabber.repositories.responses.directmessages.DirectThread;
|
||||||
import awais.instagrabber.repositories.responses.directmessages.RankedRecipient;
|
import awais.instagrabber.repositories.responses.directmessages.RankedRecipient;
|
||||||
import awais.instagrabber.utils.AppExecutors;
|
import awais.instagrabber.utils.AppExecutors;
|
||||||
import awais.instagrabber.utils.PermissionUtils;
|
import awais.instagrabber.utils.PermissionUtils;
|
||||||
|
import awais.instagrabber.utils.ResponseBodyUtils;
|
||||||
import awais.instagrabber.utils.TextUtils;
|
import awais.instagrabber.utils.TextUtils;
|
||||||
import awais.instagrabber.utils.Utils;
|
import awais.instagrabber.utils.Utils;
|
||||||
import awais.instagrabber.viewmodels.AppStateViewModel;
|
import awais.instagrabber.viewmodels.AppStateViewModel;
|
||||||
@ -133,6 +139,7 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
|||||||
private DirectItemReactionDialogFragment reactionDialogFragment;
|
private DirectItemReactionDialogFragment reactionDialogFragment;
|
||||||
private DirectItem itemToForward;
|
private DirectItem itemToForward;
|
||||||
private MutableLiveData<Object> backStackSavedStateResultLiveData;
|
private MutableLiveData<Object> backStackSavedStateResultLiveData;
|
||||||
|
private int prevLength;
|
||||||
|
|
||||||
private final AppExecutors appExecutors = AppExecutors.getInstance();
|
private final AppExecutors appExecutors = AppExecutors.getInstance();
|
||||||
private final Animatable2Compat.AnimationCallback micToSendAnimationCallback = new Animatable2Compat.AnimationCallback() {
|
private final Animatable2Compat.AnimationCallback micToSendAnimationCallback = new Animatable2Compat.AnimationCallback() {
|
||||||
@ -252,7 +259,6 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private final DirectItemLongClickListener directItemLongClickListener = position -> {
|
private final DirectItemLongClickListener directItemLongClickListener = position -> {
|
||||||
// viewModel.setSelectedPosition(position);
|
// viewModel.setSelectedPosition(position);
|
||||||
};
|
};
|
||||||
@ -280,6 +286,7 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
|||||||
// clear result
|
// clear result
|
||||||
backStackSavedStateResultLiveData.postValue(null);
|
backStackSavedStateResultLiveData.postValue(null);
|
||||||
};
|
};
|
||||||
|
private final MutableLiveData<Integer> inputLength = new MutableLiveData<>(0);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||||
@ -491,6 +498,17 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
binding.chats.addItemDecoration(headerItemDecoration);
|
binding.chats.addItemDecoration(headerItemDecoration);
|
||||||
|
final SwipeAndRestoreItemTouchHelperCallback touchHelperCallback = new SwipeAndRestoreItemTouchHelperCallback(
|
||||||
|
context,
|
||||||
|
(adapterPosition, viewHolder) -> {
|
||||||
|
if (itemsAdapter == null) return;
|
||||||
|
final DirectItemOrHeader directItemOrHeader = itemsAdapter.getList().get(adapterPosition);
|
||||||
|
if (directItemOrHeader.isHeader()) return;
|
||||||
|
viewModel.setReplyToItem(directItemOrHeader.item);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
final ItemTouchHelper itemTouchHelper = new ItemTouchHelper(touchHelperCallback);
|
||||||
|
itemTouchHelper.attachToRecyclerView(binding.chats);
|
||||||
// final MentionClickListener mentionClickListener = (view, text, isHashtag, isLocation) -> searchUsername(text);
|
// final MentionClickListener mentionClickListener = (view, text, isHashtag, isLocation) -> searchUsername(text);
|
||||||
// final DialogInterface.OnClickListener onDialogListener = (dialogInterface, which) -> {
|
// final DialogInterface.OnClickListener onDialogListener = (dialogInterface, which) -> {
|
||||||
// if (which == 0) {
|
// if (which == 0) {
|
||||||
@ -632,6 +650,141 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
|||||||
setupItemsAdapter(userThreadPair.first, userThreadPair.second);
|
setupItemsAdapter(userThreadPair.first, userThreadPair.second);
|
||||||
});
|
});
|
||||||
viewModel.getItems().observe(getViewLifecycleOwner(), this::submitItemsToAdapter);
|
viewModel.getItems().observe(getViewLifecycleOwner(), this::submitItemsToAdapter);
|
||||||
|
viewModel.getReplyToItem().observe(getViewLifecycleOwner(), item -> {
|
||||||
|
if (item == null) {
|
||||||
|
if (binding.input.length() == 0) {
|
||||||
|
showExtraInputOption(true);
|
||||||
|
}
|
||||||
|
binding.getRoot().post(() -> {
|
||||||
|
TransitionManager.beginDelayedTransition(binding.getRoot());
|
||||||
|
binding.replyBg.setVisibility(View.GONE);
|
||||||
|
binding.replyInfo.setVisibility(View.GONE);
|
||||||
|
binding.replyPreviewImage.setVisibility(View.GONE);
|
||||||
|
binding.replyCancel.setVisibility(View.GONE);
|
||||||
|
binding.replyPreviewText.setVisibility(View.GONE);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
showExtraInputOption(false);
|
||||||
|
binding.getRoot().postDelayed(() -> {
|
||||||
|
binding.replyBg.setVisibility(View.VISIBLE);
|
||||||
|
binding.replyInfo.setVisibility(View.VISIBLE);
|
||||||
|
binding.replyPreviewImage.setVisibility(View.VISIBLE);
|
||||||
|
binding.replyCancel.setVisibility(View.VISIBLE);
|
||||||
|
binding.replyPreviewText.setVisibility(View.VISIBLE);
|
||||||
|
if (item.getUserId() == viewModel.getViewerId()) {
|
||||||
|
binding.replyInfo.setText(R.string.replying_to_yourself);
|
||||||
|
} else {
|
||||||
|
final User user = viewModel.getUser(item.getUserId());
|
||||||
|
if (user != null) {
|
||||||
|
binding.replyInfo.setText(getString(R.string.replying_to_user, user.getFullName()));
|
||||||
|
} else {
|
||||||
|
binding.replyInfo.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final String previewText = getDirectItemPreviewText(item);
|
||||||
|
binding.replyPreviewText.setText(TextUtils.isEmpty(previewText) ? getString(R.string.message) : previewText);
|
||||||
|
final String previewImageUrl = getDirectItemPreviewImageUrl(item);
|
||||||
|
if (TextUtils.isEmpty(previewImageUrl)) {
|
||||||
|
binding.replyPreviewImage.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
binding.replyPreviewImage.setImageURI(previewImageUrl);
|
||||||
|
}
|
||||||
|
binding.replyCancel.setOnClickListener(v -> viewModel.setReplyToItem(null));
|
||||||
|
}, 200);
|
||||||
|
});
|
||||||
|
inputLength.observe(getViewLifecycleOwner(), length -> {
|
||||||
|
if (length == null) return;
|
||||||
|
final boolean hasReplyToItem = viewModel.getReplyToItem().getValue() != null;
|
||||||
|
if (hasReplyToItem) {
|
||||||
|
prevLength = length;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((prevLength == 0 && length != 0) || (prevLength != 0 && length == 0)) {
|
||||||
|
showExtraInputOption(length == 0);
|
||||||
|
}
|
||||||
|
prevLength = length;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showExtraInputOption(final boolean show) {
|
||||||
|
if (show) {
|
||||||
|
if (!binding.send.isListenForRecord()) {
|
||||||
|
binding.send.setListenForRecord(true);
|
||||||
|
startIconAnimation();
|
||||||
|
}
|
||||||
|
binding.gallery.setVisibility(View.VISIBLE);
|
||||||
|
binding.camera.setVisibility(View.VISIBLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (binding.send.isListenForRecord()) {
|
||||||
|
binding.send.setListenForRecord(false);
|
||||||
|
startIconAnimation();
|
||||||
|
}
|
||||||
|
binding.gallery.setVisibility(View.GONE);
|
||||||
|
binding.camera.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getDirectItemPreviewText(final DirectItem item) {
|
||||||
|
switch (item.getItemType()) {
|
||||||
|
case TEXT:
|
||||||
|
return item.getText();
|
||||||
|
case LINK:
|
||||||
|
return item.getLink().getText();
|
||||||
|
case MEDIA: {
|
||||||
|
final Media media = item.getMedia();
|
||||||
|
return getMediaPreviewTextString(media);
|
||||||
|
}
|
||||||
|
case RAVEN_MEDIA: {
|
||||||
|
final DirectItemVisualMedia visualMedia = item.getVisualMedia();
|
||||||
|
final Media media = visualMedia.getMedia();
|
||||||
|
return getMediaPreviewTextString(media);
|
||||||
|
}
|
||||||
|
case VOICE_MEDIA:
|
||||||
|
return getString(R.string.voice_message);
|
||||||
|
case MEDIA_SHARE:
|
||||||
|
return getString(R.string.post);
|
||||||
|
case REEL_SHARE:
|
||||||
|
return item.getReelShare().getText();
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private String getMediaPreviewTextString(final Media media) {
|
||||||
|
final MediaItemType mediaType = media.getMediaType();
|
||||||
|
switch (mediaType) {
|
||||||
|
case MEDIA_TYPE_IMAGE:
|
||||||
|
return getString(R.string.photo);
|
||||||
|
case MEDIA_TYPE_VIDEO:
|
||||||
|
return getString(R.string.video);
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getDirectItemPreviewImageUrl(final DirectItem item) {
|
||||||
|
switch (item.getItemType()) {
|
||||||
|
case TEXT:
|
||||||
|
case LINK:
|
||||||
|
case VOICE_MEDIA:
|
||||||
|
case REEL_SHARE:
|
||||||
|
return null;
|
||||||
|
case MEDIA: {
|
||||||
|
final Media media = item.getMedia();
|
||||||
|
return ResponseBodyUtils.getThumbUrl(media);
|
||||||
|
}
|
||||||
|
case RAVEN_MEDIA: {
|
||||||
|
final DirectItemVisualMedia visualMedia = item.getVisualMedia();
|
||||||
|
final Media media = visualMedia.getMedia();
|
||||||
|
return ResponseBodyUtils.getThumbUrl(media);
|
||||||
|
}
|
||||||
|
case MEDIA_SHARE: {
|
||||||
|
final Media media = item.getMediaShare();
|
||||||
|
return ResponseBodyUtils.getThumbUrl(media);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupBackStackResultObserver() {
|
private void setupBackStackResultObserver() {
|
||||||
@ -750,33 +903,29 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
|||||||
binding.camera.setVisibility(View.VISIBLE);
|
binding.camera.setVisibility(View.VISIBLE);
|
||||||
});
|
});
|
||||||
binding.input.addTextChangedListener(new TextWatcherAdapter() {
|
binding.input.addTextChangedListener(new TextWatcherAdapter() {
|
||||||
int prevLength = 0;
|
// int prevLength = 0;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTextChanged(final CharSequence s, final int start, final int before, final int count) {
|
public void onTextChanged(final CharSequence s, final int start, final int before, final int count) {
|
||||||
final int length = s.length();
|
final int length = s.length();
|
||||||
if (prevLength != 0 && length == 0) {
|
inputLength.postValue(length);
|
||||||
binding.send.setListenForRecord(true);
|
// boolean showExtraInputOptionsChanged = false;
|
||||||
startIconAnimation();
|
// if (prevLength != 0 && length == 0) {
|
||||||
}
|
// inputLength.postValue(true);
|
||||||
if (prevLength == 0 && length != 0) {
|
// showExtraInputOptionsChanged = true;
|
||||||
binding.send.setListenForRecord(false);
|
// binding.send.setListenForRecord(true);
|
||||||
startIconAnimation();
|
// startIconAnimation();
|
||||||
}
|
// }
|
||||||
binding.gallery.setVisibility(length == 0 ? View.VISIBLE : View.GONE);
|
// if (prevLength == 0 && length != 0) {
|
||||||
binding.camera.setVisibility(length == 0 ? View.VISIBLE : View.GONE);
|
// inputLength.postValue(false);
|
||||||
prevLength = length;
|
// showExtraInputOptionsChanged = true;
|
||||||
}
|
// binding.send.setListenForRecord(false);
|
||||||
|
// startIconAnimation();
|
||||||
private void startIconAnimation() {
|
// }
|
||||||
final Drawable icon = binding.send.getIcon();
|
// if (!showExtraInputOptionsChanged) {
|
||||||
if (icon instanceof Animatable) {
|
// showExtraInputOptions.postValue(length == 0);
|
||||||
final Animatable animatable = (Animatable) icon;
|
// }
|
||||||
if (animatable.isRunning()) {
|
// prevLength = length;
|
||||||
animatable.stop();
|
|
||||||
}
|
|
||||||
animatable.start();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
binding.send.setOnRecordClickListener(v -> {
|
binding.send.setOnRecordClickListener(v -> {
|
||||||
@ -785,6 +934,7 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
|||||||
final LiveData<Resource<DirectItem>> resourceLiveData = viewModel.sendText(text.toString());
|
final LiveData<Resource<DirectItem>> resourceLiveData = viewModel.sendText(text.toString());
|
||||||
resourceLiveData.observe(getViewLifecycleOwner(), resource -> handleSentMessage(resourceLiveData));
|
resourceLiveData.observe(getViewLifecycleOwner(), resource -> handleSentMessage(resourceLiveData));
|
||||||
binding.input.setText("");
|
binding.input.setText("");
|
||||||
|
viewModel.setReplyToItem(null);
|
||||||
});
|
});
|
||||||
binding.send.setOnRecordLongClickListener(v -> {
|
binding.send.setOnRecordLongClickListener(v -> {
|
||||||
Log.d(TAG, "setOnRecordLongClickListener");
|
Log.d(TAG, "setOnRecordLongClickListener");
|
||||||
@ -833,6 +983,17 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void startIconAnimation() {
|
||||||
|
final Drawable icon = binding.send.getIcon();
|
||||||
|
if (icon instanceof Animatable) {
|
||||||
|
final Animatable animatable = (Animatable) icon;
|
||||||
|
if (animatable.isRunning()) {
|
||||||
|
animatable.stop();
|
||||||
|
}
|
||||||
|
animatable.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void navigateToImageEditFragment(final String path) {
|
private void navigateToImageEditFragment(final String path) {
|
||||||
navigateToImageEditFragment(Uri.fromFile(new File(path)));
|
navigateToImageEditFragment(Uri.fromFile(new File(path)));
|
||||||
}
|
}
|
||||||
@ -1168,6 +1329,11 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
|
|||||||
ObjectAnimator.ofFloat(binding.gallery, TRANSLATION_Y, -height),
|
ObjectAnimator.ofFloat(binding.gallery, TRANSLATION_Y, -height),
|
||||||
ObjectAnimator.ofFloat(binding.camera, TRANSLATION_Y, -height),
|
ObjectAnimator.ofFloat(binding.camera, TRANSLATION_Y, -height),
|
||||||
ObjectAnimator.ofFloat(binding.send, TRANSLATION_Y, -height),
|
ObjectAnimator.ofFloat(binding.send, TRANSLATION_Y, -height),
|
||||||
|
ObjectAnimator.ofFloat(binding.replyBg, TRANSLATION_Y, -height),
|
||||||
|
ObjectAnimator.ofFloat(binding.replyInfo, TRANSLATION_Y, -height),
|
||||||
|
ObjectAnimator.ofFloat(binding.replyCancel, TRANSLATION_Y, -height),
|
||||||
|
ObjectAnimator.ofFloat(binding.replyPreviewImage, TRANSLATION_Y, -height),
|
||||||
|
ObjectAnimator.ofFloat(binding.replyPreviewText, TRANSLATION_Y, -height),
|
||||||
ObjectAnimator.ofFloat(binding.emojiPicker, TRANSLATION_Y, keyboardHeight - height)
|
ObjectAnimator.ofFloat(binding.emojiPicker, TRANSLATION_Y, keyboardHeight - height)
|
||||||
);
|
);
|
||||||
// if (headerItemDecoration != null && headerItemDecoration.getCurrentHeader() != null) {
|
// if (headerItemDecoration != null && headerItemDecoration.getCurrentHeader() != null) {
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
package awais.instagrabber.models.enums;
|
package awais.instagrabber.models.enums;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName;
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
@ -65,7 +63,6 @@ public enum DirectItemType implements Serializable {
|
|||||||
return map.get(id);
|
return map.get(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
switch (this) {
|
switch (this) {
|
||||||
case TEXT:
|
case TEXT:
|
||||||
@ -102,8 +99,7 @@ public enum DirectItemType implements Serializable {
|
|||||||
return "felix_share";
|
return "felix_share";
|
||||||
case LOCATION:
|
case LOCATION:
|
||||||
return "location";
|
return "location";
|
||||||
default:
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
@ -13,6 +13,9 @@ public abstract class BroadcastOptions {
|
|||||||
private final ThreadIdOrUserIds threadIdOrUserIds;
|
private final ThreadIdOrUserIds threadIdOrUserIds;
|
||||||
private final BroadcastItemType itemType;
|
private final BroadcastItemType itemType;
|
||||||
|
|
||||||
|
private String repliedToItemId;
|
||||||
|
private String repliedToClientContext;
|
||||||
|
|
||||||
public BroadcastOptions(final String clientContext,
|
public BroadcastOptions(final String clientContext,
|
||||||
@NonNull final ThreadIdOrUserIds threadIdOrUserIds,
|
@NonNull final ThreadIdOrUserIds threadIdOrUserIds,
|
||||||
@NonNull final BroadcastItemType itemType) {
|
@NonNull final BroadcastItemType itemType) {
|
||||||
@ -39,6 +42,22 @@ public abstract class BroadcastOptions {
|
|||||||
|
|
||||||
public abstract Map<String, String> getFormMap();
|
public abstract Map<String, String> getFormMap();
|
||||||
|
|
||||||
|
public String getRepliedToItemId() {
|
||||||
|
return repliedToItemId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRepliedToItemId(final String repliedToItemId) {
|
||||||
|
this.repliedToItemId = repliedToItemId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRepliedToClientContext() {
|
||||||
|
return repliedToClientContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRepliedToClientContext(final String repliedToClientContext) {
|
||||||
|
this.repliedToClientContext = repliedToClientContext;
|
||||||
|
}
|
||||||
|
|
||||||
public static final class ThreadIdOrUserIds {
|
public static final class ThreadIdOrUserIds {
|
||||||
private final String threadId;
|
private final String threadId;
|
||||||
private final List<String> userIds;
|
private final List<String> userIds;
|
||||||
|
@ -11,7 +11,7 @@ import awais.instagrabber.repositories.responses.Media;
|
|||||||
import awais.instagrabber.repositories.responses.User;
|
import awais.instagrabber.repositories.responses.User;
|
||||||
|
|
||||||
public class DirectItem implements Cloneable {
|
public class DirectItem implements Cloneable {
|
||||||
private final String itemId;
|
private String itemId;
|
||||||
private final long userId;
|
private final long userId;
|
||||||
private long timestamp;
|
private long timestamp;
|
||||||
private final DirectItemType itemType;
|
private final DirectItemType itemType;
|
||||||
@ -213,6 +213,10 @@ public class DirectItem implements Cloneable {
|
|||||||
return date;
|
return date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setItemId(final String itemId) {
|
||||||
|
this.itemId = itemId;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isPending() {
|
public boolean isPending() {
|
||||||
return isPending;
|
return isPending;
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,8 @@ public class DirectItemFactory {
|
|||||||
|
|
||||||
public static DirectItem createText(final long userId,
|
public static DirectItem createText(final long userId,
|
||||||
final String clientContext,
|
final String clientContext,
|
||||||
final String text) {
|
final String text,
|
||||||
|
final DirectItem repliedToMessage) {
|
||||||
return new DirectItem(
|
return new DirectItem(
|
||||||
UUID.randomUUID().toString(),
|
UUID.randomUUID().toString(),
|
||||||
userId,
|
userId,
|
||||||
@ -44,7 +45,7 @@ public class DirectItemFactory {
|
|||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null,
|
repliedToMessage,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
0,
|
0,
|
||||||
|
@ -62,8 +62,8 @@ public final class Utils {
|
|||||||
public static SettingsHelper settingsHelper;
|
public static SettingsHelper settingsHelper;
|
||||||
public static boolean sessionVolumeFull = false;
|
public static boolean sessionVolumeFull = false;
|
||||||
public static final MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();
|
public static final MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();
|
||||||
|
public static final DisplayMetrics displayMetrics = Resources.getSystem().getDisplayMetrics();
|
||||||
public static ClipboardManager clipboardManager;
|
public static ClipboardManager clipboardManager;
|
||||||
public static DisplayMetrics displayMetrics = Resources.getSystem().getDisplayMetrics();
|
|
||||||
public static SimpleDateFormat datetimeParser;
|
public static SimpleDateFormat datetimeParser;
|
||||||
public static SimpleCache simpleCache;
|
public static SimpleCache simpleCache;
|
||||||
private static int statusBarHeight;
|
private static int statusBarHeight;
|
||||||
@ -73,9 +73,7 @@ public final class Utils {
|
|||||||
private static int defaultStatusBarColor;
|
private static int defaultStatusBarColor;
|
||||||
|
|
||||||
public static int convertDpToPx(final float dp) {
|
public static int convertDpToPx(final float dp) {
|
||||||
if (displayMetrics == null)
|
return Math.round((dp * displayMetrics.densityDpi) / DisplayMetrics.DENSITY_DEFAULT);
|
||||||
displayMetrics = Resources.getSystem().getDisplayMetrics();
|
|
||||||
return Math.round((dp * displayMetrics.densityDpi) / 160.0f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void copyText(@NonNull final Context context, final CharSequence string) {
|
public static void copyText(@NonNull final Context context, final CharSequence string) {
|
||||||
|
@ -81,6 +81,7 @@ public class DirectThreadViewModel extends AndroidViewModel {
|
|||||||
private final MutableLiveData<Boolean> fetching = new MutableLiveData<>(false);
|
private final MutableLiveData<Boolean> fetching = new MutableLiveData<>(false);
|
||||||
private final MutableLiveData<List<User>> users = new MutableLiveData<>(new ArrayList<>());
|
private final MutableLiveData<List<User>> users = new MutableLiveData<>(new ArrayList<>());
|
||||||
private final MutableLiveData<List<User>> leftUsers = new MutableLiveData<>(new ArrayList<>());
|
private final MutableLiveData<List<User>> leftUsers = new MutableLiveData<>(new ArrayList<>());
|
||||||
|
private final MutableLiveData<DirectItem> replyToItem = new MutableLiveData<>();
|
||||||
|
|
||||||
private final DirectMessagesService service;
|
private final DirectMessagesService service;
|
||||||
private final ContentResolver contentResolver;
|
private final ContentResolver contentResolver;
|
||||||
@ -285,7 +286,7 @@ public class DirectThreadViewModel extends AndroidViewModel {
|
|||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateItemSent(final String clientContext, final long timestamp) {
|
private void updateItemSent(final String clientContext, final long timestamp, final String itemId) {
|
||||||
if (clientContext == null) return;
|
if (clientContext == null) return;
|
||||||
List<DirectItem> list = this.items.getValue();
|
List<DirectItem> list = this.items.getValue();
|
||||||
list = list == null ? new LinkedList<>() : new LinkedList<>(list);
|
list = list == null ? new LinkedList<>() : new LinkedList<>(list);
|
||||||
@ -297,6 +298,7 @@ public class DirectThreadViewModel extends AndroidViewModel {
|
|||||||
final DirectItem directItem = list.get(index);
|
final DirectItem directItem = list.get(index);
|
||||||
try {
|
try {
|
||||||
final DirectItem itemClone = (DirectItem) directItem.clone();
|
final DirectItem itemClone = (DirectItem) directItem.clone();
|
||||||
|
itemClone.setItemId(itemId);
|
||||||
itemClone.setPending(false);
|
itemClone.setPending(false);
|
||||||
itemClone.setTimestamp(timestamp);
|
itemClone.setTimestamp(timestamp);
|
||||||
list.set(index, itemClone);
|
list.set(index, itemClone);
|
||||||
@ -322,6 +324,10 @@ public class DirectThreadViewModel extends AndroidViewModel {
|
|||||||
return leftUsers;
|
return leftUsers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LiveData<DirectItem> getReplyToItem() {
|
||||||
|
return replyToItem;
|
||||||
|
}
|
||||||
|
|
||||||
public void fetchChats() {
|
public void fetchChats() {
|
||||||
final Boolean isFetching = fetching.getValue();
|
final Boolean isFetching = fetching.getValue();
|
||||||
if ((isFetching != null && isFetching) || !hasOlder) return;
|
if ((isFetching != null && isFetching) || !hasOlder) return;
|
||||||
@ -392,12 +398,21 @@ public class DirectThreadViewModel extends AndroidViewModel {
|
|||||||
final Long userId = handleCurrentUser(data);
|
final Long userId = handleCurrentUser(data);
|
||||||
if (userId == null) return data;
|
if (userId == null) return data;
|
||||||
final String clientContext = UUID.randomUUID().toString();
|
final String clientContext = UUID.randomUUID().toString();
|
||||||
final DirectItem directItem = DirectItemFactory.createText(userId, clientContext, text);
|
final DirectItem replyToItemValue = replyToItem.getValue();
|
||||||
|
final DirectItem directItem = DirectItemFactory.createText(userId, clientContext, text, replyToItemValue);
|
||||||
// Log.d(TAG, "sendText: sending: itemId: " + directItem.getItemId());
|
// Log.d(TAG, "sendText: sending: itemId: " + directItem.getItemId());
|
||||||
directItem.setPending(true);
|
directItem.setPending(true);
|
||||||
addItems(0, Collections.singletonList(directItem));
|
addItems(0, Collections.singletonList(directItem));
|
||||||
data.postValue(Resource.loading(directItem));
|
data.postValue(Resource.loading(directItem));
|
||||||
final Call<DirectThreadBroadcastResponse> request = service.broadcastText(clientContext, threadIdOrUserIds, text);
|
final String repliedToItemId = replyToItemValue != null ? replyToItemValue.getItemId() : null;
|
||||||
|
final String repliedToClientContext = replyToItemValue != null ? replyToItemValue.getClientContext() : null;
|
||||||
|
final Call<DirectThreadBroadcastResponse> request = service.broadcastText(
|
||||||
|
clientContext,
|
||||||
|
threadIdOrUserIds,
|
||||||
|
text,
|
||||||
|
repliedToItemId,
|
||||||
|
repliedToClientContext
|
||||||
|
);
|
||||||
enqueueRequest(request, data, directItem);
|
enqueueRequest(request, data, directItem);
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
@ -848,6 +863,7 @@ public class DirectThreadViewModel extends AndroidViewModel {
|
|||||||
}
|
}
|
||||||
final String payloadClientContext;
|
final String payloadClientContext;
|
||||||
final long timestamp;
|
final long timestamp;
|
||||||
|
final String itemId;
|
||||||
final DirectThreadBroadcastResponsePayload payload = broadcastResponse.getPayload();
|
final DirectThreadBroadcastResponsePayload payload = broadcastResponse.getPayload();
|
||||||
if (payload == null) {
|
if (payload == null) {
|
||||||
final List<DirectThreadBroadcastResponseMessageMetadata> messageMetadata = broadcastResponse.getMessageMetadata();
|
final List<DirectThreadBroadcastResponseMessageMetadata> messageMetadata = broadcastResponse.getMessageMetadata();
|
||||||
@ -857,12 +873,14 @@ public class DirectThreadViewModel extends AndroidViewModel {
|
|||||||
}
|
}
|
||||||
final DirectThreadBroadcastResponseMessageMetadata metadata = messageMetadata.get(0);
|
final DirectThreadBroadcastResponseMessageMetadata metadata = messageMetadata.get(0);
|
||||||
payloadClientContext = metadata.getClientContext();
|
payloadClientContext = metadata.getClientContext();
|
||||||
|
itemId = metadata.getItemId();
|
||||||
timestamp = metadata.getTimestamp();
|
timestamp = metadata.getTimestamp();
|
||||||
} else {
|
} else {
|
||||||
payloadClientContext = payload.getClientContext();
|
payloadClientContext = payload.getClientContext();
|
||||||
timestamp = payload.getTimestamp();
|
timestamp = payload.getTimestamp();
|
||||||
|
itemId = payload.getItemId();
|
||||||
}
|
}
|
||||||
updateItemSent(payloadClientContext, timestamp);
|
updateItemSent(payloadClientContext, timestamp, itemId);
|
||||||
data.postValue(Resource.success(directItem));
|
data.postValue(Resource.success(directItem));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -987,8 +1005,13 @@ public class DirectThreadViewModel extends AndroidViewModel {
|
|||||||
|
|
||||||
private void forward(@NonNull final DirectThread thread, @NonNull final DirectItem itemToForward) {
|
private void forward(@NonNull final DirectThread thread, @NonNull final DirectItem itemToForward) {
|
||||||
final DirectItemType itemType = itemToForward.getItemType();
|
final DirectItemType itemType = itemToForward.getItemType();
|
||||||
|
final String itemTypeName = itemType.getName();
|
||||||
|
if (itemTypeName == null) {
|
||||||
|
Log.e(TAG, "forward: itemTypeName was null!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
final Call<DirectThreadBroadcastResponse> request = service.forward(thread.getThreadId(),
|
final Call<DirectThreadBroadcastResponse> request = service.forward(thread.getThreadId(),
|
||||||
itemType.getName(),
|
itemTypeName,
|
||||||
threadId,
|
threadId,
|
||||||
itemToForward.getItemId());
|
itemToForward.getItemId());
|
||||||
request.enqueue(new Callback<DirectThreadBroadcastResponse>() {
|
request.enqueue(new Callback<DirectThreadBroadcastResponse>() {
|
||||||
@ -1019,4 +1042,9 @@ public class DirectThreadViewModel extends AndroidViewModel {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setReplyToItem(final DirectItem item) {
|
||||||
|
// Log.d(TAG, "setReplyToItem: " + item);
|
||||||
|
replyToItem.postValue(item);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,19 +119,29 @@ public class DirectMessagesService extends BaseService {
|
|||||||
|
|
||||||
public Call<DirectThreadBroadcastResponse> broadcastText(final String clientContext,
|
public Call<DirectThreadBroadcastResponse> broadcastText(final String clientContext,
|
||||||
final ThreadIdOrUserIds threadIdOrUserIds,
|
final ThreadIdOrUserIds threadIdOrUserIds,
|
||||||
final String text) {
|
final String text,
|
||||||
|
final String repliedToItemId,
|
||||||
|
final String repliedToClientContext) {
|
||||||
final List<String> urls = TextUtils.extractUrls(text);
|
final List<String> urls = TextUtils.extractUrls(text);
|
||||||
if (!urls.isEmpty()) {
|
if (!urls.isEmpty()) {
|
||||||
return broadcastLink(clientContext, threadIdOrUserIds, text, urls);
|
return broadcastLink(clientContext, threadIdOrUserIds, text, urls, repliedToItemId, repliedToClientContext);
|
||||||
}
|
}
|
||||||
return broadcast(new TextBroadcastOptions(clientContext, threadIdOrUserIds, text));
|
final TextBroadcastOptions broadcastOptions = new TextBroadcastOptions(clientContext, threadIdOrUserIds, text);
|
||||||
|
broadcastOptions.setRepliedToItemId(repliedToItemId);
|
||||||
|
broadcastOptions.setRepliedToClientContext(repliedToClientContext);
|
||||||
|
return broadcast(broadcastOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Call<DirectThreadBroadcastResponse> broadcastLink(final String clientContext,
|
public Call<DirectThreadBroadcastResponse> broadcastLink(final String clientContext,
|
||||||
final ThreadIdOrUserIds threadIdOrUserIds,
|
final ThreadIdOrUserIds threadIdOrUserIds,
|
||||||
final String linkText,
|
final String linkText,
|
||||||
final List<String> urls) {
|
final List<String> urls,
|
||||||
return broadcast(new LinkBroadcastOptions(clientContext, threadIdOrUserIds, linkText, urls));
|
final String repliedToItemId,
|
||||||
|
final String repliedToClientContext) {
|
||||||
|
final LinkBroadcastOptions broadcastOptions = new LinkBroadcastOptions(clientContext, threadIdOrUserIds, linkText, urls);
|
||||||
|
broadcastOptions.setRepliedToItemId(repliedToItemId);
|
||||||
|
broadcastOptions.setRepliedToClientContext(repliedToClientContext);
|
||||||
|
return broadcast(broadcastOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Call<DirectThreadBroadcastResponse> broadcastPhoto(final String clientContext,
|
public Call<DirectThreadBroadcastResponse> broadcastPhoto(final String clientContext,
|
||||||
@ -187,6 +197,10 @@ public class DirectMessagesService extends BaseService {
|
|||||||
form.put("__uuid", deviceUuid);
|
form.put("__uuid", deviceUuid);
|
||||||
form.put("client_context", broadcastOptions.getClientContext());
|
form.put("client_context", broadcastOptions.getClientContext());
|
||||||
form.put("mutation_token", broadcastOptions.getClientContext());
|
form.put("mutation_token", broadcastOptions.getClientContext());
|
||||||
|
if (!TextUtils.isEmpty(broadcastOptions.getRepliedToItemId()) && !TextUtils.isEmpty(broadcastOptions.getRepliedToClientContext())) {
|
||||||
|
form.put("replied_to_item_id", broadcastOptions.getRepliedToItemId());
|
||||||
|
form.put("replied_to_client_context", broadcastOptions.getRepliedToClientContext());
|
||||||
|
}
|
||||||
form.putAll(broadcastOptions.getFormMap());
|
form.putAll(broadcastOptions.getFormMap());
|
||||||
form.put("action", "send_item");
|
form.put("action", "send_item");
|
||||||
final Map<String, String> signedForm = Utils.sign(form);
|
final Map<String, String> signedForm = Utils.sign(form);
|
||||||
|
10
app/src/main/res/drawable/ic_round_reply_24.xml
Normal file
10
app/src/main/res/drawable/ic_round_reply_24.xml
Normal file
@ -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>
|
@ -11,12 +11,98 @@
|
|||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:scrollbars="none"
|
android:scrollbars="none"
|
||||||
app:layout_constraintBottom_toTopOf="@id/input"
|
app:layout_constraintBottom_toTopOf="@id/reply_info"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:listitem="@layout/layout_dm_base" />
|
tools:listitem="@layout/layout_dm_base" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/reply_bg"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:background="@drawable/bg_input"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/input_bg"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/input_bg"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/input_bg"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/reply_info"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/reply_info"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingTop="8dp"
|
||||||
|
android:paddingEnd="16dp"
|
||||||
|
android:paddingBottom="4dp"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Caption"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/reply_preview_text"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/reply_preview_image"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/input_bg"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/chats"
|
||||||
|
tools:text="Replying to yourself"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<androidx.emoji.widget.EmojiAppCompatTextView
|
||||||
|
android:id="@+id/reply_preview_text"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingTop="4dp"
|
||||||
|
android:paddingEnd="16dp"
|
||||||
|
android:paddingBottom="8dp"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Caption"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/input_bg"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/reply_preview_image"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/input_bg"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/reply_info"
|
||||||
|
app:layout_goneMarginTop="8dp"
|
||||||
|
tools:text="Post"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<com.facebook.drawee.view.SimpleDraweeView
|
||||||
|
android:id="@+id/reply_preview_image"
|
||||||
|
android:layout_width="@dimen/dm_inbox_avatar_size_small"
|
||||||
|
android:layout_height="@dimen/dm_inbox_avatar_size_small"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:actualImageScaleType="centerCrop"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/reply_preview_text"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/reply_cancel"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/reply_preview_text"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/reply_info"
|
||||||
|
tools:background="@mipmap/ic_launcher"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:id="@+id/reply_cancel"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_marginEnd="12dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/reply_preview_text"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/input_bg"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/reply_preview_image"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/reply_info"
|
||||||
|
app:srcCompat="@drawable/ic_close_24"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<!--<androidx.constraintlayout.widget.Group-->
|
||||||
|
<!-- android:id="@+id/reply_group"-->
|
||||||
|
<!-- android:layout_width="0dp"-->
|
||||||
|
<!-- android:layout_height="0dp"-->
|
||||||
|
<!-- android:visibility="gone"-->
|
||||||
|
<!-- app:constraint_referenced_ids="reply_bg,reply_cancel,reply_info,reply_item_type,reply_preview"-->
|
||||||
|
<!-- tools:visibility="visible" />-->
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:id="@+id/input_bg"
|
android:id="@+id/input_bg"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
@ -65,7 +151,7 @@
|
|||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toStartOf="@id/camera"
|
app:layout_constraintEnd_toStartOf="@id/camera"
|
||||||
app:layout_constraintStart_toEndOf="@id/emoji_toggle"
|
app:layout_constraintStart_toEndOf="@id/emoji_toggle"
|
||||||
app:layout_constraintTop_toBottomOf="@id/chats"
|
app:layout_constraintTop_toBottomOf="@id/reply_preview_text"
|
||||||
app:layout_goneMarginBottom="4dp"
|
app:layout_goneMarginBottom="4dp"
|
||||||
app:layout_goneMarginEnd="24dp" />
|
app:layout_goneMarginEnd="24dp" />
|
||||||
|
|
||||||
|
@ -401,4 +401,10 @@
|
|||||||
<string name="forward">Forward</string>
|
<string name="forward">Forward</string>
|
||||||
<string name="add">Add</string>
|
<string name="add">Add</string>
|
||||||
<string name="send">Send</string>
|
<string name="send">Send</string>
|
||||||
|
<string name="replying_to_yourself">Replying to yourself</string>
|
||||||
|
<string name="replying_to_user">Replying to %s</string>
|
||||||
|
<string name="photo">Photo</string>
|
||||||
|
<string name="video">Video</string>
|
||||||
|
<string name="voice_message">Voice message</string>
|
||||||
|
<string name="post">Post</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
Loading…
Reference in New Issue
Block a user