From 072cd637762e6826f68d52b63bae947a85808c16 Mon Sep 17 00:00:00 2001 From: Austin Huang Date: Tue, 18 Aug 2020 16:47:43 -0400 Subject: [PATCH] like dm messages & some polishing --- .../adapters/MessageItemsAdapter.java | 2 +- .../DirectMessageViewHolder.java | 33 +++-- .../DirectThreadBroadcaster.java | 23 +++- .../DirectMessageThreadFragment.java | 120 +++++++++++++++--- .../direct_messages/DirectItemModel.java | 23 +++- .../java/awais/instagrabber/utils/Utils.java | 10 ++ .../fragment_direct_messages_thread.xml | 22 ++-- app/src/main/res/layout/item_message_item.xml | 8 ++ app/src/main/res/values/strings.xml | 8 ++ 9 files changed, 201 insertions(+), 48 deletions(-) diff --git a/app/src/main/java/awais/instagrabber/adapters/MessageItemsAdapter.java b/app/src/main/java/awais/instagrabber/adapters/MessageItemsAdapter.java index 3fd3fe82..bd6d8e7d 100755 --- a/app/src/main/java/awais/instagrabber/adapters/MessageItemsAdapter.java +++ b/app/src/main/java/awais/instagrabber/adapters/MessageItemsAdapter.java @@ -50,7 +50,7 @@ public final class MessageItemsAdapter extends ListAdapter { final Object tag = v.getTag(); @@ -73,24 +77,18 @@ public final class DirectMessageViewHolder extends RecyclerView.ViewHolder { } }; - 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) { + final List leftUsers, + final View.OnClickListener onClickListener, + final MentionClickListener mentionClickListener) { super(binding.getRoot()); this.binding = binding; this.users = users; this.leftUsers = leftUsers; this.itemMargin = Utils.displayMetrics.widthPixels / 5; + this.onClickListener = onClickListener; + this.mentionClickListener = mentionClickListener; strDmYou = binding.getRoot().getContext().getString(R.string.direct_messages_you); } @@ -99,7 +97,7 @@ public final class DirectMessageViewHolder extends RecyclerView.ViewHolder { return; } final Context context = itemView.getContext(); - itemView.setTag(directItemModel); + //itemView.setTag(directItemModel); final DirectItemType itemType = directItemModel.getItemType(); final ProfileModel user = getUser(directItemModel.getUserId()); final int type = user == myProfileHolder ? MESSAGE_OUTGOING : MESSAGE_INCOMING; @@ -137,6 +135,15 @@ public final class DirectMessageViewHolder extends RecyclerView.ViewHolder { binding.tvUsername.setText(text); binding.ivProfilePic.setVisibility(type == MESSAGE_INCOMING ? View.VISIBLE : View.GONE); + binding.ivProfilePic.setTag(user); + binding.ivProfilePic.setOnClickListener(onClickListener); + + binding.tvMessage.setMentionClickListener(mentionClickListener); + binding.messageCard.setTag(directItemModel); + LinearLayout parent = (LinearLayout) binding.messageCard.getParent(); + parent.setTag(directItemModel); + binding.messageCard.setOnClickListener(onClickListener); + binding.liked.setVisibility(directItemModel.isLiked() ? View.VISIBLE : View.GONE); final RequestManager glideRequestManager = Glide.with(itemView); @@ -338,7 +345,7 @@ public final class DirectMessageViewHolder extends RecyclerView.ViewHolder { Glide.with(binding.profileInfo).load(profileModel.getSdProfilePic()) .into(binding.profileInfo); btnOpenProfile.setTag(profileModel); - btnOpenProfile.setOnClickListener(openProfileClickListener); + btnOpenProfile.setOnClickListener(onClickListener); binding.tvFullName.setText(profileModel.getName()); binding.profileInfoText.setText(profileModel.getUsername()); diff --git a/app/src/main/java/awais/instagrabber/asyncs/direct_messages/DirectThreadBroadcaster.java b/app/src/main/java/awais/instagrabber/asyncs/direct_messages/DirectThreadBroadcaster.java index 98542cbd..f024073f 100644 --- a/app/src/main/java/awais/instagrabber/asyncs/direct_messages/DirectThreadBroadcaster.java +++ b/app/src/main/java/awais/instagrabber/asyncs/direct_messages/DirectThreadBroadcaster.java @@ -49,7 +49,7 @@ public class DirectThreadBroadcaster extends AsyncTask getFormMap() { + final Map form = new HashMap<>(); + form.put("item_id", itemId); + form.put("reaction_status", delete ? "deleted" : "created"); + form.put("reaction_type", "like"); + return form; + } + } + public static class ImageBroadcastOptions extends BroadcastOptions { final boolean allowFullAspectRatio; final String uploadId; diff --git a/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java b/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java index afd0b475..89cc6f3b 100644 --- a/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java +++ b/app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java @@ -1,6 +1,7 @@ package awais.instagrabber.fragments.directmessages; import android.app.Activity; +import android.content.DialogInterface; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; @@ -12,11 +13,13 @@ import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.ArrayAdapter; import android.widget.LinearLayout; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; import androidx.lifecycle.MutableLiveData; @@ -71,8 +74,11 @@ public class DirectMessageThreadFragment extends Fragment { private FragmentActivity fragmentActivity; private String threadId; private String cursor; + private final String cookie = Utils.settingsHelper.getString(Constants.COOKIE); + private final String myId = Utils.getUserIdFromCookie(cookie); private FragmentDirectMessagesThreadBinding binding; private DirectItemModelListViewModel listViewModel; + private DirectItemModel directItemModel; private RecyclerView messageList; private boolean hasSentSomething; private boolean hasOlder = true; @@ -80,6 +86,7 @@ public class DirectMessageThreadFragment extends Fragment { private final ProfileModel myProfileHolder = ProfileModel.getDefaultProfileModel(); private final List users = new ArrayList<>(); private final List leftUsers = new ArrayList<>(); + private ArrayAdapter dialogAdapter; private final View.OnClickListener clickListener = v -> { if (v == binding.commentSend) { @@ -88,7 +95,7 @@ public class DirectMessageThreadFragment extends Fragment { Toast.makeText(requireContext(), R.string.comment_send_empty_comment, Toast.LENGTH_SHORT).show(); return; } - sendText(text); + sendText(text, null, false); return; } if (v == binding.image) { @@ -180,10 +187,8 @@ public class DirectMessageThreadFragment extends Fragment { new DirectMessageInboxThreadFetcher(threadId, UserInboxDirection.OLDER, cursor, fetchListener).execute(); // serial because we don't want messages to be randomly ordered })); - final View.OnClickListener onClickListener = v -> { - Object tag = v.getTag(); - if (tag instanceof DirectItemModel) { - final DirectItemModel directItemModel = (DirectItemModel) tag; + final DialogInterface.OnClickListener onDialogListener = (d,w) -> { + if (w == 0) { final DirectItemType itemType = directItemModel.getItemType(); switch (itemType) { case MEDIA_SHARE: @@ -197,14 +202,14 @@ public class DirectMessageThreadFragment extends Fragment { break; case TEXT: case REEL_SHARE: - Utils.copyText(v.getContext(), directItemModel.getText()); - Toast.makeText(v.getContext(), R.string.clipboard_copied, Toast.LENGTH_SHORT).show(); + Utils.copyText(requireContext(), directItemModel.getText()); + Toast.makeText(requireContext(), R.string.clipboard_copied, Toast.LENGTH_SHORT).show(); break; case RAVEN_MEDIA: case MEDIA: final ProfileModel user = getUser(directItemModel.getUserId()); 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(); + Toast.makeText(requireContext(), R.string.downloader_downloading_media, Toast.LENGTH_SHORT).show(); break; case STORY_SHARE: if (directItemModel.getReelShare() != null) { @@ -235,6 +240,69 @@ public class DirectMessageThreadFragment extends Fragment { Log.d("austin_debug", "unsupported type " + itemType); } } + else if (w == 1) { + sendText(null, directItemModel.getItemId(), directItemModel.isLiked()); + } + else if (w == 2) { + if (String.valueOf(directItemModel.getUserId()).equals(myId)) { + // unsend: https://www.instagram.com/direct_v2/web/threads/340282366841710300949128288687654467119/items/29473546990090204551245070881259520/delete/ + } + else searchUsername(getUser(directItemModel.getUserId()).getUsername()); + } + }; + final View.OnClickListener onClickListener = v -> { + Object tag = v.getTag(); + if (tag instanceof ProfileModel) { + searchUsername(((ProfileModel) tag).getUsername()); + } + else if (tag instanceof DirectItemModel) { + directItemModel = (DirectItemModel) tag; + final DirectItemType itemType = directItemModel.getItemType(); + int firstOption = R.string.dms_inbox_raven_message_unknown; + String[] dialogList; + + switch (itemType) { + case MEDIA_SHARE: + firstOption = R.string.view_post; + break; + case LINK: + firstOption = R.string.dms_inbox_open_link; + break; + case TEXT: + case REEL_SHARE: + firstOption = R.string.dms_inbox_copy_text; + break; + case RAVEN_MEDIA: + case MEDIA: + firstOption = R.string.dms_inbox_download; + break; + case STORY_SHARE: + if (directItemModel.getReelShare() != null) { + firstOption = R.string.show_stories; + } else if (directItemModel.getText() != null && directItemModel.getText().toString().contains("@")) { + firstOption = R.string.open_profile; + } + break; + case PLACEHOLDER: + if (directItemModel.getText().toString().contains("@")) + firstOption = R.string.open_profile; + break; + } + + dialogList = new String[]{ + getString(firstOption), + getString(directItemModel.isLiked() ? R.string.dms_inbox_unlike : R.string.dms_inbox_like), + getString(String.valueOf(directItemModel.getUserId()).equals(myId) ? R.string.dms_inbox_unsend : R.string.dms_inbox_author) + }; + + dialogAdapter = new ArrayAdapter<>(requireContext(), android.R.layout.simple_list_item_1, dialogList); + + new AlertDialog.Builder(requireContext()) + //.setTitle(title) + .setAdapter(dialogAdapter, onDialogListener) + .setNeutralButton(R.string.cancel, null) + .show(); + } }; final MentionClickListener mentionClickListener = (view, text, isHashtag) -> searchUsername(text); final MessageItemsAdapter adapter = new MessageItemsAdapter(users, leftUsers, onClickListener, mentionClickListener); @@ -264,29 +332,42 @@ public class DirectMessageThreadFragment extends Fragment { listViewModel.getList().postValue(Collections.emptyList()); } - private void sendText(final String text) { - final DirectThreadBroadcaster.TextBroadcastOptions options; - try { - options = new DirectThreadBroadcaster.TextBroadcastOptions(text); - } catch (UnsupportedEncodingException e) { - Log.e(TAG, "Error", e); - return; + private void sendText(final String text, final String itemId, final boolean delete) { + DirectThreadBroadcaster.TextBroadcastOptions textOptions = null; + DirectThreadBroadcaster.ReactionBroadcastOptions reactionOptions = null; + if (text != null) { + try { + textOptions = new DirectThreadBroadcaster.TextBroadcastOptions(text); + } catch (UnsupportedEncodingException e) { + Log.e(TAG, "Error", e); + return; + } + } + else { + reactionOptions = new DirectThreadBroadcaster.ReactionBroadcastOptions(itemId, delete); } - broadcast(options, result -> { + broadcast(text != null ? textOptions : reactionOptions, result -> { if (result == null || result.getResponseCode() != HttpURLConnection.HTTP_OK) { Toast.makeText(requireContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); return; } - binding.commentText.setText(""); - // binding.commentText.clearFocus(); + if (text != null) { + binding.commentText.setText(""); + } + else { + LinearLayout dim = (LinearLayout) binding.messageList.findViewWithTag(directItemModel); + if (dim.findViewById(R.id.liked) != null) dim.findViewById(R.id.liked).setVisibility(deleted ? View.GONE : View.VISIBLE); + directItemModel.setLiked(); + } hasSentSomething = true; new DirectMessageInboxThreadFetcher(threadId, UserInboxDirection.OLDER, null, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); }); } private void sendImage(final Uri imageUri) { - try(InputStream inputStream = requireContext().getContentResolver().openInputStream(imageUri)) { + try (InputStream inputStream = requireContext().getContentResolver().openInputStream(imageUri)) { final Bitmap bitmap = BitmapFactory.decodeStream(inputStream); + Toast.makeText(requireContext(), R.string.uploading, Toast.LENGTH_SHORT).show(); // Upload Image final ImageUploader imageUploader = new ImageUploader(); imageUploader.setOnTaskCompleteListener(response -> { @@ -312,6 +393,7 @@ public class DirectMessageThreadFragment extends Fragment { imageUploader.execute(options); } catch (IOException e) { + Toast.makeText(requireContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); Log.e(TAG, "Error opening file", e); } } diff --git a/app/src/main/java/awais/instagrabber/models/direct_messages/DirectItemModel.java b/app/src/main/java/awais/instagrabber/models/direct_messages/DirectItemModel.java index 0ff18e20..98a01ab2 100755 --- a/app/src/main/java/awais/instagrabber/models/direct_messages/DirectItemModel.java +++ b/app/src/main/java/awais/instagrabber/models/direct_messages/DirectItemModel.java @@ -3,6 +3,7 @@ package awais.instagrabber.models.direct_messages; import androidx.annotation.NonNull; import java.io.Serializable; +import java.util.Arrays; import java.util.Date; import awais.instagrabber.models.ProfileModel; @@ -12,10 +13,14 @@ import awais.instagrabber.models.enums.RavenExpiringMediaType; import awais.instagrabber.models.enums.RavenMediaViewType; import awais.instagrabber.utils.Utils; +import static awais.instagrabber.utils.Constants.COOKIE; + public final class DirectItemModel implements Serializable, Comparable { private final long userId, timestamp; private final DirectItemType itemType; private final String itemId; + private String[] likes; + private boolean liked; private final CharSequence text; private final DirectItemLinkModel linkModel; private final DirectItemMediaModel mediaModel; @@ -27,9 +32,11 @@ public final class DirectItemModel implements Serializable, Comparable + + - - + \ 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 276a9348..287e6217 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -150,6 +150,13 @@ Reacted on a story Mentioned in a story Unsupported message type + Open link + Copy text + Download attachment + Like message + Unlike message + Unsend message + View author profile Post shared from %s Unknown media type Media expired! @@ -214,4 +221,5 @@ Activity InstaGrabber\nCopyright (C) 2019 AWAiS\nCopyright (C) 2020 Austin Huang, Ammar Githam\n\nThis program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. See https://www.gnu.org/licenses/.\n\nTHIS SOFTWARE IS PROVIDED BY THE AUTHOR \'\'AS IS\'\' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nSee project page for third-party attributions. Select Picture + Uploading...