Browse Source
update direct messages
update direct messages
This commit sets up the base for future enhancements in direct messages. The changes are: 1. Introducing fragments and navigation using androidx.navigation.fragment.NavHostFragment. 2. Using ListAdapter and LiveData for the RecyclerViews. 3. Fixes a bug where a single direct message was lost when querying older messages.legacy
Ammar Githam
4 years ago
26 changed files with 1158 additions and 949 deletions
-
7app/build.gradle
-
11app/src/main/AndroidManifest.xml
-
73app/src/main/java/awais/instagrabber/activities/DirectMessagesActivity.java
-
32app/src/main/java/awais/instagrabber/activities/Main.java
-
119app/src/main/java/awais/instagrabber/activities/directmessages/DirectMessages.java
-
54app/src/main/java/awais/instagrabber/adapters/DirectMessageInboxAdapter.java
-
131app/src/main/java/awais/instagrabber/adapters/DirectMessagesAdapter.java
-
400app/src/main/java/awais/instagrabber/adapters/MessageItemsAdapter.java
-
107app/src/main/java/awais/instagrabber/adapters/viewholder/DirectMessageInboxItemViewHolder.java
-
43app/src/main/java/awais/instagrabber/adapters/viewholder/DirectMessageViewHolder.java
-
383app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectMessageViewHolder.java
-
91app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/TextMessageViewHolder.java
-
32app/src/main/java/awais/instagrabber/asyncs/direct_messages/DirectMessageInboxThreadFetcher.java
-
149app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageInboxFragment.java
-
286app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java
-
4app/src/main/java/awais/instagrabber/models/ProfileModel.java
-
18app/src/main/java/awais/instagrabber/models/direct_messages/InboxThreadModel.java
-
14app/src/main/java/awais/instagrabber/models/enums/UserInboxDirection.java
-
22app/src/main/java/awais/instagrabber/utils/Utils.java
-
54app/src/main/res/layout/activity_direct_messages.xml
-
10app/src/main/res/layout/fragment_direct_messages_inbox.xml
-
19app/src/main/res/layout/fragment_direct_messages_thread.xml
-
3app/src/main/res/layout/item_message_item.xml
-
26app/src/main/res/navigation/direct_messages_nav_graph.xml
-
2app/src/main/res/values/strings.xml
-
3build.gradle
@ -0,0 +1,73 @@ |
|||
package awais.instagrabber.activities; |
|||
|
|||
import android.os.Bundle; |
|||
import android.widget.TextView; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.annotation.Nullable; |
|||
import androidx.appcompat.widget.Toolbar; |
|||
import androidx.coordinatorlayout.widget.CoordinatorLayout; |
|||
import androidx.navigation.NavController; |
|||
import androidx.navigation.NavDestination; |
|||
import androidx.navigation.Navigation; |
|||
import androidx.navigation.ui.AppBarConfiguration; |
|||
import androidx.navigation.ui.NavigationUI; |
|||
|
|||
import awais.instagrabber.R; |
|||
import awais.instagrabber.databinding.ActivityDirectMessagesBinding; |
|||
import awais.instagrabber.fragments.directmessages.DirectMessagesThreadFragmentArgs; |
|||
|
|||
public class DirectMessagesActivity extends BaseLanguageActivity implements NavController.OnDestinationChangedListener { |
|||
|
|||
private TextView toolbarTitle; |
|||
|
|||
@Override |
|||
protected void onCreate(Bundle savedInstanceState) { |
|||
super.onCreate(savedInstanceState); |
|||
final ActivityDirectMessagesBinding binding = ActivityDirectMessagesBinding.inflate(getLayoutInflater()); |
|||
final CoordinatorLayout root = binding.getRoot(); |
|||
setContentView(root); |
|||
|
|||
toolbarTitle = binding.toolbarTitle; |
|||
|
|||
final Toolbar toolbar = binding.toolbar; |
|||
setSupportActionBar(toolbar); |
|||
|
|||
final NavController navController = Navigation.findNavController(this, R.id.direct_messages_nav_host_fragment); |
|||
navController.addOnDestinationChangedListener(this); |
|||
final AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(navController.getGraph()).build(); |
|||
NavigationUI.setupWithNavController(toolbar, navController, appBarConfiguration); |
|||
} |
|||
|
|||
@Override |
|||
public void onDestinationChanged(@NonNull final NavController controller, |
|||
@NonNull final NavDestination destination, |
|||
@Nullable final Bundle arguments) { |
|||
switch (destination.getId()) { |
|||
case R.id.directMessagesInboxFragment: |
|||
setToolbarTitle(R.string.action_dms); |
|||
return; |
|||
case R.id.directMessagesThreadFragment: |
|||
if (arguments == null) { |
|||
return; |
|||
} |
|||
final String title = DirectMessagesThreadFragmentArgs.fromBundle(arguments).getTitle(); |
|||
setToolbarTitle(title); |
|||
return; |
|||
} |
|||
} |
|||
|
|||
private void setToolbarTitle(final String text) { |
|||
if (toolbarTitle == null) { |
|||
return; |
|||
} |
|||
toolbarTitle.setText(text); |
|||
} |
|||
|
|||
private void setToolbarTitle(final int resourceId) { |
|||
if (toolbarTitle == null) { |
|||
return; |
|||
} |
|||
toolbarTitle.setText(resourceId); |
|||
} |
|||
} |
@ -1,119 +0,0 @@ |
|||
package awais.instagrabber.activities.directmessages; |
|||
|
|||
import android.content.Intent; |
|||
import android.os.AsyncTask; |
|||
import android.os.Bundle; |
|||
import android.util.Log; |
|||
|
|||
import androidx.annotation.Nullable; |
|||
import androidx.recyclerview.widget.DividerItemDecoration; |
|||
import androidx.recyclerview.widget.LinearLayoutManager; |
|||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; |
|||
import android.view.View; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.Arrays; |
|||
|
|||
import awais.instagrabber.R; |
|||
import awais.instagrabber.BuildConfig; |
|||
import awais.instagrabber.activities.BaseLanguageActivity; |
|||
import awais.instagrabber.adapters.DirectMessagesAdapter; |
|||
import awais.instagrabber.asyncs.direct_messages.InboxFetcher; |
|||
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader; |
|||
import awais.instagrabber.databinding.ActivityDmsBinding; |
|||
import awais.instagrabber.interfaces.FetchListener; |
|||
import awais.instagrabber.models.direct_messages.InboxModel; |
|||
import awais.instagrabber.models.direct_messages.InboxThreadModel; |
|||
import awais.instagrabber.utils.Constants; |
|||
import awais.instagrabber.utils.Utils; |
|||
|
|||
public final class DirectMessages extends BaseLanguageActivity implements SwipeRefreshLayout.OnRefreshListener { |
|||
private final ArrayList<InboxThreadModel> inboxThreadModelList = new ArrayList<>(); |
|||
private final DirectMessagesAdapter messagesAdapter = new DirectMessagesAdapter(inboxThreadModelList, v -> { |
|||
final Object tag = v.getTag(); |
|||
if (tag instanceof InboxThreadModel) { |
|||
startActivity(new Intent(this, DirectMessageThread.class) |
|||
.putExtra(Constants.EXTRAS_THREAD_MODEL, (InboxThreadModel) tag) |
|||
); |
|||
} |
|||
}); |
|||
private final FetchListener<InboxModel> fetchListener = new FetchListener<InboxModel>() { |
|||
@Override |
|||
public void doBefore() { |
|||
dmsBinding.swipeRefreshLayout.setRefreshing(true); |
|||
} |
|||
|
|||
@Override |
|||
public void onResult(final InboxModel inboxModel) { |
|||
if (inboxModel != null) { |
|||
endCursor = inboxModel.getOldestCursor(); |
|||
if ("MINCURSOR".equals(endCursor) || "MAXCURSOR".equals(endCursor)) endCursor = null; |
|||
// todo get request / unseen count from inboxModel |
|||
|
|||
final InboxThreadModel[] threads = inboxModel.getThreads(); |
|||
if (threads != null && threads.length > 0) { |
|||
final int oldSize = inboxThreadModelList.size(); |
|||
inboxThreadModelList.addAll(Arrays.asList(threads)); |
|||
|
|||
messagesAdapter.notifyItemRangeInserted(oldSize, threads.length); |
|||
} |
|||
} |
|||
|
|||
dmsBinding.swipeRefreshLayout.setRefreshing(false); |
|||
stopCurrentExecutor(); |
|||
} |
|||
}; |
|||
private String endCursor; |
|||
private RecyclerLazyLoader lazyLoader; |
|||
private AsyncTask<Void, Void, InboxModel> currentlyRunning; |
|||
private ActivityDmsBinding dmsBinding; |
|||
|
|||
@Override |
|||
protected void onCreate(@Nullable final Bundle savedInstanceState) { |
|||
super.onCreate(savedInstanceState); |
|||
dmsBinding = ActivityDmsBinding.inflate(getLayoutInflater()); |
|||
setContentView(dmsBinding.getRoot()); |
|||
|
|||
dmsBinding.swipeRefreshLayout.setOnRefreshListener(this); |
|||
dmsBinding.toolbar.toolbar.setTitle(R.string.action_dms); |
|||
dmsBinding.commentText.setVisibility(View.GONE); |
|||
dmsBinding.commentSend.setVisibility(View.GONE); |
|||
|
|||
final LinearLayoutManager layoutManager = new LinearLayoutManager(this); |
|||
dmsBinding.rvDirectMessages.setLayoutManager(layoutManager); |
|||
dmsBinding.rvDirectMessages.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL)); |
|||
dmsBinding.rvDirectMessages.setAdapter(messagesAdapter); |
|||
|
|||
lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> { |
|||
if (!Utils.isEmpty(endCursor)) |
|||
currentlyRunning = new InboxFetcher(endCursor, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
|||
endCursor = null; |
|||
}); |
|||
|
|||
dmsBinding.rvDirectMessages.addOnScrollListener(lazyLoader); |
|||
|
|||
stopCurrentExecutor(); |
|||
currentlyRunning = new InboxFetcher(null, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
|||
} |
|||
|
|||
@Override |
|||
public void onRefresh() { |
|||
endCursor = null; |
|||
lazyLoader.resetState(); |
|||
inboxThreadModelList.clear(); |
|||
messagesAdapter.notifyDataSetChanged(); |
|||
|
|||
stopCurrentExecutor(); |
|||
currentlyRunning = new InboxFetcher(null, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
|||
} |
|||
|
|||
private void stopCurrentExecutor() { |
|||
if (currentlyRunning != null) { |
|||
try { |
|||
currentlyRunning.cancel(true); |
|||
} catch (final Exception e) { |
|||
if (BuildConfig.DEBUG) Log.e("AWAISKING_APP", "", e); |
|||
} |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,54 @@ |
|||
package awais.instagrabber.adapters; |
|||
|
|||
import android.view.LayoutInflater; |
|||
import android.view.ViewGroup; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.recyclerview.widget.DiffUtil; |
|||
import androidx.recyclerview.widget.ListAdapter; |
|||
|
|||
import awais.instagrabber.adapters.viewholder.DirectMessageInboxItemViewHolder; |
|||
import awais.instagrabber.databinding.LayoutIncludeSimpleItemBinding; |
|||
import awais.instagrabber.models.direct_messages.InboxThreadModel; |
|||
|
|||
public final class DirectMessageInboxAdapter extends ListAdapter<InboxThreadModel, DirectMessageInboxItemViewHolder> { |
|||
private final OnItemClickListener onClickListener; |
|||
|
|||
private static final DiffUtil.ItemCallback<InboxThreadModel> diffCallback = new DiffUtil.ItemCallback<InboxThreadModel>() { |
|||
@Override |
|||
public boolean areItemsTheSame(@NonNull final InboxThreadModel oldItem, @NonNull final InboxThreadModel newItem) { |
|||
return oldItem.getThreadId().equals(newItem.getThreadId()); |
|||
} |
|||
|
|||
@Override |
|||
public boolean areContentsTheSame(@NonNull final InboxThreadModel oldItem, @NonNull final InboxThreadModel newItem) { |
|||
return oldItem.equals(newItem); |
|||
} |
|||
}; |
|||
|
|||
public DirectMessageInboxAdapter(final OnItemClickListener onClickListener) { |
|||
super(diffCallback); |
|||
this.onClickListener = onClickListener; |
|||
} |
|||
|
|||
@NonNull |
|||
@Override |
|||
public DirectMessageInboxItemViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int type) { |
|||
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext()); |
|||
final LayoutIncludeSimpleItemBinding binding = LayoutIncludeSimpleItemBinding.inflate(layoutInflater, parent, false); |
|||
return new DirectMessageInboxItemViewHolder(binding); |
|||
} |
|||
|
|||
@Override |
|||
public void onBindViewHolder(@NonNull final DirectMessageInboxItemViewHolder holder, final int position) { |
|||
final InboxThreadModel threadModel = getItem(position); |
|||
if (onClickListener != null) { |
|||
holder.itemView.setOnClickListener((v) -> onClickListener.onItemClick(threadModel)); |
|||
} |
|||
holder.bind(threadModel); |
|||
} |
|||
|
|||
public interface OnItemClickListener { |
|||
void onItemClick(final InboxThreadModel inboxThreadModel); |
|||
} |
|||
} |
@ -1,131 +0,0 @@ |
|||
package awais.instagrabber.adapters; |
|||
|
|||
import android.content.Context; |
|||
import android.view.LayoutInflater; |
|||
import android.view.View; |
|||
import android.view.ViewGroup; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.core.text.HtmlCompat; |
|||
import androidx.recyclerview.widget.RecyclerView; |
|||
|
|||
import com.bumptech.glide.Glide; |
|||
import com.bumptech.glide.RequestManager; |
|||
|
|||
import java.util.ArrayList; |
|||
|
|||
import awais.instagrabber.R; |
|||
import awais.instagrabber.adapters.viewholder.DirectMessageViewHolder; |
|||
import awais.instagrabber.models.ProfileModel; |
|||
import awais.instagrabber.models.direct_messages.DirectItemModel; |
|||
import awais.instagrabber.models.direct_messages.DirectItemModel.DirectItemActionLogModel; |
|||
import awais.instagrabber.models.direct_messages.DirectItemModel.DirectItemReelShareModel; |
|||
import awais.instagrabber.models.direct_messages.InboxThreadModel; |
|||
import awais.instagrabber.models.enums.DirectItemType; |
|||
|
|||
public final class DirectMessagesAdapter extends RecyclerView.Adapter<DirectMessageViewHolder> { |
|||
private final ArrayList<InboxThreadModel> inboxThreadModels; |
|||
private final View.OnClickListener onClickListener; |
|||
private LayoutInflater layoutInflater; |
|||
|
|||
public DirectMessagesAdapter(final ArrayList<InboxThreadModel> inboxThreadModels, final View.OnClickListener onClickListener) { |
|||
this.inboxThreadModels = inboxThreadModels; |
|||
this.onClickListener = onClickListener; |
|||
} |
|||
|
|||
@NonNull |
|||
@Override |
|||
public DirectMessageViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int type) { |
|||
if (layoutInflater == null) layoutInflater = LayoutInflater.from(parent.getContext()); |
|||
return new DirectMessageViewHolder(layoutInflater.inflate(R.layout.layout_include_simple_item, parent, false), |
|||
onClickListener); |
|||
} |
|||
|
|||
@Override |
|||
public void onBindViewHolder(@NonNull final DirectMessageViewHolder holder, final int position) { |
|||
final InboxThreadModel threadModel = inboxThreadModels.get(position); |
|||
final DirectItemModel[] itemModels; |
|||
|
|||
holder.itemView.setTag(threadModel); |
|||
|
|||
final RequestManager glideRequestManager = Glide.with(holder.itemView); |
|||
|
|||
if (threadModel != null && (itemModels = threadModel.getItems()) != null) { |
|||
final ProfileModel[] users = threadModel.getUsers(); |
|||
|
|||
if (users.length > 1) { |
|||
holder.ivProfilePic.setVisibility(View.GONE); |
|||
holder.multipleProfilePicsContainer.setVisibility(View.VISIBLE); |
|||
|
|||
for (int i = 0; i < Math.min(3, users.length); ++i) |
|||
glideRequestManager.load(users[i].getSdProfilePic()).into(holder.multipleProfilePics[i]); |
|||
|
|||
} else { |
|||
holder.ivProfilePic.setVisibility(View.VISIBLE); |
|||
holder.multipleProfilePicsContainer.setVisibility(View.GONE); |
|||
|
|||
glideRequestManager.load(users.length == 1 ? users[0].getSdProfilePic() : null).into(holder.ivProfilePic); |
|||
} |
|||
|
|||
holder.tvUsername.setText(threadModel.getThreadTitle()); |
|||
|
|||
final DirectItemModel lastItemModel = itemModels[itemModels.length - 1]; |
|||
final DirectItemType itemType = lastItemModel.getItemType(); |
|||
|
|||
holder.notTextType.setVisibility(itemType != DirectItemType.TEXT ? View.VISIBLE : View.GONE); |
|||
|
|||
final Context context = layoutInflater.getContext(); |
|||
|
|||
final CharSequence messageText; |
|||
switch (itemType) { |
|||
case TEXT: |
|||
case LIKE: |
|||
messageText = lastItemModel.getText(); |
|||
break; |
|||
case LINK: |
|||
messageText = context.getString(R.string.direct_messages_sent_link); |
|||
break; |
|||
case MEDIA: |
|||
case MEDIA_SHARE: |
|||
messageText = context.getString(R.string.direct_messages_sent_media); |
|||
break; |
|||
case ACTION_LOG: |
|||
final DirectItemActionLogModel logModel = lastItemModel.getActionLogModel(); |
|||
messageText = logModel != null ? logModel.getDescription() : "..."; |
|||
break; |
|||
case REEL_SHARE: |
|||
final DirectItemReelShareModel reelShare = lastItemModel.getReelShare(); |
|||
if (reelShare == null) |
|||
messageText = context.getString(R.string.direct_messages_sent_media); |
|||
else { |
|||
final String reelType = reelShare.getType(); |
|||
final int textRes; |
|||
if ("reply".equals(reelType)) |
|||
textRes = R.string.direct_messages_replied_story; |
|||
else if ("mention".equals(reelType)) |
|||
textRes = R.string.direct_messages_mention_story; |
|||
else if ("reaction".equals(reelType)) |
|||
textRes = R.string.direct_messages_reacted_story; |
|||
else textRes = R.string.direct_messages_sent_media; |
|||
|
|||
messageText = context.getString(textRes) + " : " + reelShare.getText(); |
|||
} |
|||
break; |
|||
case RAVEN_MEDIA: |
|||
messageText = context.getString(R.string.direct_messages_sent_media); |
|||
break; |
|||
default: |
|||
messageText = "<i>Unsupported message</i>"; |
|||
} |
|||
|
|||
holder.tvMessage.setText(HtmlCompat.fromHtml(messageText.toString(), 63)); |
|||
|
|||
holder.tvDate.setText(lastItemModel.getDateTime()); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public int getItemCount() { |
|||
return inboxThreadModels == null ? 0 : inboxThreadModels.size(); |
|||
} |
|||
} |
@ -1,410 +1,66 @@ |
|||
package awais.instagrabber.adapters; |
|||
|
|||
import android.content.Context; |
|||
import android.content.res.Resources; |
|||
import android.text.Spanned; |
|||
import android.util.Log; |
|||
import android.view.LayoutInflater; |
|||
import android.view.View; |
|||
import android.view.ViewGroup; |
|||
import android.widget.ArrayAdapter; |
|||
import android.widget.ImageView; |
|||
import android.widget.TextView; |
|||
import android.widget.Toast; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.annotation.Nullable; |
|||
import androidx.core.text.HtmlCompat; |
|||
import androidx.recyclerview.widget.RecyclerView; |
|||
import androidx.recyclerview.widget.DiffUtil; |
|||
import androidx.recyclerview.widget.ListAdapter; |
|||
|
|||
import com.bumptech.glide.Glide; |
|||
import com.bumptech.glide.RequestManager; |
|||
import java.util.List; |
|||
|
|||
import java.util.ArrayList; |
|||
|
|||
import awais.instagrabber.R; |
|||
import awais.instagrabber.activities.Main; |
|||
import awais.instagrabber.adapters.viewholder.directmessages.TextMessageViewHolder; |
|||
import awais.instagrabber.adapters.viewholder.directmessages.DirectMessageViewHolder; |
|||
import awais.instagrabber.databinding.ItemMessageItemBinding; |
|||
import awais.instagrabber.interfaces.MentionClickListener; |
|||
import awais.instagrabber.models.ProfileModel; |
|||
import awais.instagrabber.models.direct_messages.DirectItemModel; |
|||
import awais.instagrabber.models.direct_messages.DirectItemModel.DirectItemMediaModel; |
|||
import awais.instagrabber.models.direct_messages.DirectItemModel.DirectItemRavenMediaModel; |
|||
import awais.instagrabber.models.enums.DirectItemType; |
|||
import awais.instagrabber.models.enums.MediaItemType; |
|||
import awais.instagrabber.models.enums.RavenExpiringMediaType; |
|||
import awais.instagrabber.models.enums.RavenMediaViewType; |
|||
import awais.instagrabber.utils.Constants; |
|||
import awais.instagrabber.utils.Utils; |
|||
|
|||
import static awais.instagrabber.models.direct_messages.DirectItemModel.DirectItemLinkContext; |
|||
import static awais.instagrabber.models.direct_messages.DirectItemModel.DirectItemLinkModel; |
|||
import static awais.instagrabber.models.direct_messages.DirectItemModel.DirectItemReelShareModel; |
|||
import static awais.instagrabber.models.direct_messages.DirectItemModel.DirectItemVoiceMediaModel; |
|||
import static awais.instagrabber.models.direct_messages.DirectItemModel.RavenExpiringMediaActionSummaryModel; |
|||
|
|||
public final class MessageItemsAdapter extends RecyclerView.Adapter<TextMessageViewHolder> { |
|||
private static final int MESSAGE_INCOMING = 69, MESSAGE_OUTGOING = 420; |
|||
private final ProfileModel myProfileHolder = |
|||
new ProfileModel(false, false, false, |
|||
Utils.getUserIdFromCookie(Utils.settingsHelper.getString(Constants.COOKIE)), |
|||
null, null, null, null, null, null, 0, 0, 0, false, false, false, false); |
|||
private final ArrayList<DirectItemModel> directItemModels; |
|||
private final ArrayList<ProfileModel> users, leftusers; |
|||
public final class MessageItemsAdapter extends ListAdapter<DirectItemModel, DirectMessageViewHolder> { |
|||
private final List<ProfileModel> users; |
|||
private final List<ProfileModel> leftUsers; |
|||
private final View.OnClickListener onClickListener; |
|||
private final MentionClickListener mentionClickListener; |
|||
private final View.OnClickListener openProfileClickListener = v -> { |
|||
final Object tag = v.getTag(); |
|||
if (tag instanceof ProfileModel) { |
|||
// todo do profile stuff |
|||
final ProfileModel profileModel = (ProfileModel) tag; |
|||
Log.d("AWAISKING_APP", "--> " + profileModel); |
|||
} |
|||
}; |
|||
private final int itemMargin; |
|||
private DirectItemVoiceMediaModel prevVoiceModel; |
|||
private ImageView prevPlayIcon; |
|||
private final View.OnClickListener voicePlayClickListener = v -> { |
|||
final Object tag = v.getTag(); |
|||
if (v instanceof ViewGroup && tag instanceof DirectItemVoiceMediaModel) { |
|||
final ImageView playIcon = (ImageView) ((ViewGroup) v).getChildAt(0); |
|||
final DirectItemVoiceMediaModel voiceMediaModel = (DirectItemVoiceMediaModel) tag; |
|||
final boolean voicePlaying = voiceMediaModel.isPlaying(); |
|||
voiceMediaModel.setPlaying(!voicePlaying); |
|||
|
|||
if (voiceMediaModel == prevVoiceModel) { |
|||
// todo pause / resume |
|||
} else { |
|||
// todo release prev audio, start new voice |
|||
if (prevVoiceModel != null) prevVoiceModel.setPlaying(false); |
|||
if (prevPlayIcon != null) prevPlayIcon.setImageResource(android.R.drawable.ic_media_play); |
|||
} |
|||
|
|||
if (voicePlaying) { |
|||
playIcon.setImageResource(android.R.drawable.ic_media_play); |
|||
} else { |
|||
playIcon.setImageResource(android.R.drawable.ic_media_pause); |
|||
private static final DiffUtil.ItemCallback<DirectItemModel> diffCallback = new DiffUtil.ItemCallback<DirectItemModel>() { |
|||
@Override |
|||
public boolean areItemsTheSame(@NonNull final DirectItemModel oldItem, @NonNull final DirectItemModel newItem) { |
|||
return oldItem.getItemId().equals(newItem.getItemId()); |
|||
} |
|||
|
|||
prevVoiceModel = voiceMediaModel; |
|||
prevPlayIcon = playIcon; |
|||
@Override |
|||
public boolean areContentsTheSame(@NonNull final DirectItemModel oldItem, @NonNull final DirectItemModel newItem) { |
|||
return oldItem.getItemId().equals(newItem.getItemId()); |
|||
} |
|||
}; |
|||
private Context context; |
|||
private LayoutInflater layoutInflater; |
|||
private String strDmYou; |
|||
|
|||
public MessageItemsAdapter(final ArrayList<DirectItemModel> directItemModels, final ArrayList<ProfileModel> users, |
|||
final ArrayList<ProfileModel> leftusers, final View.OnClickListener onClickListener, |
|||
public MessageItemsAdapter(final List<ProfileModel> users, |
|||
final List<ProfileModel> leftUsers, |
|||
final View.OnClickListener onClickListener, |
|||
final MentionClickListener mentionClickListener) { |
|||
super(diffCallback); |
|||
this.users = users; |
|||
this.leftusers = leftusers; |
|||
this.directItemModels = directItemModels; |
|||
this.leftUsers = leftUsers; |
|||
this.onClickListener = onClickListener; |
|||
this.mentionClickListener = mentionClickListener; |
|||
this.itemMargin = Utils.displayMetrics.widthPixels / 5; |
|||
} |
|||
|
|||
@NonNull |
|||
@Override |
|||
public TextMessageViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int type) { |
|||
if (context == null) context = parent.getContext(); |
|||
if (strDmYou == null) strDmYou = context.getString(R.string.direct_messages_you); |
|||
if (layoutInflater == null) layoutInflater = LayoutInflater.from(context); |
|||
return new TextMessageViewHolder(layoutInflater.inflate(R.layout.item_message_item, parent, false), |
|||
onClickListener, mentionClickListener); |
|||
public DirectMessageViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int type) { |
|||
final LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext()); |
|||
final ItemMessageItemBinding binding = ItemMessageItemBinding.inflate(layoutInflater, parent, false); |
|||
return new DirectMessageViewHolder(binding, users, leftUsers); |
|||
} |
|||
|
|||
@Override |
|||
public void onBindViewHolder(@NonNull final TextMessageViewHolder holder, final int position) { |
|||
final DirectItemModel directItemModel = directItemModels.get(position); |
|||
holder.itemView.setTag(directItemModel); |
|||
|
|||
if (directItemModel != null) { |
|||
final DirectItemType itemType = directItemModel.getItemType(); |
|||
|
|||
final ProfileModel user = getUser(directItemModel.getUserId()); |
|||
final int type = user == myProfileHolder ? MESSAGE_OUTGOING : MESSAGE_INCOMING; |
|||
|
|||
final RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) holder.itemView.getLayoutParams(); |
|||
layoutParams.setMargins(type == MESSAGE_OUTGOING ? itemMargin : 0, 0, |
|||
type == MESSAGE_INCOMING ? itemMargin : 0, 0); |
|||
|
|||
holder.tvMessage.setVisibility(View.GONE); |
|||
holder.voiceMessageContainer.setVisibility(View.GONE); |
|||
holder.ivAnimatedMessage.setVisibility(View.GONE); |
|||
holder.linkMessageContainer.setVisibility(View.GONE); |
|||
|
|||
holder.mediaMessageContainer.setVisibility(View.GONE); |
|||
holder.mediaTypeIcon.setVisibility(View.GONE); |
|||
holder.mediaExpiredIcon.setVisibility(View.GONE); |
|||
|
|||
holder.profileMessageContainer.setVisibility(View.GONE); |
|||
holder.isVerified.setVisibility(View.GONE); |
|||
|
|||
holder.btnOpenProfile.setVisibility(View.GONE); |
|||
holder.btnOpenProfile.setOnClickListener(null); |
|||
holder.btnOpenProfile.setTag(null); |
|||
|
|||
CharSequence text = "?"; |
|||
if (user != null && user != myProfileHolder) text = user.getUsername(); |
|||
else if (user == myProfileHolder) text = strDmYou; |
|||
text = text + " - " + directItemModel.getDateTime(); |
|||
|
|||
holder.tvUsername.setText(text); |
|||
|
|||
holder.ivProfilePic.setVisibility(type == MESSAGE_INCOMING ? View.VISIBLE : View.GONE); |
|||
|
|||
final RequestManager glideRequestManager = Glide.with(holder.itemView); |
|||
|
|||
if (type == MESSAGE_INCOMING && user != null) |
|||
glideRequestManager.load(user.getSdProfilePic()).into(holder.ivProfilePic); |
|||
|
|||
DirectItemMediaModel mediaModel = directItemModel.getMediaModel(); |
|||
switch (itemType) { |
|||
case PLACEHOLDER: |
|||
holder.tvMessage.setText(HtmlCompat.fromHtml(directItemModel.getText().toString(), 63)); |
|||
holder.tvMessage.setVisibility(View.VISIBLE); |
|||
break; |
|||
case TEXT: |
|||
case LIKE: |
|||
text = directItemModel.getText(); |
|||
text = Utils.getSpannableUrl(text.toString()); // for urls |
|||
if (Utils.hasMentions(text)) text = Utils.getMentionText(text); // for mentions |
|||
|
|||
if (text instanceof Spanned) holder.tvMessage.setText(text, TextView.BufferType.SPANNABLE); |
|||
else if (text == "") holder.tvMessage.setText(context.getText(R.string.dms_inbox_raven_message_unknown)); |
|||
else holder.tvMessage.setText(text); |
|||
|
|||
holder.tvMessage.setVisibility(View.VISIBLE); |
|||
break; |
|||
|
|||
case LINK: { |
|||
final DirectItemLinkModel link = directItemModel.getLinkModel(); |
|||
final DirectItemLinkContext linkContext = link.getLinkContext(); |
|||
|
|||
final String linkImageUrl = linkContext.getLinkImageUrl(); |
|||
if (!Utils.isEmpty(linkImageUrl)) { |
|||
glideRequestManager.load(linkImageUrl).into(holder.ivLinkPreview); |
|||
holder.tvLinkTitle.setText(linkContext.getLinkTitle()); |
|||
holder.tvLinkSummary.setText(linkContext.getLinkSummary()); |
|||
holder.ivLinkPreview.setVisibility(View.VISIBLE); |
|||
holder.linkMessageContainer.setVisibility(View.VISIBLE); |
|||
} |
|||
|
|||
holder.tvMessage.setText(Utils.getSpannableUrl(link.getText())); |
|||
holder.tvMessage.setVisibility(View.VISIBLE); |
|||
} |
|||
break; |
|||
|
|||
case MEDIA_SHARE: |
|||
{ |
|||
final ProfileModel modelUser = mediaModel.getUser(); |
|||
if (modelUser != null) { |
|||
holder.tvMessage.setText(HtmlCompat.fromHtml("<small>"+context.getString(R.string.dms_inbox_media_shared_from, modelUser.getUsername())+"</small>", 63)); |
|||
holder.tvMessage.setVisibility(View.VISIBLE); |
|||
} |
|||
} |
|||
case MEDIA: { |
|||
glideRequestManager.load(mediaModel.getThumbUrl()).into(holder.ivMediaPreview); |
|||
|
|||
final MediaItemType modelMediaType = mediaModel.getMediaType(); |
|||
holder.mediaTypeIcon.setVisibility(modelMediaType == MediaItemType.MEDIA_TYPE_VIDEO || |
|||
modelMediaType == MediaItemType.MEDIA_TYPE_SLIDER ? View.VISIBLE : View.GONE); |
|||
|
|||
holder.mediaMessageContainer.setVisibility(View.VISIBLE); |
|||
} |
|||
break; |
|||
|
|||
case RAVEN_MEDIA: { |
|||
final DirectItemRavenMediaModel ravenMediaModel = directItemModel.getRavenMediaModel(); |
|||
|
|||
final boolean isExpired = ravenMediaModel == null || (mediaModel = ravenMediaModel.getMedia()) == null || |
|||
Utils.isEmpty(mediaModel.getThumbUrl()) && mediaModel.getPk() < 1; |
|||
|
|||
final RavenExpiringMediaActionSummaryModel mediaActionSummary = ravenMediaModel.getExpiringMediaActionSummary(); |
|||
holder.mediaExpiredIcon.setVisibility(isExpired ? View.VISIBLE : View.GONE); |
|||
|
|||
int textRes = R.string.dms_inbox_raven_media_unknown; |
|||
if (isExpired) textRes = R.string.dms_inbox_raven_media_expired; |
|||
|
|||
if (!isExpired) { |
|||
if (mediaActionSummary != null) { |
|||
final RavenExpiringMediaType expiringMediaType = mediaActionSummary.getType(); |
|||
|
|||
if (expiringMediaType == RavenExpiringMediaType.RAVEN_DELIVERED) |
|||
textRes = R.string.dms_inbox_raven_media_delivered; |
|||
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_SENT) |
|||
textRes = R.string.dms_inbox_raven_media_sent; |
|||
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_OPENED) |
|||
textRes = R.string.dms_inbox_raven_media_opened; |
|||
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_REPLAYED) |
|||
textRes = R.string.dms_inbox_raven_media_replayed; |
|||
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_SENDING) |
|||
textRes = R.string.dms_inbox_raven_media_sending; |
|||
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_BLOCKED) |
|||
textRes = R.string.dms_inbox_raven_media_blocked; |
|||
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_SUGGESTED) |
|||
textRes = R.string.dms_inbox_raven_media_suggested; |
|||
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_SCREENSHOT) |
|||
textRes = R.string.dms_inbox_raven_media_screenshot; |
|||
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_CANNOT_DELIVER) |
|||
textRes = R.string.dms_inbox_raven_media_cant_deliver; |
|||
} |
|||
|
|||
final RavenMediaViewType ravenMediaViewType = ravenMediaModel.getViewType(); |
|||
if (ravenMediaViewType == RavenMediaViewType.PERMANENT || ravenMediaViewType == RavenMediaViewType.REPLAYABLE) { |
|||
final MediaItemType mediaType = mediaModel.getMediaType(); |
|||
textRes = -1; |
|||
holder.mediaTypeIcon.setVisibility(mediaType == MediaItemType.MEDIA_TYPE_VIDEO || |
|||
mediaType == MediaItemType.MEDIA_TYPE_SLIDER ? View.VISIBLE : View.GONE); |
|||
|
|||
glideRequestManager.load(mediaModel.getThumbUrl()).into(holder.ivMediaPreview); |
|||
holder.mediaMessageContainer.setVisibility(View.VISIBLE); |
|||
} |
|||
} |
|||
if (textRes != -1) { |
|||
holder.tvMessage.setText(context.getText(textRes)); |
|||
holder.tvMessage.setVisibility(View.VISIBLE); |
|||
} |
|||
} |
|||
break; |
|||
|
|||
case REEL_SHARE: { |
|||
final DirectItemReelShareModel reelShare = directItemModel.getReelShare(); |
|||
if (!Utils.isEmpty(text = reelShare.getText())) { |
|||
holder.tvMessage.setText(text); |
|||
holder.tvMessage.setVisibility(View.VISIBLE); |
|||
} |
|||
|
|||
final DirectItemMediaModel reelShareMedia = reelShare.getMedia(); |
|||
final MediaItemType mediaType = reelShareMedia.getMediaType(); |
|||
|
|||
if (mediaType == null) |
|||
holder.mediaExpiredIcon.setVisibility(View.VISIBLE); |
|||
else { |
|||
holder.mediaTypeIcon.setVisibility(mediaType == MediaItemType.MEDIA_TYPE_VIDEO || |
|||
mediaType == MediaItemType.MEDIA_TYPE_SLIDER ? View.VISIBLE : View.GONE); |
|||
|
|||
glideRequestManager.load(reelShareMedia.getThumbUrl()).into(holder.ivMediaPreview); |
|||
holder.mediaMessageContainer.setVisibility(View.VISIBLE); |
|||
} |
|||
} |
|||
break; |
|||
|
|||
case STORY_SHARE: { |
|||
final DirectItemReelShareModel reelShare = directItemModel.getReelShare(); |
|||
if (reelShare == null) { |
|||
holder.tvMessage.setText(HtmlCompat.fromHtml(directItemModel.getText().toString(), 63)); |
|||
holder.tvMessage.setVisibility(View.VISIBLE); |
|||
} |
|||
else { |
|||
if (!Utils.isEmpty(text = reelShare.getText())) { |
|||
holder.tvMessage.setText(text); |
|||
holder.tvMessage.setVisibility(View.VISIBLE); |
|||
} |
|||
|
|||
final DirectItemMediaModel reelShareMedia = reelShare.getMedia(); |
|||
final MediaItemType mediaType = reelShareMedia.getMediaType(); |
|||
|
|||
holder.mediaTypeIcon.setVisibility(mediaType == MediaItemType.MEDIA_TYPE_VIDEO || |
|||
mediaType == MediaItemType.MEDIA_TYPE_SLIDER ? View.VISIBLE : View.GONE); |
|||
|
|||
glideRequestManager.load(reelShareMedia.getThumbUrl()).into(holder.ivMediaPreview); |
|||
holder.mediaMessageContainer.setVisibility(View.VISIBLE); |
|||
} |
|||
} |
|||
break; |
|||
|
|||
case VOICE_MEDIA: { |
|||
final DirectItemVoiceMediaModel voiceMediaModel = directItemModel.getVoiceMediaModel(); |
|||
|
|||
if (voiceMediaModel != null) { |
|||
final int[] waveformData = voiceMediaModel.getWaveformData(); |
|||
if (waveformData != null) holder.waveformSeekBar.setSample(waveformData); |
|||
|
|||
final long durationMs = voiceMediaModel.getDurationMs(); |
|||
holder.tvVoiceDuration.setText(Utils.millisToString(durationMs)); |
|||
holder.waveformSeekBar.setProgress(voiceMediaModel.getProgress()); |
|||
holder.waveformSeekBar.setProgressChangeListener((waveformSeekBar, progress, fromUser) -> { |
|||
// todo progress audio player |
|||
voiceMediaModel.setProgress(progress); |
|||
if (fromUser) |
|||
holder.tvVoiceDuration.setText(Utils.millisToString(durationMs * progress / 100)); |
|||
}); |
|||
holder.btnPlayVoice.setTag(voiceMediaModel); |
|||
holder.btnPlayVoice.setOnClickListener(voicePlayClickListener); |
|||
} else { |
|||
holder.waveformSeekBar.setProgress(0); |
|||
} |
|||
|
|||
holder.voiceMessageContainer.setVisibility(View.VISIBLE); |
|||
} |
|||
break; |
|||
|
|||
case ANIMATED_MEDIA: { |
|||
glideRequestManager.asGif().load(directItemModel.getAnimatedMediaModel().getGifUrl()) |
|||
.into(holder.ivAnimatedMessage); |
|||
holder.ivAnimatedMessage.setVisibility(View.VISIBLE); |
|||
} |
|||
break; |
|||
|
|||
case PROFILE: { |
|||
final ProfileModel profileModel = directItemModel.getProfileModel(); |
|||
Glide.with(holder.ivMessageProfilePic).load(profileModel.getSdProfilePic()) |
|||
.into(holder.ivMessageProfilePic); |
|||
holder.btnOpenProfile.setTag(profileModel); |
|||
holder.btnOpenProfile.setOnClickListener(openProfileClickListener); |
|||
|
|||
holder.tvProfileName.setText(profileModel.getName()); |
|||
holder.tvProfileUsername.setText(profileModel.getUsername()); |
|||
holder.isVerified.setVisibility(profileModel.isVerified() ? View.VISIBLE : View.GONE); |
|||
|
|||
holder.btnOpenProfile.setVisibility(View.VISIBLE); |
|||
holder.profileMessageContainer.setVisibility(View.VISIBLE); |
|||
} |
|||
break; |
|||
|
|||
case VIDEO_CALL_EVENT: { |
|||
// todo add call event info |
|||
holder.tvMessage.setVisibility(View.VISIBLE); |
|||
holder.itemView.setBackgroundColor(0xFF_1F90E6); |
|||
} |
|||
break; |
|||
|
|||
case ACTION_LOG: { |
|||
text = directItemModel.getActionLogModel().getDescription(); |
|||
holder.tvMessage.setText(HtmlCompat.fromHtml("<small>"+text+"</small>", 63)); |
|||
holder.tvMessage.setVisibility(View.VISIBLE); |
|||
} |
|||
break; |
|||
} |
|||
} |
|||
public void onBindViewHolder(@NonNull final DirectMessageViewHolder holder, final int position) { |
|||
final DirectItemModel directItemModel = getItem(position); |
|||
holder.bind(directItemModel); |
|||
} |
|||
|
|||
@Override |
|||
public int getItemViewType(final int position) { |
|||
return directItemModels.get(position).getItemType().ordinal(); |
|||
} |
|||
|
|||
@Override |
|||
public int getItemCount() { |
|||
return directItemModels == null ? 0 : directItemModels.size(); |
|||
} |
|||
|
|||
@Nullable |
|||
private ProfileModel getUser(final long userId) { |
|||
if (users != null) { |
|||
ProfileModel result = myProfileHolder; |
|||
for (final ProfileModel user : users) { |
|||
if (Long.toString(userId).equals(user.getId())) result = user; |
|||
} |
|||
if (leftusers != null) |
|||
for (final ProfileModel leftuser : leftusers) { |
|||
if (Long.toString(userId).equals(leftuser.getId())) result = leftuser; |
|||
} |
|||
return result; |
|||
} |
|||
return null; |
|||
return getItem(position).getItemType().ordinal(); |
|||
} |
|||
} |
@ -0,0 +1,107 @@ |
|||
package awais.instagrabber.adapters.viewholder; |
|||
|
|||
import android.content.Context; |
|||
import android.view.View; |
|||
import android.widget.ImageView; |
|||
import android.widget.LinearLayout; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.core.text.HtmlCompat; |
|||
import androidx.recyclerview.widget.RecyclerView; |
|||
|
|||
import com.bumptech.glide.Glide; |
|||
import com.bumptech.glide.RequestManager; |
|||
|
|||
import awais.instagrabber.R; |
|||
import awais.instagrabber.databinding.LayoutIncludeSimpleItemBinding; |
|||
import awais.instagrabber.models.ProfileModel; |
|||
import awais.instagrabber.models.direct_messages.DirectItemModel; |
|||
import awais.instagrabber.models.direct_messages.InboxThreadModel; |
|||
import awais.instagrabber.models.enums.DirectItemType; |
|||
|
|||
public final class DirectMessageInboxItemViewHolder extends RecyclerView.ViewHolder { |
|||
private final LinearLayout multipleProfilePicsContainer; |
|||
private final ImageView[] multipleProfilePics; |
|||
private final LayoutIncludeSimpleItemBinding binding; |
|||
|
|||
public DirectMessageInboxItemViewHolder(@NonNull final LayoutIncludeSimpleItemBinding binding) { |
|||
super(binding.getRoot()); |
|||
this.binding = binding; |
|||
binding.tvLikes.setVisibility(View.GONE); |
|||
multipleProfilePicsContainer = binding.container; |
|||
final LinearLayout containerChild = (LinearLayout) multipleProfilePicsContainer.getChildAt(1); |
|||
multipleProfilePics = new ImageView[]{ |
|||
(ImageView) multipleProfilePicsContainer.getChildAt(0), |
|||
(ImageView) containerChild.getChildAt(0), |
|||
(ImageView) containerChild.getChildAt(1) |
|||
}; |
|||
binding.tvDate.setSelected(true); |
|||
binding.tvUsername.setSelected(true); |
|||
} |
|||
|
|||
public void bind(final InboxThreadModel model) { |
|||
final DirectItemModel[] itemModels; |
|||
if (model == null || (itemModels = model.getItems()) == null) { |
|||
return; |
|||
} |
|||
itemView.setTag(model); |
|||
final RequestManager glideRequestManager = Glide.with(itemView); |
|||
final ProfileModel[] users = model.getUsers(); |
|||
if (users.length > 1) { |
|||
binding.ivProfilePic.setVisibility(View.GONE); |
|||
multipleProfilePicsContainer.setVisibility(View.VISIBLE); |
|||
for (int i = 0; i < Math.min(3, users.length); ++i) |
|||
glideRequestManager.load(users[i].getSdProfilePic()).into(multipleProfilePics[i]); |
|||
} else { |
|||
binding.ivProfilePic.setVisibility(View.VISIBLE); |
|||
multipleProfilePicsContainer.setVisibility(View.GONE); |
|||
glideRequestManager.load(users.length == 1 ? users[0].getSdProfilePic() : null).into(binding.ivProfilePic); |
|||
} |
|||
binding.tvUsername.setText(model.getThreadTitle()); |
|||
final DirectItemModel lastItemModel = itemModels[itemModels.length - 1]; |
|||
final DirectItemType itemType = lastItemModel.getItemType(); |
|||
binding.notTextType.setVisibility(itemType != DirectItemType.TEXT ? View.VISIBLE : View.GONE); |
|||
final Context context = itemView.getContext(); |
|||
final CharSequence messageText; |
|||
switch (itemType) { |
|||
case TEXT: |
|||
case LIKE: |
|||
messageText = lastItemModel.getText(); |
|||
break; |
|||
case LINK: |
|||
messageText = context.getString(R.string.direct_messages_sent_link); |
|||
break; |
|||
case MEDIA: |
|||
case MEDIA_SHARE: |
|||
case RAVEN_MEDIA: |
|||
messageText = context.getString(R.string.direct_messages_sent_media); |
|||
break; |
|||
case ACTION_LOG: |
|||
final DirectItemModel.DirectItemActionLogModel logModel = lastItemModel.getActionLogModel(); |
|||
messageText = logModel != null ? logModel.getDescription() : "..."; |
|||
break; |
|||
case REEL_SHARE: |
|||
final DirectItemModel.DirectItemReelShareModel reelShare = lastItemModel.getReelShare(); |
|||
if (reelShare == null) |
|||
messageText = context.getString(R.string.direct_messages_sent_media); |
|||
else { |
|||
final String reelType = reelShare.getType(); |
|||
final int textRes; |
|||
if ("reply".equals(reelType)) |
|||
textRes = R.string.direct_messages_replied_story; |
|||
else if ("mention".equals(reelType)) |
|||
textRes = R.string.direct_messages_mention_story; |
|||
else if ("reaction".equals(reelType)) |
|||
textRes = R.string.direct_messages_reacted_story; |
|||
else textRes = R.string.direct_messages_sent_media; |
|||
|
|||
messageText = context.getString(textRes) + " : " + reelShare.getText(); |
|||
} |
|||
break; |
|||
default: |
|||
messageText = "<i>Unsupported message</i>"; |
|||
} |
|||
binding.tvComment.setText(HtmlCompat.fromHtml(messageText.toString(), HtmlCompat.FROM_HTML_MODE_COMPACT)); |
|||
binding.tvDate.setText(lastItemModel.getDateTime()); |
|||
} |
|||
} |
@ -1,43 +0,0 @@ |
|||
package awais.instagrabber.adapters.viewholder; |
|||
|
|||
import android.view.View; |
|||
import android.widget.ImageView; |
|||
import android.widget.LinearLayout; |
|||
import android.widget.TextView; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.recyclerview.widget.RecyclerView; |
|||
|
|||
import awais.instagrabber.R; |
|||
|
|||
public final class DirectMessageViewHolder extends RecyclerView.ViewHolder { |
|||
public final LinearLayout multipleProfilePicsContainer; |
|||
public final ImageView[] multipleProfilePics; |
|||
public final ImageView ivProfilePic, notTextType; |
|||
public final TextView tvUsername, tvDate, tvMessage; |
|||
|
|||
public DirectMessageViewHolder(@NonNull final View itemView, final View.OnClickListener clickListener) { |
|||
super(itemView); |
|||
|
|||
if (clickListener != null) itemView.setOnClickListener(clickListener); |
|||
|
|||
itemView.findViewById(R.id.tvLikes).setVisibility(View.GONE); |
|||
|
|||
tvDate = itemView.findViewById(R.id.tvDate); |
|||
tvMessage = itemView.findViewById(R.id.tvComment); |
|||
tvUsername = itemView.findViewById(R.id.tvUsername); |
|||
notTextType = itemView.findViewById(R.id.notTextType); |
|||
ivProfilePic = itemView.findViewById(R.id.ivProfilePic); |
|||
|
|||
multipleProfilePicsContainer = itemView.findViewById(R.id.container); |
|||
final LinearLayout containerChild = (LinearLayout) multipleProfilePicsContainer.getChildAt(1); |
|||
multipleProfilePics = new ImageView[]{ |
|||
(ImageView) multipleProfilePicsContainer.getChildAt(0), |
|||
(ImageView) containerChild.getChildAt(0), |
|||
(ImageView) containerChild.getChildAt(1) |
|||
}; |
|||
|
|||
tvDate.setSelected(true); |
|||
tvUsername.setSelected(true); |
|||
} |
|||
} |
@ -0,0 +1,383 @@ |
|||
package awais.instagrabber.adapters.viewholder.directmessages; |
|||
|
|||
import android.content.Context; |
|||
import android.text.Spanned; |
|||
import android.util.Log; |
|||
import android.view.View; |
|||
import android.view.ViewGroup; |
|||
import android.widget.FrameLayout; |
|||
import android.widget.ImageView; |
|||
import android.widget.TextView; |
|||
|
|||
import androidx.annotation.Nullable; |
|||
import androidx.core.text.HtmlCompat; |
|||
import androidx.recyclerview.widget.RecyclerView; |
|||
|
|||
import com.bumptech.glide.Glide; |
|||
import com.bumptech.glide.RequestManager; |
|||
|
|||
import java.util.List; |
|||
|
|||
import awais.instagrabber.R; |
|||
import awais.instagrabber.databinding.ItemMessageItemBinding; |
|||
import awais.instagrabber.models.ProfileModel; |
|||
import awais.instagrabber.models.direct_messages.DirectItemModel; |
|||
import awais.instagrabber.models.enums.DirectItemType; |
|||
import awais.instagrabber.models.enums.MediaItemType; |
|||
import awais.instagrabber.models.enums.RavenExpiringMediaType; |
|||
import awais.instagrabber.models.enums.RavenMediaViewType; |
|||
import awais.instagrabber.utils.Constants; |
|||
import awais.instagrabber.utils.Utils; |
|||
|
|||
import static androidx.core.text.HtmlCompat.FROM_HTML_MODE_COMPACT; |
|||
|
|||
public final class DirectMessageViewHolder extends RecyclerView.ViewHolder { |
|||
private static final String TAG = "DirectMessageViewHolder"; |
|||
private static final int MESSAGE_INCOMING = 69; |
|||
private static final int MESSAGE_OUTGOING = 420; |
|||
|
|||
private final ProfileModel myProfileHolder = ProfileModel.getDefaultProfileModel(Utils.getUserIdFromCookie(Utils.settingsHelper.getString(Constants.COOKIE))); |
|||
private final ItemMessageItemBinding binding; |
|||
private final List<ProfileModel> users; |
|||
private final List<ProfileModel> leftUsers; |
|||
private final int itemMargin; |
|||
private final String strDmYou; |
|||
private DirectItemModel.DirectItemVoiceMediaModel prevVoiceModel; |
|||
private ImageView prevPlayIcon; |
|||
|
|||
private final View.OnClickListener voicePlayClickListener = v -> { |
|||
final Object tag = v.getTag(); |
|||
if (v instanceof ViewGroup && tag instanceof DirectItemModel.DirectItemVoiceMediaModel) { |
|||
final ImageView playIcon = (ImageView) ((ViewGroup) v).getChildAt(0); |
|||
final DirectItemModel.DirectItemVoiceMediaModel voiceMediaModel = (DirectItemModel.DirectItemVoiceMediaModel) tag; |
|||
final boolean voicePlaying = voiceMediaModel.isPlaying(); |
|||
voiceMediaModel.setPlaying(!voicePlaying); |
|||
|
|||
if (voiceMediaModel == prevVoiceModel) { |
|||
// todo pause / resume |
|||
} else { |
|||
// todo release prev audio, start new voice |
|||
if (prevVoiceModel != null) prevVoiceModel.setPlaying(false); |
|||
if (prevPlayIcon != null) |
|||
prevPlayIcon.setImageResource(android.R.drawable.ic_media_play); |
|||
} |
|||
|
|||
if (voicePlaying) { |
|||
playIcon.setImageResource(android.R.drawable.ic_media_play); |
|||
} else { |
|||
playIcon.setImageResource(android.R.drawable.ic_media_pause); |
|||
} |
|||
|
|||
prevVoiceModel = voiceMediaModel; |
|||
prevPlayIcon = playIcon; |
|||
} |
|||
}; |
|||
|
|||
private final View.OnClickListener openProfileClickListener = v -> { |
|||
final Object tag = v.getTag(); |
|||
if (tag instanceof ProfileModel) { |
|||
// todo do profile stuff |
|||
final ProfileModel profileModel = (ProfileModel) tag; |
|||
Log.d(TAG, "--> " + profileModel); |
|||
} |
|||
}; |
|||
|
|||
|
|||
public DirectMessageViewHolder(final ItemMessageItemBinding binding, |
|||
final List<ProfileModel> users, |
|||
final List<ProfileModel> leftUsers) { |
|||
super(binding.getRoot()); |
|||
this.binding = binding; |
|||
this.users = users; |
|||
this.leftUsers = leftUsers; |
|||
this.itemMargin = Utils.displayMetrics.widthPixels / 5; |
|||
strDmYou = binding.getRoot().getContext().getString(R.string.direct_messages_you); |
|||
} |
|||
|
|||
public void bind(final DirectItemModel directItemModel) { |
|||
if (directItemModel == null) { |
|||
return; |
|||
} |
|||
final Context context = itemView.getContext(); |
|||
itemView.setTag(directItemModel); |
|||
final DirectItemType itemType = directItemModel.getItemType(); |
|||
final ProfileModel user = getUser(directItemModel.getUserId()); |
|||
final int type = user == myProfileHolder ? MESSAGE_OUTGOING : MESSAGE_INCOMING; |
|||
|
|||
final RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) itemView.getLayoutParams(); |
|||
layoutParams.setMargins(type == MESSAGE_OUTGOING ? itemMargin : 0, 0, |
|||
type == MESSAGE_INCOMING ? itemMargin : 0, 0); |
|||
|
|||
binding.tvMessage.setVisibility(View.GONE); |
|||
final View voiceMessageContainer = (View) binding.waveformSeekBar.getParent(); |
|||
final View linkMessageContainer = (View) binding.ivLinkPreview.getParent(); |
|||
final View mediaMessageContainer = (View) binding.ivMediaPreview.getParent(); |
|||
final View mediaTypeIcon = binding.typeIcon; |
|||
final View profileMessageContainer = (View) binding.profileInfo.getParent(); |
|||
|
|||
voiceMessageContainer.setVisibility(View.GONE); |
|||
binding.ivAnimatedMessage.setVisibility(View.GONE); |
|||
linkMessageContainer.setVisibility(View.GONE); |
|||
mediaMessageContainer.setVisibility(View.GONE); |
|||
mediaTypeIcon.setVisibility(View.GONE); |
|||
binding.mediaExpiredIcon.setVisibility(View.GONE); |
|||
profileMessageContainer.setVisibility(View.GONE); |
|||
binding.isVerified.setVisibility(View.GONE); |
|||
|
|||
final FrameLayout btnOpenProfile = binding.btnInfo; |
|||
btnOpenProfile.setVisibility(View.GONE); |
|||
btnOpenProfile.setOnClickListener(null); |
|||
btnOpenProfile.setTag(null); |
|||
|
|||
CharSequence text = "?"; |
|||
if (user != null && user != myProfileHolder) text = user.getUsername(); |
|||
else if (user == myProfileHolder) text = strDmYou; |
|||
text = text + " - " + directItemModel.getDateTime(); |
|||
|
|||
binding.tvUsername.setText(text); |
|||
|
|||
binding.ivProfilePic.setVisibility(type == MESSAGE_INCOMING ? View.VISIBLE : View.GONE); |
|||
|
|||
final RequestManager glideRequestManager = Glide.with(itemView); |
|||
|
|||
if (type == MESSAGE_INCOMING && user != null) |
|||
glideRequestManager.load(user.getSdProfilePic()).into(binding.ivProfilePic); |
|||
|
|||
DirectItemModel.DirectItemMediaModel mediaModel = directItemModel.getMediaModel(); |
|||
switch (itemType) { |
|||
case PLACEHOLDER: |
|||
binding.tvMessage.setText(HtmlCompat.fromHtml(directItemModel.getText().toString(), FROM_HTML_MODE_COMPACT)); |
|||
binding.tvMessage.setVisibility(View.VISIBLE); |
|||
break; |
|||
case TEXT: |
|||
case LIKE: |
|||
text = directItemModel.getText(); |
|||
text = Utils.getSpannableUrl(text.toString()); // for urls |
|||
if (Utils.hasMentions(text)) text = Utils.getMentionText(text); // for mentions |
|||
|
|||
if (text instanceof Spanned) |
|||
binding.tvMessage.setText(text, TextView.BufferType.SPANNABLE); |
|||
else if (text == "") { |
|||
binding.tvMessage.setText(context.getText(R.string.dms_inbox_raven_message_unknown)); |
|||
} else binding.tvMessage.setText(text); |
|||
|
|||
binding.tvMessage.setVisibility(View.VISIBLE); |
|||
break; |
|||
|
|||
case LINK: { |
|||
final DirectItemModel.DirectItemLinkModel link = directItemModel.getLinkModel(); |
|||
final DirectItemModel.DirectItemLinkContext linkContext = link.getLinkContext(); |
|||
|
|||
final String linkImageUrl = linkContext.getLinkImageUrl(); |
|||
if (!Utils.isEmpty(linkImageUrl)) { |
|||
glideRequestManager.load(linkImageUrl).into(binding.ivLinkPreview); |
|||
binding.tvLinkTitle.setText(linkContext.getLinkTitle()); |
|||
binding.tvLinkSummary.setText(linkContext.getLinkSummary()); |
|||
binding.ivLinkPreview.setVisibility(View.VISIBLE); |
|||
linkMessageContainer.setVisibility(View.VISIBLE); |
|||
} |
|||
|
|||
binding.tvMessage.setText(Utils.getSpannableUrl(link.getText())); |
|||
binding.tvMessage.setVisibility(View.VISIBLE); |
|||
} |
|||
break; |
|||
|
|||
case MEDIA_SHARE: { |
|||
final ProfileModel modelUser = mediaModel.getUser(); |
|||
if (modelUser != null) { |
|||
binding.tvMessage.setText(HtmlCompat.fromHtml("<small>" + context.getString(R.string.dms_inbox_media_shared_from, modelUser.getUsername()) + "</small>", FROM_HTML_MODE_COMPACT)); |
|||
binding.tvMessage.setVisibility(View.VISIBLE); |
|||
} |
|||
} |
|||
case MEDIA: { |
|||
glideRequestManager.load(mediaModel.getThumbUrl()).into(binding.ivMediaPreview); |
|||
|
|||
final MediaItemType modelMediaType = mediaModel.getMediaType(); |
|||
mediaTypeIcon.setVisibility(modelMediaType == MediaItemType.MEDIA_TYPE_VIDEO || |
|||
modelMediaType == MediaItemType.MEDIA_TYPE_SLIDER ? View.VISIBLE : View.GONE); |
|||
mediaMessageContainer.setVisibility(View.VISIBLE); |
|||
} |
|||
break; |
|||
|
|||
case RAVEN_MEDIA: { |
|||
final DirectItemModel.DirectItemRavenMediaModel ravenMediaModel = directItemModel.getRavenMediaModel(); |
|||
|
|||
final boolean isExpired = ravenMediaModel == null || (mediaModel = ravenMediaModel.getMedia()) == null || |
|||
Utils.isEmpty(mediaModel.getThumbUrl()) && mediaModel.getPk() < 1; |
|||
|
|||
DirectItemModel.RavenExpiringMediaActionSummaryModel mediaActionSummary = null; |
|||
if (ravenMediaModel != null) { |
|||
mediaActionSummary = ravenMediaModel.getExpiringMediaActionSummary(); |
|||
} |
|||
binding.mediaExpiredIcon.setVisibility(isExpired ? View.VISIBLE : View.GONE); |
|||
|
|||
int textRes = R.string.dms_inbox_raven_media_unknown; |
|||
if (isExpired) textRes = R.string.dms_inbox_raven_media_expired; |
|||
|
|||
if (!isExpired) { |
|||
if (mediaActionSummary != null) { |
|||
final RavenExpiringMediaType expiringMediaType = mediaActionSummary.getType(); |
|||
|
|||
if (expiringMediaType == RavenExpiringMediaType.RAVEN_DELIVERED) |
|||
textRes = R.string.dms_inbox_raven_media_delivered; |
|||
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_SENT) |
|||
textRes = R.string.dms_inbox_raven_media_sent; |
|||
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_OPENED) |
|||
textRes = R.string.dms_inbox_raven_media_opened; |
|||
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_REPLAYED) |
|||
textRes = R.string.dms_inbox_raven_media_replayed; |
|||
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_SENDING) |
|||
textRes = R.string.dms_inbox_raven_media_sending; |
|||
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_BLOCKED) |
|||
textRes = R.string.dms_inbox_raven_media_blocked; |
|||
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_SUGGESTED) |
|||
textRes = R.string.dms_inbox_raven_media_suggested; |
|||
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_SCREENSHOT) |
|||
textRes = R.string.dms_inbox_raven_media_screenshot; |
|||
else if (expiringMediaType == RavenExpiringMediaType.RAVEN_CANNOT_DELIVER) |
|||
textRes = R.string.dms_inbox_raven_media_cant_deliver; |
|||
} |
|||
|
|||
final RavenMediaViewType ravenMediaViewType = ravenMediaModel.getViewType(); |
|||
if (ravenMediaViewType == RavenMediaViewType.PERMANENT || ravenMediaViewType == RavenMediaViewType.REPLAYABLE) { |
|||
final MediaItemType mediaType = mediaModel.getMediaType(); |
|||
textRes = -1; |
|||
mediaTypeIcon.setVisibility(mediaType == MediaItemType.MEDIA_TYPE_VIDEO || |
|||
mediaType == MediaItemType.MEDIA_TYPE_SLIDER ? View.VISIBLE : View.GONE); |
|||
|
|||
glideRequestManager.load(mediaModel.getThumbUrl()).into(binding.ivMediaPreview); |
|||
mediaMessageContainer.setVisibility(View.VISIBLE); |
|||
} |
|||
} |
|||
if (textRes != -1) { |
|||
binding.tvMessage.setText(context.getText(textRes)); |
|||
binding.tvMessage.setVisibility(View.VISIBLE); |
|||
} |
|||
} |
|||
break; |
|||
|
|||
case REEL_SHARE: { |
|||
final DirectItemModel.DirectItemReelShareModel reelShare = directItemModel.getReelShare(); |
|||
if (!Utils.isEmpty(text = reelShare.getText())) { |
|||
binding.tvMessage.setText(text); |
|||
binding.tvMessage.setVisibility(View.VISIBLE); |
|||
} |
|||
|
|||
final DirectItemModel.DirectItemMediaModel reelShareMedia = reelShare.getMedia(); |
|||
final MediaItemType mediaType = reelShareMedia.getMediaType(); |
|||
|
|||
if (mediaType == null) |
|||
binding.mediaExpiredIcon.setVisibility(View.VISIBLE); |
|||
else { |
|||
mediaTypeIcon.setVisibility(mediaType == MediaItemType.MEDIA_TYPE_VIDEO || |
|||
mediaType == MediaItemType.MEDIA_TYPE_SLIDER ? View.VISIBLE : View.GONE); |
|||
|
|||
glideRequestManager.load(reelShareMedia.getThumbUrl()).into(binding.ivMediaPreview); |
|||
mediaMessageContainer.setVisibility(View.VISIBLE); |
|||
} |
|||
} |
|||
break; |
|||
|
|||
case STORY_SHARE: { |
|||
final DirectItemModel.DirectItemReelShareModel reelShare = directItemModel.getReelShare(); |
|||
if (reelShare == null) { |
|||
binding.tvMessage.setText(HtmlCompat.fromHtml(directItemModel.getText().toString(), FROM_HTML_MODE_COMPACT)); |
|||
binding.tvMessage.setVisibility(View.VISIBLE); |
|||
} else { |
|||
if (!Utils.isEmpty(text = reelShare.getText())) { |
|||
binding.tvMessage.setText(text); |
|||
binding.tvMessage.setVisibility(View.VISIBLE); |
|||
} |
|||
|
|||
final DirectItemModel.DirectItemMediaModel reelShareMedia = reelShare.getMedia(); |
|||
final MediaItemType mediaType = reelShareMedia.getMediaType(); |
|||
|
|||
mediaTypeIcon.setVisibility(mediaType == MediaItemType.MEDIA_TYPE_VIDEO || |
|||
mediaType == MediaItemType.MEDIA_TYPE_SLIDER ? View.VISIBLE : View.GONE); |
|||
|
|||
glideRequestManager.load(reelShareMedia.getThumbUrl()).into(binding.ivMediaPreview); |
|||
mediaMessageContainer.setVisibility(View.VISIBLE); |
|||
} |
|||
} |
|||
break; |
|||
|
|||
case VOICE_MEDIA: { |
|||
final DirectItemModel.DirectItemVoiceMediaModel voiceMediaModel = directItemModel.getVoiceMediaModel(); |
|||
|
|||
if (voiceMediaModel != null) { |
|||
final int[] waveformData = voiceMediaModel.getWaveformData(); |
|||
if (waveformData != null) binding.waveformSeekBar.setSample(waveformData); |
|||
|
|||
final long durationMs = voiceMediaModel.getDurationMs(); |
|||
binding.tvVoiceDuration.setText(Utils.millisToString(durationMs)); |
|||
binding.waveformSeekBar.setProgress(voiceMediaModel.getProgress()); |
|||
binding.waveformSeekBar.setProgressChangeListener((waveformSeekBar, progress, fromUser) -> { |
|||
// todo progress audio player |
|||
voiceMediaModel.setProgress(progress); |
|||
if (fromUser) |
|||
binding.tvVoiceDuration.setText(Utils.millisToString(durationMs * progress / 100)); |
|||
}); |
|||
binding.btnPlayVoice.setTag(voiceMediaModel); |
|||
binding.btnPlayVoice.setOnClickListener(voicePlayClickListener); |
|||
} else { |
|||
binding.waveformSeekBar.setProgress(0); |
|||
} |
|||
voiceMessageContainer.setVisibility(View.VISIBLE); |
|||
} |
|||
break; |
|||
|
|||
case ANIMATED_MEDIA: { |
|||
glideRequestManager.asGif().load(directItemModel.getAnimatedMediaModel().getGifUrl()) |
|||
.into(binding.ivAnimatedMessage); |
|||
binding.ivAnimatedMessage.setVisibility(View.VISIBLE); |
|||
} |
|||
break; |
|||
|
|||
case PROFILE: { |
|||
final ProfileModel profileModel = directItemModel.getProfileModel(); |
|||
Glide.with(binding.profileInfo).load(profileModel.getSdProfilePic()) |
|||
.into(binding.profileInfo); |
|||
btnOpenProfile.setTag(profileModel); |
|||
btnOpenProfile.setOnClickListener(openProfileClickListener); |
|||
|
|||
binding.tvFullName.setText(profileModel.getName()); |
|||
binding.profileInfoText.setText(profileModel.getUsername()); |
|||
binding.isVerified.setVisibility(profileModel.isVerified() ? View.VISIBLE : View.GONE); |
|||
|
|||
btnOpenProfile.setVisibility(View.VISIBLE); |
|||
profileMessageContainer.setVisibility(View.VISIBLE); |
|||
} |
|||
break; |
|||
|
|||
case VIDEO_CALL_EVENT: { |
|||
// todo add call event info |
|||
binding.tvMessage.setVisibility(View.VISIBLE); |
|||
binding.profileInfoText.setBackgroundColor(0xFF_1F90E6); |
|||
} |
|||
break; |
|||
|
|||
case ACTION_LOG: { |
|||
text = directItemModel.getActionLogModel().getDescription(); |
|||
binding.tvMessage.setText(HtmlCompat.fromHtml("<small>" + text + "</small>", FROM_HTML_MODE_COMPACT)); |
|||
binding.tvMessage.setVisibility(View.VISIBLE); |
|||
} |
|||
break; |
|||
} |
|||
} |
|||
|
|||
@Nullable |
|||
private ProfileModel getUser(final long userId) { |
|||
if (users != null) { |
|||
ProfileModel result = myProfileHolder; |
|||
for (final ProfileModel user : users) { |
|||
if (Long.toString(userId).equals(user.getId())) result = user; |
|||
} |
|||
if (leftUsers != null) |
|||
for (final ProfileModel leftUser : leftUsers) { |
|||
if (Long.toString(userId).equals(leftUser.getId())) result = leftUser; |
|||
} |
|||
return result; |
|||
} |
|||
return null; |
|||
} |
|||
} |
@ -1,91 +0,0 @@ |
|||
package awais.instagrabber.adapters.viewholder.directmessages; |
|||
|
|||
import android.view.View; |
|||
import android.widget.ImageView; |
|||
import android.widget.TextView; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.cardview.widget.CardView; |
|||
import androidx.recyclerview.widget.RecyclerView; |
|||
|
|||
import awais.instagrabber.R; |
|||
import awais.instagrabber.customviews.RamboTextView; |
|||
import awais.instagrabber.customviews.masoudss_waveform.WaveformSeekBar; |
|||
import awais.instagrabber.interfaces.MentionClickListener; |
|||
|
|||
public final class TextMessageViewHolder extends RecyclerView.ViewHolder { |
|||
public final CardView rootCardView; |
|||
public final TextView tvUsername; |
|||
public final ImageView ivProfilePic; |
|||
// text message |
|||
public final RamboTextView tvMessage; |
|||
// expired message icon |
|||
public final View mediaExpiredIcon; |
|||
// media message |
|||
public final View mediaMessageContainer; |
|||
public final ImageView ivMediaPreview, mediaTypeIcon; |
|||
// profile messag |
|||
public final View profileMessageContainer, isVerified, btnOpenProfile; |
|||
public final TextView tvProfileUsername, tvProfileName; |
|||
public final ImageView ivMessageProfilePic; |
|||
// animated message |
|||
public final ImageView ivAnimatedMessage; |
|||
// link message |
|||
public final View linkMessageContainer; |
|||
public final ImageView ivLinkPreview; |
|||
public final TextView tvLinkTitle, tvLinkSummary; |
|||
// voice message |
|||
public final View voiceMessageContainer, btnPlayVoice; |
|||
public final WaveformSeekBar waveformSeekBar; |
|||
public final TextView tvVoiceDuration; |
|||
|
|||
public TextMessageViewHolder(@NonNull final View itemView, final View.OnClickListener clickListener, |
|||
final MentionClickListener mentionClickListener) { |
|||
super(itemView); |
|||
|
|||
if (clickListener != null) itemView.setOnClickListener(clickListener); |
|||
|
|||
tvUsername = itemView.findViewById(R.id.tvUsername); |
|||
ivProfilePic = itemView.findViewById(R.id.ivProfilePic); |
|||
|
|||
// text message |
|||
tvMessage = itemView.findViewById(R.id.tvMessage); |
|||
tvMessage.setCaptionIsExpandable(true); |
|||
tvMessage.setCaptionIsExpanded(true); |
|||
if (mentionClickListener != null) tvMessage.setMentionClickListener(mentionClickListener); |
|||
|
|||
// root view |
|||
rootCardView = (CardView) tvMessage.getParent().getParent(); |
|||
|
|||
// expired message icon |
|||
mediaExpiredIcon = itemView.findViewById(R.id.mediaExpiredIcon); |
|||
|
|||
// media message |
|||
ivMediaPreview = itemView.findViewById(R.id.ivMediaPreview); |
|||
mediaMessageContainer = (View) ivMediaPreview.getParent(); |
|||
mediaTypeIcon = mediaMessageContainer.findViewById(R.id.typeIcon); |
|||
|
|||
// profile message |
|||
btnOpenProfile = itemView.findViewById(R.id.btnInfo); |
|||
ivMessageProfilePic = itemView.findViewById(R.id.profileInfo); |
|||
profileMessageContainer = (View) ivMessageProfilePic.getParent(); |
|||
isVerified = profileMessageContainer.findViewById(R.id.isVerified); |
|||
tvProfileName = profileMessageContainer.findViewById(R.id.tvFullName); |
|||
tvProfileUsername = profileMessageContainer.findViewById(R.id.profileInfoText); |
|||
|
|||
// animated message |
|||
ivAnimatedMessage = itemView.findViewById(R.id.ivAnimatedMessage); |
|||
|
|||
// link message |
|||
ivLinkPreview = itemView.findViewById(R.id.ivLinkPreview); |
|||
linkMessageContainer = (View) ivLinkPreview.getParent(); |
|||
tvLinkTitle = linkMessageContainer.findViewById(R.id.tvLinkTitle); |
|||
tvLinkSummary = linkMessageContainer.findViewById(R.id.tvLinkSummary); |
|||
|
|||
// voice message |
|||
waveformSeekBar = itemView.findViewById(R.id.waveformSeekBar); |
|||
voiceMessageContainer = (View) waveformSeekBar.getParent(); |
|||
btnPlayVoice = voiceMessageContainer.findViewById(R.id.btnPlayVoice); |
|||
tvVoiceDuration = voiceMessageContainer.findViewById(R.id.tvVoiceDuration); |
|||
} |
|||
} |
@ -0,0 +1,149 @@ |
|||
package awais.instagrabber.fragments.directmessages; |
|||
|
|||
import android.os.AsyncTask; |
|||
import android.os.Bundle; |
|||
import android.util.Log; |
|||
import android.view.LayoutInflater; |
|||
import android.view.View; |
|||
import android.view.ViewGroup; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.annotation.Nullable; |
|||
import androidx.fragment.app.Fragment; |
|||
import androidx.fragment.app.FragmentActivity; |
|||
import androidx.lifecycle.MutableLiveData; |
|||
import androidx.lifecycle.ViewModel; |
|||
import androidx.lifecycle.ViewModelProvider; |
|||
import androidx.navigation.NavDirections; |
|||
import androidx.navigation.fragment.NavHostFragment; |
|||
import androidx.recyclerview.widget.LinearLayoutManager; |
|||
import androidx.recyclerview.widget.RecyclerView; |
|||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; |
|||
|
|||
import java.util.Arrays; |
|||
import java.util.Collections; |
|||
import java.util.LinkedList; |
|||
import java.util.List; |
|||
|
|||
import awais.instagrabber.BuildConfig; |
|||
import awais.instagrabber.adapters.DirectMessageInboxAdapter; |
|||
import awais.instagrabber.asyncs.direct_messages.InboxFetcher; |
|||
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader; |
|||
import awais.instagrabber.databinding.FragmentDirectMessagesInboxBinding; |
|||
import awais.instagrabber.interfaces.FetchListener; |
|||
import awais.instagrabber.models.direct_messages.InboxModel; |
|||
import awais.instagrabber.models.direct_messages.InboxThreadModel; |
|||
import awais.instagrabber.utils.Utils; |
|||
|
|||
public class DirectMessageInboxFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener { |
|||
private static final String TAG = "DirectMessagesInboxFrag"; |
|||
|
|||
private FragmentActivity fragmentActivity; |
|||
private SwipeRefreshLayout root; |
|||
private RecyclerView inboxList; |
|||
private RecyclerLazyLoader lazyLoader; |
|||
private LinearLayoutManager layoutManager; |
|||
private String endCursor; |
|||
private AsyncTask<Void, Void, InboxModel> currentlyRunning; |
|||
private InboxThreadModelListViewModel listViewModel; |
|||
|
|||
private final FetchListener<InboxModel> fetchListener = new FetchListener<InboxModel>() { |
|||
@Override |
|||
public void doBefore() { |
|||
root.setRefreshing(true); |
|||
} |
|||
|
|||
@Override |
|||
public void onResult(final InboxModel inboxModel) { |
|||
if (inboxModel != null) { |
|||
endCursor = inboxModel.getOldestCursor(); |
|||
if ("MINCURSOR".equals(endCursor) || "MAXCURSOR".equals(endCursor)) |
|||
endCursor = null; |
|||
// todo get request / unseen count from inboxModel |
|||
final InboxThreadModel[] threads = inboxModel.getThreads(); |
|||
if (threads != null && threads.length > 0) { |
|||
List<InboxThreadModel> list = listViewModel.getList().getValue(); |
|||
list = list != null ? new LinkedList<>(list) : new LinkedList<>(); |
|||
// final int oldSize = list != null ? list.size() : 0; |
|||
final List<InboxThreadModel> newList = Arrays.asList(threads); |
|||
list.addAll(newList); |
|||
listViewModel.getList().postValue(list); |
|||
} |
|||
} |
|||
root.setRefreshing(false); |
|||
stopCurrentExecutor(); |
|||
} |
|||
}; |
|||
|
|||
@Override |
|||
public void onCreate(@Nullable final Bundle savedInstanceState) { |
|||
super.onCreate(savedInstanceState); |
|||
fragmentActivity = requireActivity(); |
|||
} |
|||
|
|||
@Override |
|||
public View onCreateView(@NonNull final LayoutInflater inflater, |
|||
final ViewGroup container, |
|||
final Bundle savedInstanceState) { |
|||
if (root != null) { |
|||
return root; |
|||
} |
|||
final FragmentDirectMessagesInboxBinding binding = FragmentDirectMessagesInboxBinding.inflate(inflater, container, false); |
|||
root = binding.getRoot(); |
|||
root.setOnRefreshListener(this); |
|||
inboxList = binding.inboxList; |
|||
inboxList.setHasFixedSize(true); |
|||
layoutManager = new LinearLayoutManager(requireContext()); |
|||
inboxList.setLayoutManager(layoutManager); |
|||
final DirectMessageInboxAdapter inboxAdapter = new DirectMessageInboxAdapter(inboxThreadModel -> { |
|||
final NavDirections action = DirectMessagesInboxFragmentDirections.actionDMInboxFragmentToDMThreadFragment(inboxThreadModel.getThreadId(), inboxThreadModel.getThreadTitle()); |
|||
NavHostFragment.findNavController(this).navigate(action); |
|||
}); |
|||
inboxList.setAdapter(inboxAdapter); |
|||
listViewModel = new ViewModelProvider(fragmentActivity).get(InboxThreadModelListViewModel.class); |
|||
listViewModel.getList().observe(fragmentActivity, inboxAdapter::submitList); |
|||
initData(); |
|||
return root; |
|||
} |
|||
|
|||
@Override |
|||
public void onRefresh() { |
|||
endCursor = null; |
|||
lazyLoader.resetState(); |
|||
listViewModel.getList().postValue(Collections.emptyList()); |
|||
stopCurrentExecutor(); |
|||
currentlyRunning = new InboxFetcher(null, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
|||
} |
|||
|
|||
private void initData() { |
|||
lazyLoader = new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> { |
|||
if (!Utils.isEmpty(endCursor)) |
|||
currentlyRunning = new InboxFetcher(endCursor, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
|||
endCursor = null; |
|||
}); |
|||
inboxList.addOnScrollListener(lazyLoader); |
|||
stopCurrentExecutor(); |
|||
currentlyRunning = new InboxFetcher(null, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); |
|||
} |
|||
|
|||
private void stopCurrentExecutor() { |
|||
if (currentlyRunning != null) { |
|||
try { |
|||
currentlyRunning.cancel(true); |
|||
} catch (final Exception e) { |
|||
if (BuildConfig.DEBUG) Log.e(TAG, "", e); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public static class InboxThreadModelListViewModel extends ViewModel { |
|||
private MutableLiveData<List<InboxThreadModel>> list; |
|||
|
|||
public MutableLiveData<List<InboxThreadModel>> getList() { |
|||
if (list == null) { |
|||
list = new MutableLiveData<>(); |
|||
} |
|||
return list; |
|||
} |
|||
} |
|||
} |
@ -1,6 +1,16 @@ |
|||
package awais.instagrabber.models.enums; |
|||
|
|||
public enum UserInboxDirection { |
|||
OLDER, |
|||
NEWER, |
|||
OLDER("older"), |
|||
NEWER("newer"); |
|||
|
|||
private final String value; |
|||
|
|||
UserInboxDirection(final String value) { |
|||
this.value = value; |
|||
} |
|||
|
|||
public String getValue() { |
|||
return value; |
|||
} |
|||
} |
@ -0,0 +1,54 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" |
|||
xmlns:app="http://schemas.android.com/apk/res-auto" |
|||
xmlns:tools="http://schemas.android.com/tools" |
|||
android:id="@+id/homeCoordinator" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="match_parent" |
|||
android:animateLayoutChanges="true" |
|||
android:fitsSystemWindows="true" |
|||
tools:context=".activities.DirectMessagesActivity"> |
|||
|
|||
<fragment |
|||
android:id="@+id/direct_messages_nav_host_fragment" |
|||
android:name="androidx.navigation.fragment.NavHostFragment" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="match_parent" |
|||
app:defaultNavHost="true" |
|||
app:layout_behavior="@string/appbar_scrolling_view_behavior" |
|||
app:navGraph="@navigation/direct_messages_nav_graph" |
|||
tools:ignore="FragmentTagUsage" /> |
|||
|
|||
<com.google.android.material.appbar.AppBarLayout |
|||
android:id="@+id/appBarLayout" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
app:liftOnScroll="true"> |
|||
|
|||
<com.google.android.material.appbar.CollapsingToolbarLayout |
|||
android:id="@+id/collapsingToolbarLayout" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:animateLayoutChanges="true" |
|||
app:contentScrim="?attr/colorPrimary" |
|||
app:layout_scrollFlags="scroll|enterAlways|exitUntilCollapsed|snap" |
|||
app:scrimAnimationDuration="10"> |
|||
|
|||
<androidx.appcompat.widget.Toolbar |
|||
android:id="@+id/toolbar" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="?attr/actionBarSize" |
|||
app:layout_collapseMode="pin" |
|||
app:layout_scrollFlags="scroll|exitUntilCollapsed"> |
|||
|
|||
<TextView |
|||
android:id="@+id/toolbar_title" |
|||
style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="match_parent" |
|||
android:gravity="center_vertical" |
|||
android:text="@string/app_name" /> |
|||
</androidx.appcompat.widget.Toolbar> |
|||
</com.google.android.material.appbar.CollapsingToolbarLayout> |
|||
</com.google.android.material.appbar.AppBarLayout> |
|||
</androidx.coordinatorlayout.widget.CoordinatorLayout> |
@ -0,0 +1,10 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android" |
|||
android:id="@+id/swipeRefreshLayout" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="match_parent"> |
|||
<androidx.recyclerview.widget.RecyclerView |
|||
android:id="@+id/inbox_list" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="match_parent" /> |
|||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout> |
@ -0,0 +1,26 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<navigation xmlns:android="http://schemas.android.com/apk/res/android" |
|||
xmlns:app="http://schemas.android.com/apk/res-auto" |
|||
android:id="@+id/direct_messages_nav_graph" |
|||
app:startDestination="@id/directMessagesInboxFragment"> |
|||
|
|||
<fragment |
|||
android:id="@+id/directMessagesInboxFragment" |
|||
android:name="awais.instagrabber.fragments.directmessages.DirectMessageInboxFragment" |
|||
android:label="DirectMessagesInboxFragment" > |
|||
<action |
|||
android:id="@+id/action_dMInboxFragment_to_dMThreadFragment" |
|||
app:destination="@id/directMessagesThreadFragment" /> |
|||
</fragment> |
|||
<fragment |
|||
android:id="@+id/directMessagesThreadFragment" |
|||
android:name="awais.instagrabber.fragments.directmessages.DirectMessageThreadFragment" |
|||
android:label="DirectMessagesThreadFragment"> |
|||
<argument |
|||
android:name="threadId" |
|||
app:argType="string" /> |
|||
<argument |
|||
android:name="title" |
|||
app:argType="string" /> |
|||
</fragment> |
|||
</navigation> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue