20 changed files with 613 additions and 50 deletions
-
4app/src/main/java/awais/instagrabber/adapters/DirectItemsAdapter.java
-
2app/src/main/java/awais/instagrabber/adapters/DirectMessageInboxAdapter.java
-
81app/src/main/java/awais/instagrabber/adapters/DirectReactionsAdapter.java
-
2app/src/main/java/awais/instagrabber/adapters/DirectUsersAdapter.java
-
2app/src/main/java/awais/instagrabber/adapters/UserSearchResultsAdapter.java
-
2app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectInboxItemViewHolder.java
-
35app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectItemViewHolder.java
-
71app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectReactionViewHolder.java
-
2app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectUserViewHolder.java
-
82app/src/main/java/awais/instagrabber/customviews/ReactionEmojiTextView.java
-
121app/src/main/java/awais/instagrabber/dialogs/DirectItemReactionDialogFragment.java
-
61app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java
-
3app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItemEmojiReaction.java
-
3app/src/main/java/awais/instagrabber/repositories/responses/directmessages/DirectItemReactions.java
-
7app/src/main/java/awais/instagrabber/utils/emoji/EmojiParser.java
-
134app/src/main/java/awais/instagrabber/viewmodels/DirectThreadViewModel.java
-
30app/src/main/res/layout/layout_dm_base.xml
-
18app/src/main/res/layout/layout_dm_user_item.xml
-
2app/src/main/res/values/dimens.xml
-
1app/src/main/res/values/strings.xml
@ -0,0 +1,81 @@ |
|||||
|
package awais.instagrabber.adapters; |
||||
|
|
||||
|
import android.view.LayoutInflater; |
||||
|
import android.view.ViewGroup; |
||||
|
|
||||
|
import androidx.annotation.NonNull; |
||||
|
import androidx.annotation.Nullable; |
||||
|
import androidx.recyclerview.widget.DiffUtil; |
||||
|
import androidx.recyclerview.widget.ListAdapter; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
import awais.instagrabber.adapters.viewholder.directmessages.DirectReactionViewHolder; |
||||
|
import awais.instagrabber.databinding.LayoutDmUserItemBinding; |
||||
|
import awais.instagrabber.repositories.responses.User; |
||||
|
import awais.instagrabber.repositories.responses.directmessages.DirectItemEmojiReaction; |
||||
|
|
||||
|
public final class DirectReactionsAdapter extends ListAdapter<DirectItemEmojiReaction, DirectReactionViewHolder> { |
||||
|
|
||||
|
private static final DiffUtil.ItemCallback<DirectItemEmojiReaction> DIFF_CALLBACK = new DiffUtil.ItemCallback<DirectItemEmojiReaction>() { |
||||
|
@Override |
||||
|
public boolean areItemsTheSame(@NonNull final DirectItemEmojiReaction oldItem, @NonNull final DirectItemEmojiReaction newItem) { |
||||
|
return oldItem.getSenderId() == newItem.getSenderId(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean areContentsTheSame(@NonNull final DirectItemEmojiReaction oldItem, @NonNull final DirectItemEmojiReaction newItem) { |
||||
|
return oldItem.getEmoji().equals(newItem.getEmoji()); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
private final long viewerId; |
||||
|
private final List<User> users; |
||||
|
private final String itemId; |
||||
|
private final OnReactionClickListener onReactionClickListener; |
||||
|
|
||||
|
public DirectReactionsAdapter(final long viewerId, |
||||
|
final List<User> users, |
||||
|
final String itemId, |
||||
|
final OnReactionClickListener onReactionClickListener) { |
||||
|
super(DIFF_CALLBACK); |
||||
|
this.viewerId = viewerId; |
||||
|
this.users = users; |
||||
|
this.itemId = itemId; |
||||
|
this.onReactionClickListener = onReactionClickListener; |
||||
|
setHasStableIds(true); |
||||
|
} |
||||
|
|
||||
|
@NonNull |
||||
|
@Override |
||||
|
public DirectReactionViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int viewType) { |
||||
|
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext()); |
||||
|
final LayoutDmUserItemBinding binding = LayoutDmUserItemBinding.inflate(layoutInflater, parent, false); |
||||
|
return new DirectReactionViewHolder(binding, viewerId, itemId, onReactionClickListener); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onBindViewHolder(@NonNull final DirectReactionViewHolder holder, final int position) { |
||||
|
final DirectItemEmojiReaction reaction = getItem(position); |
||||
|
if (reaction == null) return; |
||||
|
holder.bind(reaction, getUser(reaction.getSenderId())); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public long getItemId(final int position) { |
||||
|
return getItem(position).getSenderId(); |
||||
|
} |
||||
|
|
||||
|
@Nullable |
||||
|
private User getUser(final long pk) { |
||||
|
return users.stream() |
||||
|
.filter(user -> user.getPk() == pk) |
||||
|
.findFirst() |
||||
|
.orElse(null); |
||||
|
} |
||||
|
|
||||
|
public interface OnReactionClickListener { |
||||
|
void onReactionClick(String itemId, DirectItemEmojiReaction reaction); |
||||
|
} |
||||
|
} |
@ -1,4 +1,4 @@ |
|||||
package awais.instagrabber.adapters.viewholder; |
|
||||
|
package awais.instagrabber.adapters.viewholder.directmessages; |
||||
|
|
||||
import android.graphics.Typeface; |
import android.graphics.Typeface; |
||||
import android.view.View; |
import android.view.View; |
@ -0,0 +1,71 @@ |
|||||
|
package awais.instagrabber.adapters.viewholder.directmessages; |
||||
|
|
||||
|
import android.view.View; |
||||
|
|
||||
|
import androidx.annotation.Nullable; |
||||
|
import androidx.recyclerview.widget.RecyclerView; |
||||
|
|
||||
|
import awais.instagrabber.R; |
||||
|
import awais.instagrabber.adapters.DirectReactionsAdapter.OnReactionClickListener; |
||||
|
import awais.instagrabber.customviews.emoji.Emoji; |
||||
|
import awais.instagrabber.databinding.LayoutDmUserItemBinding; |
||||
|
import awais.instagrabber.repositories.responses.User; |
||||
|
import awais.instagrabber.repositories.responses.directmessages.DirectItemEmojiReaction; |
||||
|
import awais.instagrabber.utils.emoji.EmojiParser; |
||||
|
|
||||
|
public class DirectReactionViewHolder extends RecyclerView.ViewHolder { |
||||
|
private final LayoutDmUserItemBinding binding; |
||||
|
private final long viewerId; |
||||
|
private final String itemId; |
||||
|
private final OnReactionClickListener onReactionClickListener; |
||||
|
private final EmojiParser emojiParser; |
||||
|
|
||||
|
public DirectReactionViewHolder(final LayoutDmUserItemBinding binding, |
||||
|
final long viewerId, |
||||
|
final String itemId, |
||||
|
final OnReactionClickListener onReactionClickListener) { |
||||
|
super(binding.getRoot()); |
||||
|
this.binding = binding; |
||||
|
this.viewerId = viewerId; |
||||
|
this.itemId = itemId; |
||||
|
this.onReactionClickListener = onReactionClickListener; |
||||
|
binding.info.setVisibility(View.GONE); |
||||
|
binding.secondaryImage.setVisibility(View.VISIBLE); |
||||
|
emojiParser = EmojiParser.getInstance(); |
||||
|
} |
||||
|
|
||||
|
public void bind(final DirectItemEmojiReaction reaction, |
||||
|
@Nullable final User user) { |
||||
|
itemView.setOnClickListener(v -> { |
||||
|
if (onReactionClickListener == null) return; |
||||
|
onReactionClickListener.onReactionClick(itemId, reaction); |
||||
|
}); |
||||
|
setUser(user); |
||||
|
setReaction(reaction); |
||||
|
} |
||||
|
|
||||
|
private void setReaction(final DirectItemEmojiReaction reaction) { |
||||
|
final Emoji emoji = emojiParser.getEmoji(reaction.getEmoji()); |
||||
|
if (emoji == null) { |
||||
|
binding.secondaryImage.setImageDrawable(null); |
||||
|
return; |
||||
|
} |
||||
|
binding.secondaryImage.setImageDrawable(emoji.getDrawable()); |
||||
|
} |
||||
|
|
||||
|
private void setUser(final User user) { |
||||
|
if (user == null) { |
||||
|
binding.fullName.setText(""); |
||||
|
binding.username.setText(""); |
||||
|
binding.profilePic.setImageURI((String) null); |
||||
|
return; |
||||
|
} |
||||
|
binding.fullName.setText(user.getFullName()); |
||||
|
if (user.getPk() == viewerId) { |
||||
|
binding.username.setText(R.string.tap_to_remove); |
||||
|
} else { |
||||
|
binding.username.setText(user.getUsername()); |
||||
|
} |
||||
|
binding.profilePic.setImageURI(user.getProfilePicUrl()); |
||||
|
} |
||||
|
} |
@ -1,4 +1,4 @@ |
|||||
package awais.instagrabber.adapters.viewholder; |
|
||||
|
package awais.instagrabber.adapters.viewholder.directmessages; |
||||
|
|
||||
import android.graphics.drawable.Drawable; |
import android.graphics.drawable.Drawable; |
||||
import android.text.SpannableStringBuilder; |
import android.text.SpannableStringBuilder; |
@ -0,0 +1,82 @@ |
|||||
|
package awais.instagrabber.customviews; |
||||
|
|
||||
|
import android.annotation.SuppressLint; |
||||
|
import android.content.Context; |
||||
|
import android.text.SpannableString; |
||||
|
import android.text.SpannableStringBuilder; |
||||
|
import android.text.TextUtils; |
||||
|
import android.util.AttributeSet; |
||||
|
|
||||
|
import androidx.annotation.NonNull; |
||||
|
import androidx.emoji.widget.EmojiAppCompatTextView; |
||||
|
|
||||
|
import java.util.List; |
||||
|
import java.util.stream.Collectors; |
||||
|
|
||||
|
public class ReactionEmojiTextView extends EmojiAppCompatTextView { |
||||
|
private static final String TAG = ReactionEmojiTextView.class.getSimpleName(); |
||||
|
|
||||
|
private final SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(); |
||||
|
|
||||
|
private String count = ""; |
||||
|
private SpannableString ellipsisSpannable; |
||||
|
private String distinctEmojis; |
||||
|
|
||||
|
public ReactionEmojiTextView(final Context context) { |
||||
|
super(context); |
||||
|
init(); |
||||
|
} |
||||
|
|
||||
|
public ReactionEmojiTextView(final Context context, final AttributeSet attrs) { |
||||
|
super(context, attrs); |
||||
|
init(); |
||||
|
} |
||||
|
|
||||
|
public ReactionEmojiTextView(final Context context, final AttributeSet attrs, final int defStyleAttr) { |
||||
|
super(context, attrs, defStyleAttr); |
||||
|
init(); |
||||
|
} |
||||
|
|
||||
|
private void init() { |
||||
|
ellipsisSpannable = new SpannableString(count); |
||||
|
} |
||||
|
|
||||
|
@SuppressLint("SetTextI18n") |
||||
|
public void setEmojis(@NonNull final List<String> emojis) { |
||||
|
count = String.valueOf(emojis.size()); |
||||
|
distinctEmojis = emojis.stream() |
||||
|
.distinct() |
||||
|
.collect(Collectors.joining()); |
||||
|
ellipsisSpannable = new SpannableString(count); |
||||
|
setText(distinctEmojis + (emojis.size() > 1 ? count : "")); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) { |
||||
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec); |
||||
|
final CharSequence text = getText(); |
||||
|
if (text == null) return; |
||||
|
final int measuredWidth = getMeasuredWidth(); |
||||
|
float availableTextWidth = measuredWidth - getCompoundPaddingLeft() - getCompoundPaddingRight(); |
||||
|
CharSequence ellipsizedText = TextUtils.ellipsize(text, getPaint(), availableTextWidth, getEllipsize()); |
||||
|
if (!ellipsizedText.toString().equals(text.toString())) { |
||||
|
// If the ellipsizedText is different than the original text, this means that it didn't fit and got indeed ellipsized. |
||||
|
// Calculate the new availableTextWidth by taking into consideration the size of the custom ellipsis, too. |
||||
|
availableTextWidth = (availableTextWidth - getPaint().measureText(count)); |
||||
|
ellipsizedText = TextUtils.ellipsize(text, getPaint(), availableTextWidth, getEllipsize()); |
||||
|
final int defaultEllipsisStart = ellipsizedText.toString().indexOf(getDefaultEllipsis()); |
||||
|
final int defaultEllipsisEnd = defaultEllipsisStart + 1; |
||||
|
spannableStringBuilder.clear(); |
||||
|
// Update the text with the ellipsized version and replace the default ellipsis with the custom one. |
||||
|
final SpannableStringBuilder replace = spannableStringBuilder.append(ellipsizedText) |
||||
|
.replace(defaultEllipsisStart, defaultEllipsisEnd, ellipsisSpannable); |
||||
|
setText(replace); |
||||
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private char getDefaultEllipsis() { |
||||
|
return '…'; |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,121 @@ |
|||||
|
package awais.instagrabber.dialogs; |
||||
|
|
||||
|
import android.app.Dialog; |
||||
|
import android.content.Context; |
||||
|
import android.os.Bundle; |
||||
|
import android.view.LayoutInflater; |
||||
|
import android.view.View; |
||||
|
import android.view.ViewGroup; |
||||
|
|
||||
|
import androidx.annotation.NonNull; |
||||
|
import androidx.annotation.Nullable; |
||||
|
import androidx.fragment.app.DialogFragment; |
||||
|
import androidx.recyclerview.widget.LinearLayoutManager; |
||||
|
import androidx.recyclerview.widget.RecyclerView; |
||||
|
|
||||
|
import com.google.android.material.bottomsheet.BottomSheetDialog; |
||||
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment; |
||||
|
|
||||
|
import java.io.Serializable; |
||||
|
import java.util.ArrayList; |
||||
|
import java.util.List; |
||||
|
|
||||
|
import awais.instagrabber.R; |
||||
|
import awais.instagrabber.adapters.DirectReactionsAdapter; |
||||
|
import awais.instagrabber.adapters.DirectReactionsAdapter.OnReactionClickListener; |
||||
|
import awais.instagrabber.repositories.responses.User; |
||||
|
import awais.instagrabber.repositories.responses.directmessages.DirectItemReactions; |
||||
|
import awais.instagrabber.utils.TextUtils; |
||||
|
|
||||
|
public class DirectItemReactionDialogFragment extends BottomSheetDialogFragment { |
||||
|
|
||||
|
private static final String ARG_VIEWER_ID = "viewerId"; |
||||
|
private static final String ARG_ITEM_ID = "itemId"; |
||||
|
private static final String ARG_USERS = "users"; |
||||
|
private static final String ARG_REACTIONS = "reactions"; |
||||
|
|
||||
|
private RecyclerView recyclerView; |
||||
|
private OnReactionClickListener onReactionClickListener; |
||||
|
|
||||
|
public static DirectItemReactionDialogFragment newInstance(final long viewerId, |
||||
|
@NonNull final ArrayList<User> users, |
||||
|
@NonNull final String itemId, |
||||
|
@NonNull final DirectItemReactions reactions) { |
||||
|
Bundle args = new Bundle(); |
||||
|
args.putLong(ARG_VIEWER_ID, viewerId); |
||||
|
args.putSerializable(ARG_USERS, users); |
||||
|
args.putString(ARG_ITEM_ID, itemId); |
||||
|
args.putSerializable(ARG_REACTIONS, reactions); |
||||
|
DirectItemReactionDialogFragment fragment = new DirectItemReactionDialogFragment(); |
||||
|
fragment.setArguments(args); |
||||
|
return fragment; |
||||
|
} |
||||
|
|
||||
|
public DirectItemReactionDialogFragment() {} |
||||
|
|
||||
|
@Override |
||||
|
public void onCreate(@Nullable final Bundle savedInstanceState) { |
||||
|
super.onCreate(savedInstanceState); |
||||
|
setStyle(DialogFragment.STYLE_NORMAL, R.style.ThemeOverlay_Rounded_BottomSheetDialog); |
||||
|
} |
||||
|
|
||||
|
@Nullable |
||||
|
@Override |
||||
|
public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) { |
||||
|
final Context context = getContext(); |
||||
|
if (context == null) { |
||||
|
return null; |
||||
|
} |
||||
|
recyclerView = new RecyclerView(context); |
||||
|
return recyclerView; |
||||
|
|
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) { |
||||
|
init(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onAttach(@NonNull final Context context) { |
||||
|
super.onAttach(context); |
||||
|
try { |
||||
|
onReactionClickListener = (OnReactionClickListener) getParentFragment(); |
||||
|
} catch (ClassCastException e) { |
||||
|
throw new ClassCastException("Calling fragment must implement DirectReactionsAdapter.OnReactionClickListener interface"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onStart() { |
||||
|
super.onStart(); |
||||
|
final Dialog dialog = getDialog(); |
||||
|
if (dialog == null) return; |
||||
|
final BottomSheetDialog bottomSheetDialog = (BottomSheetDialog) dialog; |
||||
|
final View bottomSheetInternal = bottomSheetDialog.findViewById(com.google.android.material.R.id.design_bottom_sheet); |
||||
|
if (bottomSheetInternal == null) return; |
||||
|
bottomSheetInternal.getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT; |
||||
|
bottomSheetInternal.requestLayout(); |
||||
|
} |
||||
|
|
||||
|
private void init() { |
||||
|
final Context context = getContext(); |
||||
|
if (context == null) return; |
||||
|
final Bundle arguments = getArguments(); |
||||
|
if (arguments == null) return; |
||||
|
final long viewerId = arguments.getLong(ARG_VIEWER_ID); |
||||
|
final Serializable usersSerializable = arguments.getSerializable(ARG_USERS); |
||||
|
if (!(usersSerializable instanceof ArrayList)) return; |
||||
|
//noinspection unchecked |
||||
|
final List<User> users = (ArrayList<User>) usersSerializable; |
||||
|
final Serializable reactionsSerializable = arguments.getSerializable(ARG_REACTIONS); |
||||
|
if (!(reactionsSerializable instanceof DirectItemReactions)) return; |
||||
|
final DirectItemReactions reactions = (DirectItemReactions) reactionsSerializable; |
||||
|
final String itemId = arguments.getString(ARG_ITEM_ID); |
||||
|
if (TextUtils.isEmpty(itemId)) return; |
||||
|
recyclerView.setLayoutManager(new LinearLayoutManager(context)); |
||||
|
final DirectReactionsAdapter adapter = new DirectReactionsAdapter(viewerId, users, itemId, onReactionClickListener); |
||||
|
recyclerView.setAdapter(adapter); |
||||
|
adapter.submitList(reactions.getEmojis()); |
||||
|
} |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue