diff --git a/app/build.gradle b/app/build.gradle index 99fe8211..c64e49ff 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,4 +1,5 @@ apply plugin: 'com.android.application' +apply plugin: "androidx.navigation.safeargs" android { compileSdkVersion 29 @@ -37,10 +38,14 @@ android { dependencies { implementation('androidx.appcompat:appcompat:1.3.0-alpha01@aar') { transitive true } - implementation('androidx.recyclerview:recyclerview:1.2.0-alpha03@aar') { transitive true } + implementation "androidx.recyclerview:recyclerview:1.1.0" implementation('com.google.android.material:material:1.3.0-alpha01@aar') { transitive true } implementation('androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-rc01') { transitive true } + def nav_version = "2.3.0" + implementation "androidx.navigation:navigation-fragment:$nav_version" + implementation "androidx.navigation:navigation-ui:$nav_version" + annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0' implementation('org.jsoup:jsoup:1.13.1') { transitive true } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 43696544..1b29f327 100755 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -215,7 +215,7 @@ - - - - - { - searchView.setQuery((cookieModel != null && userQuery != null && userQuery.equals("@"+cookieModel.getUsername())) ? "" : userQuery, false); + searchView.setQuery((cookieModel != null && userQuery != null && userQuery.equals("@" + cookieModel.getUsername())) ? "" : userQuery, false); menu.findItem(R.id.action_about).setVisible(false); menu.findItem(R.id.action_settings).setVisible(false); menu.findItem(R.id.action_dms).setVisible(false); @@ -400,7 +402,10 @@ public final class Main extends BaseLanguageActivity { private void cancelSuggestionsAsync() { if (prevSuggestionAsync != null) - try { prevSuggestionAsync.cancel(true); } catch (final Exception ignored) { } + try { + prevSuggestionAsync.cancel(true); + } catch (final Exception ignored) { + } } @Override @@ -411,7 +416,7 @@ public final class Main extends BaseLanguageActivity { closeAnyOpenDrawer(); addToStack(); - userQuery = (query.contains("@") || query.contains("#")) ? query : ("@"+query); + userQuery = (query.contains("@") || query.contains("#")) ? query : ("@" + query); searchAction.collapseActionView(); searchView.setIconified(true); searchView.setIconified(true); @@ -468,8 +473,7 @@ public final class Main extends BaseLanguageActivity { mainHelper.onRefresh(); return; } - } - else { + } else { finish(); } } diff --git a/app/src/main/java/awais/instagrabber/activities/directmessages/DirectMessages.java b/app/src/main/java/awais/instagrabber/activities/directmessages/DirectMessages.java deleted file mode 100644 index 8a6408e9..00000000 --- a/app/src/main/java/awais/instagrabber/activities/directmessages/DirectMessages.java +++ /dev/null @@ -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 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 fetchListener = new FetchListener() { - @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 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); - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/adapters/DirectMessageInboxAdapter.java b/app/src/main/java/awais/instagrabber/adapters/DirectMessageInboxAdapter.java new file mode 100644 index 00000000..dfe6af2c --- /dev/null +++ b/app/src/main/java/awais/instagrabber/adapters/DirectMessageInboxAdapter.java @@ -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 { + private final OnItemClickListener onClickListener; + + private static final DiffUtil.ItemCallback diffCallback = new DiffUtil.ItemCallback() { + @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); + } +} \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/adapters/DirectMessagesAdapter.java b/app/src/main/java/awais/instagrabber/adapters/DirectMessagesAdapter.java deleted file mode 100755 index b38db004..00000000 --- a/app/src/main/java/awais/instagrabber/adapters/DirectMessagesAdapter.java +++ /dev/null @@ -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 { - private final ArrayList inboxThreadModels; - private final View.OnClickListener onClickListener; - private LayoutInflater layoutInflater; - - public DirectMessagesAdapter(final ArrayList 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 = "Unsupported message"; - } - - holder.tvMessage.setText(HtmlCompat.fromHtml(messageText.toString(), 63)); - - holder.tvDate.setText(lastItemModel.getDateTime()); - } - } - - @Override - public int getItemCount() { - return inboxThreadModels == null ? 0 : inboxThreadModels.size(); - } -} \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/adapters/MessageItemsAdapter.java b/app/src/main/java/awais/instagrabber/adapters/MessageItemsAdapter.java index dbde8085..3fd3fe82 100755 --- a/app/src/main/java/awais/instagrabber/adapters/MessageItemsAdapter.java +++ b/app/src/main/java/awais/instagrabber/adapters/MessageItemsAdapter.java @@ -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 { - 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 directItemModels; - private final ArrayList users, leftusers; +public final class MessageItemsAdapter extends ListAdapter { + private final List users; + private final List 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 static final DiffUtil.ItemCallback diffCallback = new DiffUtil.ItemCallback() { + @Override + public boolean areItemsTheSame(@NonNull final DirectItemModel oldItem, @NonNull final DirectItemModel newItem) { + return oldItem.getItemId().equals(newItem.getItemId()); + } + + @Override + public boolean areContentsTheSame(@NonNull final DirectItemModel oldItem, @NonNull final DirectItemModel newItem) { + return oldItem.getItemId().equals(newItem.getItemId()); } }; - 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); - } - - prevVoiceModel = voiceMediaModel; - prevPlayIcon = playIcon; - } - }; - private Context context; - private LayoutInflater layoutInflater; - private String strDmYou; - - public MessageItemsAdapter(final ArrayList directItemModels, final ArrayList users, - final ArrayList leftusers, final View.OnClickListener onClickListener, + public MessageItemsAdapter(final List users, + final List 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(""+context.getString(R.string.dms_inbox_media_shared_from, modelUser.getUsername())+"", 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(""+text+"", 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(); } } \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/DirectMessageInboxItemViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/DirectMessageInboxItemViewHolder.java new file mode 100644 index 00000000..4f2c8693 --- /dev/null +++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/DirectMessageInboxItemViewHolder.java @@ -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 = "Unsupported message"; + } + binding.tvComment.setText(HtmlCompat.fromHtml(messageText.toString(), HtmlCompat.FROM_HTML_MODE_COMPACT)); + binding.tvDate.setText(lastItemModel.getDateTime()); + } +} \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/DirectMessageViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/DirectMessageViewHolder.java deleted file mode 100755 index c7cdddb4..00000000 --- a/app/src/main/java/awais/instagrabber/adapters/viewholder/DirectMessageViewHolder.java +++ /dev/null @@ -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); - } -} \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectMessageViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectMessageViewHolder.java new file mode 100644 index 00000000..5a1fef4f --- /dev/null +++ b/app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/DirectMessageViewHolder.java @@ -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 users; + private final List 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 users, + final List 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("" + context.getString(R.string.dms_inbox_media_shared_from, modelUser.getUsername()) + "", 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("" + text + "", 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; + } +} \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/TextMessageViewHolder.java b/app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/TextMessageViewHolder.java deleted file mode 100755 index 3e1bed67..00000000 --- a/app/src/main/java/awais/instagrabber/adapters/viewholder/directmessages/TextMessageViewHolder.java +++ /dev/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); - } -} \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/asyncs/direct_messages/UserInboxFetcher.java b/app/src/main/java/awais/instagrabber/asyncs/direct_messages/DirectMessageInboxThreadFetcher.java old mode 100755 new mode 100644 similarity index 67% rename from app/src/main/java/awais/instagrabber/asyncs/direct_messages/UserInboxFetcher.java rename to app/src/main/java/awais/instagrabber/asyncs/direct_messages/DirectMessageInboxThreadFetcher.java index d38a9301..b45d44bd --- a/app/src/main/java/awais/instagrabber/asyncs/direct_messages/UserInboxFetcher.java +++ b/app/src/main/java/awais/instagrabber/asyncs/direct_messages/DirectMessageInboxThreadFetcher.java @@ -9,6 +9,8 @@ import org.json.JSONObject; import java.net.HttpURLConnection; import java.net.URL; +import java.util.HashMap; +import java.util.Map; import awais.instagrabber.BuildConfig; import awais.instagrabber.interfaces.FetchListener; @@ -21,17 +23,21 @@ import awais.instagrabber.utils.Utils; import static awais.instagrabber.utils.Utils.logCollector; import static awaisomereport.LogCollector.LogFile; -public final class UserInboxFetcher extends AsyncTask { +public final class DirectMessageInboxThreadFetcher extends AsyncTask { + private static final String TAG = "DMInboxThreadFetcher"; + private final String id; private final String endCursor; private final FetchListener fetchListener; - private final String direction; + private final UserInboxDirection direction; - public UserInboxFetcher(final String id, final UserInboxDirection direction, final String endCursor, - final FetchListener fetchListener) { + public DirectMessageInboxThreadFetcher(final String id, + final UserInboxDirection direction, + final String cursor, + final FetchListener fetchListener) { this.id = id; - this.direction = "&direction=" + (direction == UserInboxDirection.NEWER ? "newer" : "older"); - this.endCursor = !Utils.isEmpty(endCursor) ? "&cursor=" + endCursor : ""; + this.direction = direction; + this.endCursor = cursor; this.fetchListener = fetchListener; } @@ -39,11 +45,14 @@ public final class UserInboxFetcher extends AsyncTask queryParamsMap = new HashMap<>(); + queryParamsMap.put("visual_message_return_type", "unseen"); + queryParamsMap.put("direction", direction.getValue()); + if (!Utils.isEmpty(endCursor)) { + queryParamsMap.put("cursor", endCursor); + } + final String queryString = Utils.getQueryString(queryParamsMap); + final String url = "https://i.instagram.com/api/v1/direct_v2/threads/" + id + "/?" + queryString; try { final HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection(); conn.setRequestProperty("User-Agent", Constants.I_USER_AGENT); @@ -60,9 +69,8 @@ public final class UserInboxFetcher extends AsyncTask currentlyRunning; + private InboxThreadModelListViewModel listViewModel; + + private final FetchListener fetchListener = new FetchListener() { + @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 list = listViewModel.getList().getValue(); + list = list != null ? new LinkedList<>(list) : new LinkedList<>(); + // final int oldSize = list != null ? list.size() : 0; + final List 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; + + public MutableLiveData> getList() { + if (list == null) { + list = new MutableLiveData<>(); + } + return list; + } + } +} diff --git a/app/src/main/java/awais/instagrabber/activities/directmessages/DirectMessageThread.java b/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java similarity index 51% rename from app/src/main/java/awais/instagrabber/activities/directmessages/DirectMessageThread.java rename to app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java index 7a374a2d..1e6b37f2 100644 --- a/app/src/main/java/awais/instagrabber/activities/directmessages/DirectMessageThread.java +++ b/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java @@ -1,17 +1,26 @@ -package awais.instagrabber.activities.directmessages; +package awais.instagrabber.fragments.directmessages; import android.app.Activity; import android.content.Intent; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; +import android.os.Handler; import android.os.ParcelFileDescriptor; -import android.text.TextUtils; import android.util.Log; +import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; import android.widget.Toast; +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.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -25,24 +34,21 @@ import java.net.HttpURLConnection; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.LinkedList; import java.util.List; import awais.instagrabber.R; -import awais.instagrabber.activities.BaseLanguageActivity; import awais.instagrabber.activities.PostViewer; import awais.instagrabber.activities.ProfileViewer; import awais.instagrabber.activities.StoryViewer; import awais.instagrabber.adapters.MessageItemsAdapter; import awais.instagrabber.asyncs.ImageUploader; +import awais.instagrabber.asyncs.direct_messages.DirectMessageInboxThreadFetcher; import awais.instagrabber.asyncs.direct_messages.DirectThreadBroadcaster; -import awais.instagrabber.asyncs.direct_messages.DirectThreadBroadcaster.BroadcastOptions; -import awais.instagrabber.asyncs.direct_messages.DirectThreadBroadcaster.ImageBroadcastOptions; -import awais.instagrabber.asyncs.direct_messages.DirectThreadBroadcaster.OnBroadcastCompleteListener; -import awais.instagrabber.asyncs.direct_messages.DirectThreadBroadcaster.TextBroadcastOptions; -import awais.instagrabber.asyncs.direct_messages.UserInboxFetcher; import awais.instagrabber.customviews.helpers.RecyclerLazyLoader; -import awais.instagrabber.databinding.ActivityDmsBinding; +import awais.instagrabber.databinding.FragmentDirectMessagesThreadBinding; import awais.instagrabber.interfaces.FetchListener; +import awais.instagrabber.interfaces.MentionClickListener; import awais.instagrabber.models.ImageUploadOptions; import awais.instagrabber.models.PostModel; import awais.instagrabber.models.ProfileModel; @@ -55,72 +61,34 @@ import awais.instagrabber.models.enums.UserInboxDirection; import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Utils; -import static android.view.View.VISIBLE; - -public final class DirectMessageThread extends BaseLanguageActivity { - private static final String TAG = "DirectMessageThread"; +public class DirectMessageThreadFragment extends Fragment { + private static final String TAG = "DirectMessagesThreadFmt"; private static final int PICK_IMAGE = 100; - private DirectItemModel directItemModel; + private FragmentActivity fragmentActivity; private String threadId; - private String endCursor; - private ActivityDmsBinding dmsBinding; - private MessageItemsAdapter messageItemsAdapter; + private String cursor; + private FragmentDirectMessagesThreadBinding binding; + private DirectItemModelListViewModel listViewModel; + private RecyclerView messageList; + private boolean hasSentSomething; + private boolean hasOlder = true; private final ProfileModel myProfileHolder = ProfileModel.getDefaultProfileModel(); - private final ArrayList users = new ArrayList<>(); - private final ArrayList leftUsers = new ArrayList<>(); - private final ArrayList directItemModels = new ArrayList<>(); - private final FetchListener fetchListener = new FetchListener() { - @Override - public void doBefore() { - dmsBinding.swipeRefreshLayout.setRefreshing(true); - } + private final List users = new ArrayList<>(); + private final List leftUsers = new ArrayList<>(); - @Override - public void onResult(final InboxThreadModel result) { - if (result == null && ("MINCURSOR".equals(endCursor) || "MAXCURSOR".equals(endCursor) || Utils.isEmpty(endCursor))) - Toast.makeText(DirectMessageThread.this, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); - - if (result != null) { - endCursor = result.getPrevCursor(); - if ("MINCURSOR".equals(endCursor) || "MAXCURSOR".equals(endCursor)) - endCursor = null; - - users.clear(); - users.addAll(Arrays.asList(result.getUsers())); - - leftUsers.clear(); - leftUsers.addAll(Arrays.asList(result.getLeftUsers())); - - threadId = result.getThreadId(); - dmsBinding.toolbar.toolbar.setTitle(result.getThreadTitle()); - String[] users = new String[result.getUsers().length]; - for (int i = 0; i < users.length; ++i) { - users[i] = result.getUsers()[i].getUsername(); - } - dmsBinding.toolbar.toolbar.setSubtitle(TextUtils.join(", ", users)); - - final int oldSize = directItemModels.size(); - final List itemModels = Arrays.asList(result.getItems()); - directItemModels.addAll(itemModels); - messageItemsAdapter.notifyItemRangeInserted(oldSize, itemModels.size()); - } - - dmsBinding.swipeRefreshLayout.setRefreshing(false); - } - }; private final View.OnClickListener clickListener = v -> { - if (v == dmsBinding.commentSend) { - final String text = dmsBinding.commentText.getText().toString(); + if (v == binding.commentSend) { + final String text = binding.commentText.getText().toString(); if (Utils.isEmpty(text)) { - Toast.makeText(getApplicationContext(), R.string.comment_send_empty_comment, Toast.LENGTH_SHORT).show(); + Toast.makeText(requireContext(), R.string.comment_send_empty_comment, Toast.LENGTH_SHORT).show(); return; } sendText(text); return; } - if (v == dmsBinding.image) { + if (v == binding.image) { final Intent intent = new Intent(); intent.setType("image/*"); intent.setAction(Intent.ACTION_GET_CONTENT); @@ -128,45 +96,95 @@ public final class DirectMessageThread extends BaseLanguageActivity { } }; - @Override - protected void onCreate(@Nullable final Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - dmsBinding = ActivityDmsBinding.inflate(getLayoutInflater()); - setContentView(dmsBinding.getRoot()); - - final InboxThreadModel threadModel; - final Intent intent = getIntent(); - if (intent == null || !intent.hasExtra(Constants.EXTRAS_THREAD_MODEL) || - (threadModel = (InboxThreadModel) intent.getSerializableExtra(Constants.EXTRAS_THREAD_MODEL)) == null) { - Utils.errorFinish(this); - return; + private final FetchListener fetchListener = new FetchListener() { + @Override + public void doBefore() { + binding.swipeRefreshLayout.setRefreshing(true); } - dmsBinding.swipeRefreshLayout.setEnabled(false); - dmsBinding.commentText.setVisibility(VISIBLE); - dmsBinding.commentSend.setVisibility(VISIBLE); - dmsBinding.image.setVisibility(VISIBLE); - dmsBinding.commentSend.setOnClickListener(clickListener); - dmsBinding.image.setOnClickListener(clickListener); + @Override + public void onResult(final InboxThreadModel result) { + if (result == null && ("MINCURSOR".equals(cursor) || "MAXCURSOR".equals(cursor) || Utils.isEmpty(cursor))) + Toast.makeText(requireContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); - final LinearLayoutManager layoutManager = new LinearLayoutManager(this, RecyclerView.VERTICAL, true); - dmsBinding.rvDirectMessages.setLayoutManager(layoutManager); + if (result != null) { + cursor = result.getOldestCursor(); + hasOlder = result.hasOlder(); + if ("MINCURSOR".equals(cursor) || "MAXCURSOR".equals(cursor)) { + cursor = null; + } + users.clear(); + users.addAll(Arrays.asList(result.getUsers())); + leftUsers.clear(); + leftUsers.addAll(Arrays.asList(result.getLeftUsers())); - dmsBinding.rvDirectMessages.addOnScrollListener(new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> { - if (!Utils.isEmpty(endCursor)) { - new UserInboxFetcher(threadModel.getThreadId(), UserInboxDirection.OLDER, - endCursor, fetchListener).execute(); // serial because we don't want messages to be randomly ordered + // thread title is already comma separated username, so no need to set by ourselves + // String[] users = new String[result.getUsers().length]; + // for (int i = 0; i < users.length; ++i) { + // users[i] = result.getUsers()[i].getUsername(); + // } + + List list = listViewModel.getList().getValue(); + final List newList = Arrays.asList(result.getItems()); + list = list != null ? new LinkedList<>(list) : new LinkedList<>(); + if (hasSentSomething) { + list = newList; + hasSentSomething = false; + final Handler handler = new Handler(); + handler.postDelayed(() -> { + if (messageList != null) { + messageList.smoothScrollToPosition(0); + } + }, 200); + } else { + list.addAll(newList); + } + listViewModel.getList().postValue(list); } + binding.swipeRefreshLayout.setRefreshing(false); + } + }; + + @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) { + binding = FragmentDirectMessagesThreadBinding.inflate(inflater, container, false); + final LinearLayout root = binding.getRoot(); + if (getArguments() == null) { + return root; + } + threadId = DirectMessagesThreadFragmentArgs.fromBundle(getArguments()).getThreadId(); + binding.swipeRefreshLayout.setEnabled(false); + messageList = binding.messageList; + messageList.setHasFixedSize(true); + binding.commentSend.setOnClickListener(clickListener); + binding.image.setOnClickListener(clickListener); + final LinearLayoutManager layoutManager = new LinearLayoutManager(requireContext()); + layoutManager.setReverseLayout(true); + // layoutManager.setStackFromEnd(true); + messageList.setLayoutManager(layoutManager); + messageList.addOnScrollListener(new RecyclerLazyLoader(layoutManager, (page, totalItemsCount) -> { + if (Utils.isEmpty(cursor) || !hasOlder) { + return; + } + new DirectMessageInboxThreadFetcher(threadId, UserInboxDirection.OLDER, cursor, fetchListener).execute(); // serial because we don't want messages to be randomly ordered })); - messageItemsAdapter = new MessageItemsAdapter(directItemModels, users, leftUsers, v -> { + final View.OnClickListener onClickListener = v -> { Object tag = v.getTag(); if (tag instanceof DirectItemModel) { - directItemModel = (DirectItemModel) tag; + final DirectItemModel directItemModel = (DirectItemModel) tag; final DirectItemType itemType = directItemModel.getItemType(); switch (itemType) { case MEDIA_SHARE: - startActivity(new Intent(this, PostViewer.class) + startActivity(new Intent(requireContext(), PostViewer.class) .putExtra(Constants.EXTRAS_POST, new PostModel(directItemModel.getMediaModel().getCode(), false))); break; case LINK: @@ -182,9 +200,7 @@ public final class DirectMessageThread extends BaseLanguageActivity { case RAVEN_MEDIA: case MEDIA: final ProfileModel user = getUser(directItemModel.getUserId()); - if (user != null) { - Utils.dmDownload(this, user.getUsername(), DownloadMethod.DOWNLOAD_DIRECT, Collections.singletonList(itemType == DirectItemType.MEDIA ? directItemModel.getMediaModel() : directItemModel.getRavenMediaModel().getMedia())); - } + Utils.dmDownload(requireContext(), user.getUsername(), DownloadMethod.DOWNLOAD_DIRECT, Collections.singletonList(itemType == DirectItemType.MEDIA ? directItemModel.getMediaModel() : directItemModel.getRavenMediaModel().getMedia())); Toast.makeText(v.getContext(), R.string.downloader_downloading_media, Toast.LENGTH_SHORT).show(); break; case STORY_SHARE: @@ -200,7 +216,7 @@ public final class DirectMessageThread extends BaseLanguageActivity { ); sm.setVideoUrl(directItemModel.getReelShare().getMedia().getVideoUrl()); StoryModel[] sms = {sm}; - startActivity(new Intent(this, StoryViewer.class) + startActivity(new Intent(requireContext(), StoryViewer.class) .putExtra(Constants.EXTRAS_USERNAME, directItemModel.getReelShare().getReelOwnerName()) .putExtra(Constants.EXTRAS_STORIES, sms) ); @@ -216,15 +232,18 @@ public final class DirectMessageThread extends BaseLanguageActivity { Log.d("austin_debug", "unsupported type " + itemType); } } - }, (view, text, isHashtag) -> searchUsername(text)); - - dmsBinding.rvDirectMessages.setAdapter(messageItemsAdapter); - - new UserInboxFetcher(threadModel.getThreadId(), UserInboxDirection.OLDER, null, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + }; + final MentionClickListener mentionClickListener = (view, text, isHashtag) -> searchUsername(text); + final MessageItemsAdapter adapter = new MessageItemsAdapter(users, leftUsers, onClickListener, mentionClickListener); + messageList.setAdapter(adapter); + listViewModel = new ViewModelProvider(fragmentActivity).get(DirectItemModelListViewModel.class); + listViewModel.getList().observe(fragmentActivity, adapter::submitList); + new DirectMessageInboxThreadFetcher(threadId, UserInboxDirection.OLDER, null, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + return root; } @Override - protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { + public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == PICK_IMAGE && resultCode == Activity.RESULT_OK) { if (data == null || data.getData() == null) { @@ -236,61 +255,46 @@ public final class DirectMessageThread extends BaseLanguageActivity { } } - @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; - } - - private void searchUsername(final String text) { - startActivity(new Intent(getApplicationContext(), ProfileViewer.class).putExtra(Constants.EXTRAS_USERNAME, text)); + @Override + public void onDestroy() { + super.onDestroy(); + listViewModel.getList().postValue(Collections.emptyList()); } private void sendText(final String text) { - final TextBroadcastOptions options; + final DirectThreadBroadcaster.TextBroadcastOptions options; try { - options = new TextBroadcastOptions(text); + options = new DirectThreadBroadcaster.TextBroadcastOptions(text); } catch (UnsupportedEncodingException e) { Log.e(TAG, "Error", e); return; } broadcast(options, result -> { if (result == null || result.getResponseCode() != HttpURLConnection.HTTP_OK) { - Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); + Toast.makeText(requireContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); return; } - dmsBinding.commentText.setText(""); - dmsBinding.commentText.clearFocus(); - directItemModels.clear(); - messageItemsAdapter.notifyDataSetChanged(); - new UserInboxFetcher(threadId, UserInboxDirection.OLDER, null, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + binding.commentText.setText(""); + // binding.commentText.clearFocus(); + hasSentSomething = true; + new DirectMessageInboxThreadFetcher(threadId, UserInboxDirection.OLDER, null, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); }); } private void sendImage(final Uri imageUri) { try { - final ParcelFileDescriptor fileDescriptor = getContentResolver().openFileDescriptor(imageUri, "r"); + final ParcelFileDescriptor fileDescriptor = requireContext().getContentResolver().openFileDescriptor(imageUri, "r"); if (fileDescriptor == null) { Log.e(TAG, "fileDescriptor is null!"); return; } final long contentLength = fileDescriptor.getStatSize(); - final InputStream inputStream = getContentResolver().openInputStream(imageUri); + final InputStream inputStream = requireContext().getContentResolver().openInputStream(imageUri); // Upload Image final ImageUploader imageUploader = new ImageUploader(); imageUploader.setOnTaskCompleteListener(response -> { if (response == null || response.getResponseCode() != HttpURLConnection.HTTP_OK) { - Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); + Toast.makeText(requireContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); if (response != null && response.getResponse() != null) { Log.e(TAG, response.getResponse().toString()); } @@ -300,12 +304,9 @@ public final class DirectMessageThread extends BaseLanguageActivity { try { final String uploadId = responseJson.getString("upload_id"); // Broadcast - final ImageBroadcastOptions options = new ImageBroadcastOptions(true, uploadId); - broadcast(options, onBroadcastCompleteListener -> { - directItemModels.clear(); - messageItemsAdapter.notifyDataSetChanged(); - new UserInboxFetcher(threadId, UserInboxDirection.OLDER, null, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - }); + final DirectThreadBroadcaster.ImageBroadcastOptions options = new DirectThreadBroadcaster.ImageBroadcastOptions(true, uploadId); + hasSentSomething = true; + broadcast(options, onBroadcastCompleteListener -> new DirectMessageInboxThreadFetcher(threadId, UserInboxDirection.OLDER, null, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)); } catch (JSONException e) { Log.e(TAG, "Error parsing json response", e); } @@ -317,9 +318,36 @@ public final class DirectMessageThread extends BaseLanguageActivity { } } - private void broadcast(final BroadcastOptions broadcastOptions, final OnBroadcastCompleteListener listener) { + private void broadcast(final DirectThreadBroadcaster.BroadcastOptions broadcastOptions, final DirectThreadBroadcaster.OnBroadcastCompleteListener listener) { final DirectThreadBroadcaster broadcaster = new DirectThreadBroadcaster(threadId); broadcaster.setOnTaskCompleteListener(listener); broadcaster.execute(broadcastOptions); } -} \ No newline at end of file + + @NonNull + private ProfileModel getUser(final long userId) { + ProfileModel result = myProfileHolder; + for (final ProfileModel user : users) { + if (Long.toString(userId).equals(user.getId())) result = user; + } + for (final ProfileModel leftUser : leftUsers) { + if (Long.toString(userId).equals(leftUser.getId())) result = leftUser; + } + return result; + } + + private void searchUsername(final String text) { + startActivity(new Intent(requireContext(), ProfileViewer.class).putExtra(Constants.EXTRAS_USERNAME, text)); + } + + public static class DirectItemModelListViewModel extends ViewModel { + private MutableLiveData> list; + + public MutableLiveData> getList() { + if (list == null) { + list = new MutableLiveData<>(); + } + return list; + } + } +} diff --git a/app/src/main/java/awais/instagrabber/models/ProfileModel.java b/app/src/main/java/awais/instagrabber/models/ProfileModel.java index 027cbf03..ac75527f 100755 --- a/app/src/main/java/awais/instagrabber/models/ProfileModel.java +++ b/app/src/main/java/awais/instagrabber/models/ProfileModel.java @@ -35,6 +35,10 @@ public final class ProfileModel implements Serializable { return new ProfileModel(false, false, false, null, null, null, null, null, null, null, 0, 0, 0, false, false, false, false); } + public static ProfileModel getDefaultProfileModel(final String userId) { + return new ProfileModel(false, false, false, userId, null, null, null, null, null, null, 0, 0, 0, false, false, false, false); + } + public boolean isPrivate() { return isPrivate; } diff --git a/app/src/main/java/awais/instagrabber/models/direct_messages/InboxThreadModel.java b/app/src/main/java/awais/instagrabber/models/direct_messages/InboxThreadModel.java index 9678023a..2eec3549 100755 --- a/app/src/main/java/awais/instagrabber/models/direct_messages/InboxThreadModel.java +++ b/app/src/main/java/awais/instagrabber/models/direct_messages/InboxThreadModel.java @@ -1,5 +1,7 @@ package awais.instagrabber.models.direct_messages; +import androidx.core.util.ObjectsCompat; + import java.io.Serializable; import awais.instagrabber.models.ProfileModel; @@ -123,7 +125,7 @@ public final class InboxThreadModel implements Serializable { return canonical; } - public boolean isHasOlder() { + public boolean hasOlder() { return hasOlder; } @@ -142,4 +144,18 @@ public final class InboxThreadModel implements Serializable { public long getLastActivityAt() { return lastActivityAt; } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final InboxThreadModel that = (InboxThreadModel) o; + return ObjectsCompat.equals(threadId, that.threadId) && + ObjectsCompat.equals(threadV2Id, that.threadV2Id); + } + + @Override + public int hashCode() { + return ObjectsCompat.hash(threadId, threadV2Id); + } } \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/models/enums/UserInboxDirection.java b/app/src/main/java/awais/instagrabber/models/enums/UserInboxDirection.java index 620c50f0..dc6cf83a 100755 --- a/app/src/main/java/awais/instagrabber/models/enums/UserInboxDirection.java +++ b/app/src/main/java/awais/instagrabber/models/enums/UserInboxDirection.java @@ -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; + } } \ No newline at end of file diff --git a/app/src/main/java/awais/instagrabber/utils/Utils.java b/app/src/main/java/awais/instagrabber/utils/Utils.java index 033b3f26..9d25ecd3 100755 --- a/app/src/main/java/awais/instagrabber/utils/Utils.java +++ b/app/src/main/java/awais/instagrabber/utils/Utils.java @@ -42,6 +42,7 @@ import androidx.fragment.app.FragmentManager; import org.json.JSONArray; import org.json.JSONObject; +import org.jsoup.internal.StringUtil; import java.io.BufferedReader; import java.io.File; @@ -58,6 +59,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Set; import java.util.regex.Pattern; import javax.crypto.Mac; @@ -1371,4 +1373,24 @@ public final class Utils { connection.setRequestProperty(header.getKey(), header.getValue()); } } + + public static String getQueryString(final Map queryParamsMap) { + if (queryParamsMap == null || queryParamsMap.isEmpty()) { + return ""; + } + final Set> params = queryParamsMap.entrySet(); + final StringBuilder builder = new StringBuilder(); + for (final Map.Entry param : params) { + if (isEmpty(param.getKey())) { + continue; + } + if (builder.length() != 0) { + builder.append("&"); + } + builder.append(param.getKey()); + builder.append("="); + builder.append(param.getValue() != null ? param.getValue() : ""); + } + return builder.toString(); + } } \ No newline at end of file diff --git a/app/src/main/res/layout/activity_direct_messages.xml b/app/src/main/res/layout/activity_direct_messages.xml new file mode 100644 index 00000000..cda5e611 --- /dev/null +++ b/app/src/main/res/layout/activity_direct_messages.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_direct_messages_inbox.xml b/app/src/main/res/layout/fragment_direct_messages_inbox.xml new file mode 100644 index 00000000..724b313f --- /dev/null +++ b/app/src/main/res/layout/fragment_direct_messages_inbox.xml @@ -0,0 +1,10 @@ + + + + diff --git a/app/src/main/res/layout/activity_dms.xml b/app/src/main/res/layout/fragment_direct_messages_thread.xml old mode 100755 new mode 100644 similarity index 81% rename from app/src/main/res/layout/activity_dms.xml rename to app/src/main/res/layout/fragment_direct_messages_thread.xml index 2d7c0f83..5ff80e79 --- a/app/src/main/res/layout/activity_dms.xml +++ b/app/src/main/res/layout/fragment_direct_messages_thread.xml @@ -1,24 +1,18 @@ - - + android:orientation="vertical"> + android:layout_weight="1"> @@ -31,7 +25,7 @@ android:id="@+id/commentText" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_weight="2" + android:layout_weight="1" android:gravity="bottom" android:hint="@string/dm_hint" android:importantForAutofill="no" @@ -42,8 +36,7 @@ android:paddingLeft="8dp" android:paddingEnd="4dp" android:paddingRight="4dp" - android:scrollHorizontally="false" - android:visibility="visible" /> + android:scrollHorizontally="false" /> \ No newline at end of file diff --git a/app/src/main/res/layout/item_message_item.xml b/app/src/main/res/layout/item_message_item.xml index 46235156..ca78d237 100755 --- a/app/src/main/res/layout/item_message_item.xml +++ b/app/src/main/res/layout/item_message_item.xml @@ -4,8 +4,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="horizontal" - tools:viewBindingIgnore="true"> + android:orientation="horizontal"> + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a7682f9d..276a9348 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -181,7 +181,7 @@ Like comment Unlike comment Delete comment - No empty comments, dawg! + No empty comments! Do you want to search the username? Do you want to search the hashtag? Do you want to search the location? diff --git a/build.gradle b/build.gradle index 156a6372..1c9b89c0 100755 --- a/build.gradle +++ b/build.gradle @@ -6,6 +6,9 @@ buildscript { dependencies { classpath 'com.android.tools.build:gradle:4.0.1' + def nav_version = "2.3.0" + classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version" + } }